作者: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的长度,造成了越界写,引发了异常。
这个漏洞不能造成任意地址写,因此不能利用,是个拒绝服务漏洞。