Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Monday, November 9, 2009

Reversing JavaScript Shellcode: A Step By Step How-To

With more and more exploits being written in JavaScript, even some 0-day, there is a need to be able to reverse exploits written in JavaScript beyond de-obfuscation. I spent some time this weekend searching Google for a simple way to reverse JavaScript shellcode to assembly. I know people do it all the time. It's hardly rocket science. Yet, I didn't find any good walk-throughs on how to do this. So I thought I'd write one.

For this walk-through, I'll start with JavaScript that has already been extracted from a PDF file and de-obfuscated. So this isn't step 1 of fully reversing a PDF exploit, but for the first several steps, check out Part 2 of this slide deck.

What you'll need:
  1. A safe place to play with exploits (I'll be using an image in VMWare Workstation.)
  2. JavaScript debugger (I highly recommend and will be using Didier Stevens' modified SpiderMonkey.)
  3. Perl
  4. The crap2shellcode.pl script, which you'll find further down in this post
  5. A C compiler and your favorite binary debugger

I'll be using one of the example Adobe Acrobat exploits from the aforementioned slides for this example. You can grab it from milw0rm.

Step 1 - Converting from UTF-encoded characters to ASCII
Most JavaScript shellcode is encoded as either UTF-8 or UTF-16 characters. It would be easy enough to write a tool to convert from any one of these formats to the typical \x-ed UTF-8 format that we're used to seeing shellcode in. But because of the diversity of encoding and obfuscation showing up in JavaScript exploits today, it's more reliable to use JavaScript to decode the shellcode.

For this task, you need a JavaScript debugger. Didier Stevens' SpiderMonkey mod is a great choice. Start by preparing the shellcode text for passing to the debugger. In this case, drop the rest of the exploit, and then wrap the unescape function in an eval function:



Now run this code through SpiderMonkey. SpiderMonkey will create two log files for the eval command, the one with our ASCII shellcode is eval.001.log.



Step 2 - crap2shellcode.pl
This is why I wrote this script, to take an ASCII dump of some shellcode and automate making it debugger-friendly.


---cut---

#!/bin/perl
#
# crap2shellcode - 11/9/2009 Paul Melson
#
# This script takes stdin from some ascii dump of shellcode
# (i.e. unescape-ed JavaScript sploit) and converts it to
# hex and outputs it in a simple C source file for debugging.
#
# gcc -g3 -o dummy dummy.c
# gdb ./dummy
# (gdb) display /50i shellcode
# (gdb) break main
# (gdb) run
#

use strict;
use warnings;

my $crap;
while($crap=<stdin>) {
my $hex = unpack('H*', "$crap");

my $len = length($hex);
my $start = 0;

print "#include <stdio.h>\n\n";
print "static char shellcode[] = \"";

for (my $i = 0; $i < length $hex; $i+=4) {
my $a = substr $hex, $i, 2;
my $b = substr $hex, $i+2, 2;
print "\\x$b\\x$a";
}
print "\";\n\n";
}

print "int main(int argc, char *argv[])\n";
print "{\n";
print " void (*code)() = (void *)shellcode;\n";
print " code();\n";
print " exit(0);\n";
print "}\n";
print "\n";



--paste--

The output of passing eval.001.log through crap2shellcode.pl is a C program that makes debugging the shellcode easy.



Step 3 - View the shellcode/assembly in a debugger
First we have to build it. Since we know that this shellcode is a Linux bindshell the logical choice for where and how to build is Linux with gcc. Similarly, we can use gdb to dump the shellcode. For Win32 shellcode, we would probably pick Visual Studio Express and OllyDbg. Just about any Windows C compiler and debugger will work fine, though.

To build the C code we generated in step 2 with gcc, use the following:

gcc -g3 shellcode.c -o shellcode

The '-g3' flag builds the binary with labels for function stack tracing. This is necessary for debugging the binary. Or at least it makes it a whole lot easier.

Now open the binary in gdb, print *shellcode in x/50i format, set a breakpoint at main(), and run it.

$ gdb ./shellcode
(gdb) display /50i shellcode

(gdb) break main

(gdb) run



Thursday, December 27, 2007

Building Didier Stevens' SpiderMonkey in Cygwin

Here's one for your malware analysis toolkit. For some time now, I've been using Rhino, Mozilla's Java implementation of JavaScript, to help automate deobfuscation. SpiderMonkey is Mozilla's C implementation of JavaScript, including a shell much like Rhino's.

There are a couple of things that Mozilla's engine doesn't do when it comes to deobfuscating JavaScript. Specifically, you're left to manually convert eval and document.* calls yourself. That's where this really smart guy Didier Stevens comes in. He has a modified SpiderMonkey that solves both of these issues.

So you already know that I like Cygwin for lots of things, including malware analysis. Unfortunately, SpiderMonkey is really only intended to build on Win32 with Visual Studio. However, there are a couple of quick shortcuts you can take to get it to build with gcc in Cygwin. So here we go.

1. Install Cygwin with gcc and standard C libraries.
2. Download and untar Stevens' SpiderMonkey source tarball.
3. In js/src/config/Linux_All.mk find the line that begins with MKSHLIB and change the ld linker syntax by replacing '-shared' with '-r':

$ grep -n MKSHLIB config/Linux_All.mk
50:MKSHLIB = $(LD) -shared $(XMKSHLIBOPTS)

4. Build using make with the following syntax:

$ make -f Makefile.ref OS_ARCH='Linux'

We're essentially lying to make to get it to build as if our Cygwin environment is a Linux box. This is why shared linking breaks. But it should be a non-issue.

5. The make will exit with errors, but if all went well, the JavaScript shell, js.exe, has already been built:

$ cd Linux_All_DBG.OBJ
$ ls -l js.exe
-rwxr-xr-x 1 nobody None 1493267 Dec 27 17:40 js.exe
$ cd
$ cp js/src/Linux_All_DBG.OBJ/js.exe $HOME

$ ./js.exe
js> document.write("oh word!");
js> ^C
$ cat write.log
oh word!

And that's it. Make a copy of the binary for future use and clean up.

Thursday, July 12, 2007

My Feeds

Hi, my name is Paul, and I'm an addict.

So, I've been using Sage for Firefox for a couple of years now, and it's become a problem. It's a problem because if a web site that I find interesting has an RSS feed, I add it to my Sage feeds instead of bookmarking it. As part of the first step toward recovery, I know I must admit that I have a problem. And here is the scope of said problem:


And of course, if you have Sage, or if your RSS reader supports feed discovery, you can automagically add all of my feeds to your feeds. Share the love. Spread the disease.

Friday, April 20, 2007

My ArcSight Toolbox

I'm not shy about the fact that I use ArcSight at work, though when talking about SIM's and logging, I try not to make it all about them. But this post is all about ArcSight, but also maybe not. Maybe you use another SIM that has this same type of functionality - it wouldn't surprise me if this was standard on most SIM's shipping today.

Anyway, ArcSight has a "Tools" feature that basically allows you to pass the contents of any cell in a table view (ArcSight calls them Active Channels) to an external program. This is unbelievably handy. So here are some of my favorite ArcSight Tools.

1. Cygwin Whois - ArcSight comes with a built-in, java-based whois lookup tool. But for whatever reason, if the address is outside the US, say in an APNIC block, ArcSight just returns the NIC. Cygwin's whois will look up the registrant from the correct NIC.




2. EventID.Net Lookup - Takes a field containing EventLogType:ID ('Device Event Class ID' by default) and passes it to a shell script that launches IE with a properly f0rmatted eventid.net URL:

#!/bin/bash
PATH=$PATH:/cygdrive/c/cygwin/bin:/usr/bin:/bin
if [ "$1" = "" ];
then
echo "usage: $0 [ArcSight EventLog ID Tag]";
exit 0
fi
query=`echo $1 | sed 's/\(.*\):\(.*\)/eventid=\2\&source=\1/'`
if [ "$query" = "" ]; then echo "Error in field format";
exit 1

fi
/cygdrive/c/Program\ Files/Internet\ Explorer/IEXPLORE.EXE "http://www.eventid.net/display.asp?$query" &


3. LDAP Server/User Lookup - This is a Perl script that I wrote that takes a server or user name field and searches AD via LDAP for it and returns things like distinguishedName, operatingSystem, description, memberOf, and so on. This runs in Cygwin as well.


4. VHost Live Search - Got this idea from a post to the pen-test mailing list. Sometimes whois and nslookup don't cut it. This is a great way to figure out what vhosts might be present on a given IP address.



5. IP2Asset - On our network, workstation names and asset ID's are the same. So here's a script that takes an IP address, runs nslookup, and then launches Altiris web console to search for the asset.

#!/bin/bash PATH=$PATH:/cygdrive/c/cygwin/bin:/usr/bin:/bin
if [ "$1" = "" ]; then echo "usage: $0 [ip address]";
exit 0

fi
asset=`nslookup $1 |grep ^Name |sed 's/.*\(it[0-9]*\)\.wks.*/\1/'`
if [ "$asset" = "" ]; then echo "Error resolving address";
exit 1
fi
/cygdrive/c/Program\ Files/Internet\ Explorer/IEXPLORE.EXE "http://altiris_svr/Altiris/NS/Console.aspx?NameMatch='$asset'" &

Thursday, April 12, 2007

Malware, Packers, Debuggers, OEPs, and... Arrrgggh!

I haven't posted much this week because most of my free time has been spent tearing my hair out.

This past Tuesday I gave a lecture to my friend Tim's computer security class at a local college while he was on vacation. The topic was introductory malware analysis, and I decided I would include a live demo of iDefense SysAnalyzer in VMWare as the big finale. I was extra excited to do this when, last Friday, I found an ANI exploit in the wild and captured not only the exploit file but the alleged malware that the exploit drops on its victims.

The ANI exploit was easy enough to analyze:

$ strings file.jpg
RIFF
ACONanih$
tsil
tsil
anihR
11111111111111111111111111111111
444444444444444444444444444
000000000
CMD >
/C "
T} >
QSPPPPPPWP
hURlm
jlhntdl
huser
l$$6
6;|$(u
http://XXXXXXXXXXXXXX/download/167212/bin.exe

But bin.exe continues to be a pain in my side. So the students got to see my demo with some older malware that my Nepenthes honeypot collected last November. I refuse to admit defeat, now at least in the hopes of learning something.

In VMWare, it simply exits with errorlevel=0. My initial reaction was, "I found vm-aware malware! Sweet!" But now I'm not so sure. Applying some great advice from my friend Matt at IntelGuardians, I tried to disguise the presence of VMWare. Still nothing in SysAnalyzer.

So I decided to venture into new territory and attempt to unpack the bin.exe. I spent several hours yesterday and today trying to unpack the binary. PEID says it's packed with UPX, but UPX won't unpack it straight up. After much searching, I found an excellent flash demonstration by Frank Boldewin on unpacking obfuscated packed executables with OllyDbg, the OllyDump plugin, and ImpRec. But after several hours of trying variations of Frank's method, I still can't find a valid OEP (Original Entry Point - from which the binary can be dumped). I wish I had a point to all of this other than the one I have - malware analysis is hard and people like Frank and Matt that do this stuff for a living are jaw-droppingly smart.

This is me being envious of their giant brains.

Wednesday, March 7, 2007

February Catch Up

Here's some random stuff I've been meaning to post up here as follow-ups to posts from February. I've been pretty busy with work and am late on these. Sorry.

Python code: I've improved on my original, first Python program by adding the ability to create a whitelist file full of regular expressions. This makes it easy to isolate only those hostnames you want to find without knowing what they are. In case you're wondering, being able to do fast PTR record lookups against your DHCP ranges looking for things you don't know about is the poor man's NAC (oh, you thought EAP was the poor man's NAC?). Most Windows machines will announce their FQDN and register with Windows DHCP/DNS making them findable by doing reverse DNS lookups. Use the whitelist to exclude the stuff you know about like 'myinternaldomain.local'. Link here.

Nepenthes and ops: In this post, I mention that Tim Crothers presented on an easy way to work honeypots into network security ops. And then I totally neglected to describe how that works. Just to be clear, this is Tim's idea, not mine. I'm reposting it without permission. Hopefully he's cool with that.

Step 1. Build a VMWare image as similar to your corporate workstation image as possible. Specifically, keep it at the same patch level and run the same anti-virus or other security software with the same signatures.
Step 2. Install Linux on some old computers. Now install and configure Nepenthes on these as well.
Step 3. Deploy the Nepenthes boxes where they can collect malware: on a DSL/cable connection with no firewall, on a darknet, on a workstation network, outside the corporate firewall.
Step 4. Regularly (or automatically) review nepenthes.log and check the binaries directory for captured malware.
Step 5. Carefully transfer malware (via password-protected ZIP, for example) to the VM built in step 1.
Step 6. Disconnect the VM from the corporate network and unleash the malware. See if your AV tools detect it. Use SysAnalyzer to see what it does.
Step 7. If AV doesn't detect it, send a sample to AV vendor asking for emergency update. Deploy emergency update.

The thing I like about this is the simplicity of it. And being proactive on malware definitely won't hurt you. This stuff changes so fast it's difficult for AV vendors to keep up.

Tuesday, February 13, 2007

I am SO NOT a developer

I came up in IT as a sysadmin, and though I have a few semesters of formal education in C and C++, what I know best are scripting languages - MS-DOS batch, UNIX shell, and Perl.

There is an undeniable trend toward the widespread use of Python in the infosec industry. I was finally convinced of this when I recently got a sneak peek of a commercial app that is going to offer a Python stepping interface to its scanning engine. Very cool. And we bought it... so, I better learn how to use it.

To prepare myself, I wrote my first Python program. It replaces a shell script hack that I wrote a year or two ago that basically does bulk DNS reverse-lookups on large IP ranges. To be cool, and to prepare for working with a scanning engine, I decided to use threading.

I've been working on it in small bursts over the past two weeks, and as of this morning I have something that works very well. I also have to say, it wasn't much harder than working with Perl. It took a little Googling to find the dnspython libraries, which I used instead of writing my own DNS query code. Once I had that working, the rest was pretty straightforward. Using threading was painless, and well worth the effort. Compared to the shell script it replaces, the Python program is smoking fast, as you would expect.

Mostly this post is me patting myself on the back, but what I wanted to impart to the other non-coders that might read this is that if I can muddle out 20 lines of working Python code, you can too.