[CVE-2016-5108]VideoLAN VLC Media Player 2.2.1越界写拒绝服务漏洞

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


漏洞说明


VideoLAN VLC Media Player是一款播放器,这个漏洞编号为CVE-2016-5108,播放器在处理某数据的时候,由于处理某数据时没有对数据长度进行严格校验,导致越界写入引发拒绝服务漏洞,下面对此漏洞进行详细分析。

软件下载:
https://www.exploit-db.com/apps/b8c997e772be343e1664fee14c1fb9b7-vlc-2.2.1-win32.exe

PoC:
https://github.com/offensive-security/exploit-database-bin-sploits/raw/master/sploits/41025.mov


漏洞复现


首先打开VLC,打开mov文件,程序崩溃,附加windbg

(1588.788): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ffffffff ebx=0000178f ecx=00000058 edx=0000178f esi=0d1fd000 edi=1a44fdc8
eip=68322501 esp=1a44fd30 ebp=155cb8de iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010286
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\VideoLAN\VLC\plugins\codec\libadpcm_plugin.dll - 
libadpcm_plugin+0x2501:
68322501 66891e          mov     word ptr [esi],bx        ds:0023:0d1fd000=????

通过kb回溯堆栈调用

0:024> kb
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\system32\KERNELBASE.dll - 
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
1a44fd38 75536a18 7a0e1e88 1a44fdc8 0d1fd000 libadpcm_plugin+0x2501
00000000 00000000 00000000 00000000 00000000 KERNELBASE!InterlockedCompareExchange+0xf8

之前只有一个Interlock的原子函数调用,来看一下当前是用bx向esi指针写入数据,esi指针的值是一个无效值,来看一下之前的情况。

0:024> dd 0d1fd000-8
0d1fcff8  c0c0178f c0c0c0c0 ???????? ????????

可以看到,前面还有值,这有可能是一个长度控制不严格引发的越界写漏洞。


漏洞分析


分析的过程中,发现了这个漏洞处于一个函数中。

int __cdecl sub_714C18B0(int a1, int *a2)
{
    while ( 1 )
    {
    }
}

函数sub_714c18b0中有一处while循环,这个while循环代码量很长,执行的是一个向内存拷贝的操作,这里我就不具体分析整个代码逻辑,只看关键部分,首先是对待拷贝区的赋值。

.text:714C24B7 ; 846:         v83 = 88;
.text:714C24B7                 cmova   ecx, eax
.text:714C24BA                 mov     [esi], edx

714c24ba地址执行完毕后,edx存放的是待拷贝缓冲区的指针,它会交给esi地址中。

0:012> g
Breakpoint 0 hit
eax=00000058 ebx=00001200 ecx=00000034 edx=00001200 esi=16d2fdc8 edi=0e1f6e40
eip=6bdf24ba esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
libadpcm_plugin+0x24ba:
6bdf24ba 8916            mov     dword ptr [esi],edx  ds:0023:16d2fdc8=16d2fd74
0:006> p
eax=00000058 ebx=00001200 ecx=00000034 edx=00001200 esi=16d2fdc8 edi=0e1f6e40
eip=6bdf24bc esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
libadpcm_plugin+0x24bc:
6bdf24bc 897c2418        mov     dword ptr [esp+18h],edi ss:0023:16d2fd48=16d2fde8
0:006> dd 16d2fdc8
16d2fdc8  0e1f6e40

之后每轮都会向这个缓冲区指针内写入值,接下来继续单步跟踪,发现了要拷贝字符串的赋值。

0:006> p
eax=00000058 ebx=00001200 ecx=00000034 edx=00001200 esi=16d2fdc8 edi=0e1f6e40
eip=6bdf24c0 esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
libadpcm_plugin+0x24c0:
6bdf24c0 e9ef000000      jmp     libadpcm_plugin+0x25b4 (6bdf25b4)
0:006> p
eax=00000058 ebx=00001200 ecx=00000034 edx=00001200 esi=16d2fdc8 edi=0e1f6e40
eip=6bdf25b4 esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
libadpcm_plugin+0x25b4:
6bdf25b4 8b3c8dc062df6b  mov     edi,dword ptr libadpcm_plugin!vlc_entry_license__2_2_0b+0x2c20 (6bdf62c0)[ecx*4] ds:0023:6bdf6390=00000424

在6bdf25b4地址位置执行了一处赋值操作,会把6bdf62c0这个固定缓冲区中的内容拷贝到edi中,而是从ecx*4+6bdf62c0的位置向前拷贝,ecx会逐步减少,拷贝的长度就是(6bdf6390-6bdf62c0)*8,这个后面会提到。

接下来会将edi的值加eax的值,这个eax就是每次循环最后赋值的值,相加后算数右移3位。

0:006> p
eax=00000058 ebx=00000000 ecx=00000034 edx=00001200 esi=16d2fdc8 edi=00000424
eip=6bdf25bf esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
libadpcm_plugin+0x25bf:
6bdf25bf 89f8            mov     eax,edi
0:006> p
eax=00000424 ebx=00000000 ecx=00000034 edx=00001200 esi=16d2fdc8 edi=00000424
eip=6bdf25c1 esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
libadpcm_plugin+0x25c1:
6bdf25c1 c1f803          sar     eax,3
0:006> p
eax=00000084 ebx=00000000 ecx=00000034 edx=00001200 esi=16d2fdc8 edi=00000424
eip=6bdf25c4 esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei pl nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000207
libadpcm_plugin+0x25c4:
6bdf25c4 89de            mov     esi,ebx

