- DDoS (distributed denial-of-service)
- Application-layer DoS attacks
- Security defect example
- Additional links
DoS attack (Denial of Service attack) is a type of attack meant to shut down or slow down the processing of user requests. DoS attacks do not aim to steal or corrupt customer data. The primary focus of the attack is to create problems with the computing resources availability. Due to these problems processes are not executed in an acceptable time frame. Computing resources usually include:
- CPU time;
- RAM capacity;
- disk storage;
- network channel bandwidth;
- I/O channel bandwidth.
Attacks can be divided into two groups: DDoS (distributed denial-of-service) and application-layer DoS attacks.
DDoS (distributed denial-of-service)
DDoS attacks occurs when superfluous requests (input data) target the API system. DDoS attacks involve multiple devices attacking the system. That's why it's called distributed. Usually, for a system, these devices and their requests are not an obvious threat to the processes of its internal logic. The requests do not contain malicious data or data that exploits vulnerabilities. Troubles start when the server's network channel does not have enough capacity to process all requests. Or the computing resources of the host machine are being exhausted.
The attack perpetrators utilize pre-infected devices that make requests to the target on command. Attackers do not need a wide network channel, so the network activity of an infected device may be invisible to the owner. Sometimes it's impossible to distinguish real users from attackers. To defend against DDoS attacks, you can deploy additional resources for the system during the attack. This solution may be technically challenging and expensive. However, it may prevent the customers data existing in the system from being damaged.
Application-layer DoS attacks
Application-layer DoS attacks target the specific vulnerabilities and issues of software and hardware. The "application-layer attack" name refers to the 7th level of the OSI model, which is called the application layer. Application-layer attacks also aim to overwhelm the system. Attackers use a small number of devices but target the vulnerabilities or architectural issues of the system. To exploit these vulnerabilities and issues, attackers generate special data, the processing of which leads to disruption of processes.
The issues are often found in the specifications and standards of languages and formats, from where they're implicitly passed into parsers and serializers. But mistakes can also occur in the parser and serializer implementations.
The example of an issue in standards and specifications is an XEE attack, which requires the presence of an API that accepts XML files. There are cases when the XML parser used for the API implementation is configured without protection from the expanding of nested XML entities. In these cases, the parser may exhaust the resources of the host machine.
Some other code vulnerabilities can also be used for DoS attacks. For example, code injection with high computational complexity through an XSS vulnerability or SQL injection. Although these vulnerabilities can be used to perform more dangerous attacks for data theft and corruption.
If the system architecture is incorrect, an accidental DoS attack can occur even without malicious intent. The system architecture issues can cause a lot of resource consumption even when you work with ordinary data. Examples of these issues:
- the use of inefficient algorithms with computational complexity greater than O(n^2);
- errors in conditional constructs that lead to an infinite loop;
- memory leaks;
- the creation of inefficient SQL scripts that process more rows than actually required;
- creating inefficient regular expressions and not having text search time limits for all regular expressions.
Security defect example
Let's take a closer look at the case when processing regular expressions can exhaust system resources. This situation is called ReDoS (Regular Expression Denial of Service). Look at the following code in C#:
string userInput = GetInput(); Regex regex = new Regex(@"(a+)+b"); bool isMatch = regex.IsMatch(userInput); ....
The 'a+' expression declares to find one match or more of the 'a' character. The expression is taken in parentheses, and the '+' quantifier is applied to it. The '(a+)+' expression declares to find one or more match of the '(a+)' group. Finally, the full expression declares to find the 'b' character after finding the previous substrings. The minimum matching string is 'ab'.
This is a synthetic example. However, it contains a case of the catastrophic backtracking issue.
For example, let's take the 'aaaa' string. The string doesn't have a character that can match the regular expression. The substring can contain only one character to match '(a+)'. Thus, the regular expression engine will iterate through the entire string searching for 'b'. If the search fails, the engine backtracks one character with the hope that there will be matching characters among the new combinations.
- The substring 'aaaa' matches the '(a+)+' expression. But the 'b' doesn't match. So the engine backtracks one character again.
- Now 'aaa' matches '(a+)'. The last character matches '(a+)+'. 'b' doesn't match. The engine backtracks the character from 'aaa'.
- Now 'aa' and 'aa' match '(a+)+'. However there is still no match to the full '(a+)+b' pattern. The engine backtracks the character of the second 'aa' group.
- Now the groups look like 'aa', and 'a' and 'a'. The last character creates a match to '(a+)+'. This match fails. The second last character creates a match to '(a+)+'. This match fails too. The engine splits groups by one character, as specified in the '(a+)' grouping. And then the engine traverses the string until it finds a match.
This mechanism is called backtracking, and the engine traverses the whole string. The search time grows exponentially. Thus, the problem described above is called catastrophic backtracking.
So, a regular expression is vulnerable when it contains:
- Grouping with repetition;
- A repetition inside the repeated grouping.
You can prevent catastrophic backtracking by making the search time limited:
string userInput = GetInput(); Regex regex = new Regex(@"(a+)+b", RegexOptions.None, matchTimeout: TimeSpan.FromSeconds(1)); bool isMatch = regex.IsMatch(userInput);
The search time is specified by the 'matchTimeout' argument. If the search time is exceeded, the 'RegexMatchTimeoutException' exception is thrown.
Also, there is another approach to overcome the catastrophic backtracking. Rewrite the regular expression to make them more exact.