Learning from hackers a week after the Drupal SQL Injection announcement

  • 10 minute read

Since October 15th, hackers have been busy coming up with creative ways to exploit the SQL Injection in Drupal 7 sites revealed by SA-CORE-2014-005. A week has already passed, and attacks are still ongoing. In a previous post, Moshe Weitzman explained how we were able to protect our customers' sites the moment the vulnerability was announced. In this blog post, we will look at some trends that we've seen in foiled attacks on Acquia Cloud in the past week, with a focus on this SQL Injection vulnerability.

This post draws its data from the aggregated logs of tens of thousands of sites hosted on Acquia Cloud which are managed and analyzed by our internal log analysis tool. All times in this post are EST. To our knowledge, no site on Acquia Cloud was affected, so we're only looking at exploit attempts here, but the observations below can easily be extrapolated to the rest of the Drupal sites from the wild. If you haven't patched yet when you are reading this, chances are your site has already been hacked.

Pattern 1: backdoor in docroot

Number of attacks: 7,785
First seen: 10/16 4:09am

The day following the official announcement, many people reported on the drupal.org forums and IRC that they found their site with a backdoor in the docroot via an exploit described by Josh Koenig. It creates a random file in one of your core module directories, for example modules/aggregator/dlov.php, which is a backdoor allowing the attacker to run arbitrary PHP code. Many people have also reported that their site had already been patched. The hackers used their backdoor to patch Drupal, presumably because they wanted to make sure they really owned the site and no other hacker could come in later.

INSERT INTO `menu_router` (`path`, `load_functions`, `to_arg_functions`, `description`, `access_callback`, `access_arguments`) VALUES (dlov, '', '', dlov, 'file_put_contents', HEX)

I've tracked down this attack to the IP address 62.76.191.119 from Russia. Others on IRC confirmed being hit by the same IP and found their sites hacked. This IP hit our platform 36,786 times in the 10-16 03:00 to 10-17 13:00 time window. Those hits include:

  • the initial POST request to insert data in the database using the above query, taking advantage of the SQL injection on the user login form

  • several GET requests to the paths /dlov and /?q=dlov in order to install the backdoor in the docroot

  • more GET requests to access the backdoor PHP file (modules/aggregator/dlov.php).

All those GET requests returned a 404 since the SQL Injection vector was mitigated on Acquia Cloud, and thus the initial POST request didn’t expose the SQL Injection vector. Even if the SQL injection was possible, this exploit wouldn’t have worked on Acquia Cloud because the docroot is not writable by the webserver.

This diagram shows the distribution of the 7,785 POST queries over a period of 34 hours, each hitting a different site:

Pattern 2: backdoor in /user path

Number of attacks: 732
First seen: 10/16 7:38pm

I wrote about this pattern as soon as I discovered it on Oct 17th. This attack enables the PHP module and updates the '/user' menu_router access_callback to 'php_eval' to let the attacker execute any PHP code via any kind of request.

TRUNCATE TABLE cache_bootstrap;
UPDATE menu_router SET access_arguments=HEX, access_callback=HEX WHERE path=0x75736572;
UPDATE system SET status = 1 WHERE name = 0x706870;
INSERT INTO registry_file (filename,hash) VALUES (HEX, HEX);

The advantage of this backdoor to the attacker is that it doesn’t require write access to the docroot to be set up, but it is neutralized on menu cache clear.

This attack wasn’t associated to a unique IP but most likely came from a botnet. Here are some of the IPs that I found: 92.222.172.41, 199.27.76.34, 192.42.116.16, 199.27.76.30, 199.27.76.25, 162.247.72.7, 209.234.102.238, 85.25.103.48, 95.130.9.89. All those IPs were associated with spam traffic on our platform. The last 4 IPs are Tor IPs:

Pattern 3: create a custom block with PHP code in body

Number of attacks: 24
First seen: 10/17/2014 09:17

This exploit creates a custom block, sets up the PHP code text format, and insert the following in the block body:

