Database Denial of Service: Attacks
Today’s post will discuss database denial of service attacks so later we can consider how to stop them. From the security researcher’s perspective I cannot help but be impressed by the diversity of database DoS attacks. Many such attacks are pretty dumb – they seem to be written by a person who does not understand SQL, writing horrible queries that are the opposite of efficient. Some exploits are so simple – yet clever – that we are amazed the targeted vulnerability was not found in quality assurance tests. But dumb or not, these attacks are effective. For example you could start a couple different searches on a website, choose a very broad list of values, and hit ‘search’. The backend relational system starts to look at every record in every table, chewing up memory and waiting for slow disk reads. Let’s look more closely at a couple different classes of denial of service attacks: Abuse of Functions The abuse of database functions is, by my count of reported vulnerabilities related to DoS, the single most common type of database DoS attack. There have been hundreds, and it seems like no externally accessible feature is safe. This class of attack is a bit like competitive judo: as you shift your weight in one direction, your opponent pushes you in the same direction to make you lose your balance and fall over. A judo expert will use your weight against you, just like an attacker uses database features against you. For example, if you implement restrictions on failed logins attackers may try bad passwords until they lock all legitimate users out. If you implement services to automatically scale up to handle user requests attackers can scale the database up until it collapses under its own weight, or the bill becomes ruinous, or you hit a billing threshold and service is shut down. There is no single attack vector, but there is a whole range of ways to misuse database features. This class of attacks is essentially an attacker getting a database function to misbehave. Typically it occurs when a database command confuses the database, the query parser, or a sub-function enough to lock up or crashes. Relational databases are complex gestalts of many interdependent processes, so the loss of a single service can often cause the entire database to grind to a halt. One example is an attacker sending malformed Remote Procedure Calls, incomprehensible to the parser, which cause it to simply stop. Malformed XML and TDS calls have been used the same way, as well as SNMP queries. Pretty much every database communication protocol has, at one time or another, been fooled or subverted by requests that are formatted correctly but violate the expectations of the database developers in some way that causes a problem. SQL injection is the best known type of functional abuse: SQL strings are bound into a variable passed to the database, so the database processes a very different query than was expected. SQLi is not typically associated with DoS – it is more often employed as a first step in a database takeover because most attackers want to control the database but don’t want to be detected, but it SQL injection works and is used in both ways. Back to judo: every feature you have can be used against you. Complex Queries Complex queries are an attack class that works by giving the database too much work to do. Attackers find the most resource-intensive process accessible, and kick off a few dozen requests. Computed columns and views: Computed columns are virtual columns, typically created from the results of a query, and usually stored in memory. A view is a virtual table, the contents of which are also derived from a query. If the query selects a large amount of data the results occupy a large space in memory. And if the column or view is based on a complex query, it requires significant processing power to create. Exposure of computed columns and views has been the source of database DoS attacks in the past, with attackers continually refreshing views to slow down the database. Nested queries & recursion: Recursion is when a program calls itself; each time it recreates declared parameters or variables in memory. A common attack is to place a recursive call within a cursor FOR loop; after a few thousand iterations the database runs out of cursors or memory and comes to a halt. The IN operator: This operator tests whether a supplied variable matches any value within a set. The operation itself is very slow – even if the number of values to be compared is small. An attacker can inject the IN operator into a query to compare a large set of values against a variable that never matches. This is also called the snowflake search because it is like attempting to match two unique snowflakes, but the database continues to search regardless. Cartesian products and joins: The JOIN operation combines rows from two or more tables. A cartesian product is the sum of all rows from all tables specified in the FROM clause. Queries which calculate cartesian products on a few large tables generate huge result sets – possibly as large as the entire database. Any operations on a cartesian product can overwhelm a database. User defined functions: Similar to computed columns and views, any user-defined function gives an attacker carte blanche to abuse the database with whatever query they choose. Attackers leverage any of the above complex queries. Attackers attempt to exploit any complex query they can access. All these abuses are predicated on the attacker being able to inject SQL into some part of the database or abuse legitimate instances of complex operations. A couple complex queries running in parallel are enough to consume the majority of a database platform’s resources, badly slowing or completely stopping the database. Bugs and Defects Attackers exploit defects by targeting a specific weakness or bug in the database. To succeed the attacker needs to know or guess the type of database in use, and must know or learn of a weakness in code or design that can