How to replace malicious PHP pages with sneaky POST data capture
Whenever I’ve had the task of personally assisting someone with remediating a compromised web server, I can’t help switching into researcher mode. I want to know how the attack has happened, and of course to stop it from happening again, but I’m always intrigued by what the attacker is trying to achieve – and it isn’t always what you’d think.
In my experience most attackers will inject a PHP file that is nothing more than a web shell that allows them to explore other directories on the server, and perhaps run a few commands here and there – sometimes leading to a full-scale breach of the server. Most PHP shells that I have pulled apart are heavily obfuscated with the PHP code going through a series of additional steps to effectively decode itself upon execution – and sometimes they require passwords to decrypt which are only supplied by the attacker via POST form data when used.
On other occasions I have found more complicated PHP files that act as very small and powerful remote execution gateways that evaluate code submitted on the fly via POST form data – sometimes also with a decryption key or similar. The last time I saw this was on a web server that was being used to send a spam email.
Unfortunately, keeping only standard web logs is not enough because they only keep the URL and URI information from a GET request, and not any of the data of the request. While you can implement modules in Apache to log POST data such as mod_dumpio I’ve sometimes found myself short on time, so I’ve opted for a much simpler approach.
What I do is locate the malicious PHP files on the infected website and replace them completely with the script below:
<?php // POST data capture - replaces malicious file previously found on this system header('HTTP/1.1 500 Internal Server Error'); $date = new DateTime(); $date->setTimezone(new DateTimeZone('Australia/Melbourne')); $fdate = $date->format('Y-m-d H:i:s'); // same format as NOW() file_put_contents("/tmp/post.log",$fdate . "\n" . print_r($_SERVER,true) . "\n\n" . print_r($_POST,true) . "\n==========\n"); ?>
This handy script will simply return a fake HTTP Status code of 500 indicating an Internal Server Error has occurred (even though really it hasn’t!). Many attackers are used to seeing this error, since some of the very badly written scripts they write often have syntax errors. So a 500 error is enough to keep them trying in the hope they think they’ve done something wrong with whatever data they’re trying to inject.
But, as well as returning the fake error, this script sneakily captures any POST data submitted to the page and appends it to the /tmp/post.log file (obviously you’ll want to change this to wherever you’d like). Once you’ve started to capture some real POST data you can replay that data yourself in an isolated environment with a copy of the real malicious PHP file to see what happens. Often the captured POST data will reveal the decryption password you need to unlock the code for further analysis.
Yes, this is a kludge. Yes, it’d be nice if every network was designed to capture all network traffic for later analysis (although the complexities that come into play with SSL make that troublesome for most), but in the environments I’m often called upon to assist in, this does the trick nicely.
Until next time. Stay safe out there.