insert into block(module,delta,theme,status,weight,region,custom,visibility,pages,title,cache) values ('block','63353',(select substring_index(substring_index(value,'"','2'),'"',-1) from variable where name='theme_default'),1,0,'footer',0,0,'','',-1);insert into block_custom(bid,body,info,format) values (63353,HEX,'nonono','php_code');insert into filter_format values ('php_code','PHP_code',0,1,11);insert into filter values ('php_code', 'filter', 'filter_autop', 0, 0, HEX),('php_code', 'filter', 'filter_html', -10, 0, HEX),('php_code', 'filter', 'filter_htmlcorrector', 10, 0, HEX),('php_code', 'filter', 'filter_html_escape', -10, 0, HEX),('php_code', 'filter', 'filter_url', 0, 0, HEX),('php_code', 'php', 'php_code', 0, 1, HEX);update system set status=1,schema_version=0;delete from cache_bootstrap;delete from cache;delete from cache_menu;

Other patterns: reset admin username and password or insert new admin user

Number of attacks: 3,000+
First seen: 10/15 8:20pm

These patterns were the first ones to emerge.

This query renames the uid 1 and change its password:

update users set name='jhuang' , pass = 'HASH' where uid = '1';

This query creates a new user with a uid high enough that it won’t clash with an existing user. The role id 3 is the default role id for the administrator role:

insert into {users} (uid,name,pass,status) values (333333,'admin2','HASH',1);insert into {users_roles} (uid,rid) values(333333,3);

Top offending IPs: 118.113.158.7, 103.242.1.193, 118.113.158.7, 125.70.172.51, 222.211.211.65, 125.70.173.81, 103.242.1.193

Another query similar to the previous one:

INSERT INTO `users` VALUES (9999,HASH,'[email protected]','','',NULL,1413423527,1413426879,1413426953,1,'Africa/Abidjan','',0,'[email protected]','b:0;');insert into `users_roles` values (9999,3);

What’s interesting about this query is that hackers attempted to hide their IP behind a X-Forwarded-For: 8.8.8.8 header, one of the Google Public DNS IP addresses.

This query looks up the maximum uid to prepare the insert statement:

set @a=(SELECT MAX(uid) FROM users)+1;INSERT INTO users set uid=@a,status=1,name='phantom' , pass = 'HASH';INSERT INTO users_roles set uid=@a,rid=3;

Conclusion

This security issue has offered a fascinating opportunity to study the behavior of web hackers responding to a newly disclosed vulnerability. We suspected, and confirmed, that attacks would begin only hours after the vulnerability was disclosed. We were not sure how much Drupal-specific knowledge would be applied, but the attacks show that a fair bit was brought to bear. Also, given the breadth of attacks we've seen, if you have not patched your site and it is not hosted on a platform like Acquia Cloud that protected you, your site has almost certainly been hacked by now.

In all those attacks, we couldn't find any indication that the attackers were targeting only Drupal 7 sites: we observed attacks on Drupal 6 and Drupal 5 sites too. 99% of the domains attacked were vanity domains and dev environments were very rarely attacked. We could not find any query intended to change the content or destroy sites: attackers were only interested in installing backdoors to take over the site or server at a later point in time, and make the intrusion unnoticeable. Most of the queries in this article had some parts redacted. Those removed parts were most of the time hexadecimal literals which MySQL supports natively. Hackers seem to favor those as it allows to obfuscate any dangerous PHP code and bypass any potential filters.

For help determining if your site has been hacked and what to do about it, see the official FAQ and read Your Drupal site got hacked. Now what?. There is also a useful flowchart at Your Drupal website has a backdoor. Bear in mind that even if you updated to Drupal 7.32, you might still have been hacked in the time window between the official announcement (noon EDT / 4pm UTC) and the time you patched, see for example a post from someone who reported a hack on the zen theme on this site.

An open question is whether the SQL injection vulnerability was exploitable via some vector other than via the user login form. So far, we haven't seen any evidence of such a vector; none of our customers have reported a successful attack, and we have not heard any other reports of one from the Drupal community. This does not mean, however, that no such vulnerability exists, or even that one hasn't been exploited already. It's possible that there is such a vector, and everyone whose site was exploited using it believed they were attacked via the user login form before updating. The best way to be safe is to patch your site or upgrade to Drupal 7.32. We believe that the way we protected all our customers from this attack also protected against any other such vector, and time will tell whether or not it exists at all.