算数右移后的值在eax中,是0x84,随后会将eax的值交给ebx,再把ebx的值和上一轮的值相加。

0:006> p
eax=00000084 ebx=00000000 ecx=00000034 edx=000004a8 esi=00000000 edi=00000424
eip=6bdf25cf esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
libadpcm_plugin+0x25cf:
6bdf25cf 0f44d0          cmove   edx,eax
0:006> p
eax=00000084 ebx=00000000 ecx=00000034 edx=00000084 esi=00000000 edi=16d2fdc8
eip=6bdf25f4 esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
libadpcm_plugin+0x25f4:
6bdf25f4 0317            add     edx,dword ptr [edi]  ds:0023:16d2fdc8=00001200

最后会将edx的值先存放进eax指针,这个指针很熟悉吧,就是之前相加的指针,之后每次都会叠加,而edx同时也会交给ebx,最后ebx会执行拷贝。

0:006> p
eax=16d2fdc8 ebx=00000000 ecx=00000034 edx=00001284 esi=00000000 edi=16d2fdc8
eip=6bdf3494 esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
libadpcm_plugin+0x3494:
6bdf3494 8910            mov     dword ptr [eax],edx  ds:0023:16d2fdc8=00001200
0:006> p
eax=16d2fdc8 ebx=00000000 ecx=00000034 edx=00001284 esi=00000000 edi=16d2fdc8
eip=6bdf3496 esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
libadpcm_plugin+0x3496:
6bdf3496 89d3            mov     ebx,edx
0:006> p
eax=00000033 ebx=00001284 ecx=00000058 edx=00001284 esi=0e1f6e40 edi=16d2fdc8
eip=6bdf2501 esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
libadpcm_plugin+0x2501:
6bdf2501 66891e          mov     word ptr [esi],bx        ds:0023:0e1f6e40=c0c0

那么现在这个漏洞的原因就很清晰了,从6bdf6390开始向前依次拷贝内容,每次拷贝的内容都会交给固定指针,这个指针里的值会进行叠加算术右移等操作,之后继续进行拷贝,而这个长度没有进行控制。

而每一轮拷贝,拷贝区长度都会加0x10。

0:006> p
eax=00000033 ebx=00001284 ecx=00000058 edx=00001284 esi=0e1f6e40 edi=16d2fdc8
eip=6bdf2501 esp=16d2fd30 ebp=11b208c2 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
libadpcm_plugin+0x2501:
6bdf2501 66891e          mov     word ptr [esi],bx        ds:0023:0e1f6e40=c0c0
0:006> g
Breakpoint 1 hit
eax=00000031 ebx=00001369 ecx=00000058 edx=00001369 esi=0e1f6e50 edi=16d2fdc8
eip=6bdf2501 esp=16d2fd30 ebp=11b208c3 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
libadpcm_plugin+0x2501:
6bdf2501 66891e          mov     word ptr [esi],bx        ds:0023:0e1f6e50=c0c0

注意esi的值,接下来来看一下几轮拷贝之后esi最开始拷贝位置的值的前后变化。

0:006> dd esi
0e1f6e40  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
0e1f6e50  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
0e1f6e60  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
0e1f6e70  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
0e1f6e80  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
0e1f6e90  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
0e1f6ea0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
0:006> dd 0e1f6e40
0e1f6e40  c0c01284 c0c0c0c0 c0c012fc c0c0c0c0
0e1f6e50  c0c01369 c0c0c0c0 c0c013cc c0c0c0c0
0e1f6e60  c0c01426 c0c0c0c0 c0c01478 c0c0c0c0
0e1f6e70  c0c014c2 c0c0c0c0 c0c01506 c0c0c0c0
0e1f6e80  c0c01543 c0c0c0c0 c0c0157b c0c0c0c0
0e1f6e90  c0c015ae c0c0c0c0 c0c015dc c0c0c0c0
0e1f6ea0  c0c01606 c0c0c0c0 c0c0162c c0c0c0c0
0e1f6eb0  c0c0164e c0c0c0c0 c0c0166d c0c0c0c0

而拷贝区的内容则是这样

0:006> dd 6bdf6360
6bdf6360  00000151 00000173 00000198 000001c1
6bdf6370  000001ee 00000220 00000256 00000292
6bdf6380  000002d4 0000031c 0000036c 000003c3
6bdf6390  00000424 0000048e 00000502 00000583
6bdf63a0  00000610 000006ab 00000756 00000812
6bdf63b0  000008e0 000009c3 00000abd 00000bd0
6bdf63c0  00000cff 00000e4c 00000fba 0000114c

也就是说,每次拷贝4个字节,经过算法处理后,会在待拷贝区偏移8字节,长度还要乘以2,最后也就是说拷贝的长度,超过了申请buffer的长度,造成了越界写,引发了异常。

这个漏洞不能造成任意地址写,因此不能利用,是个拒绝服务漏洞。

Comments
Write a Comment