本文是针对ASX to MP3 Converter进行的漏洞分析
本来是浏览学长博客ASX TO MP3本地代码执行漏洞 感觉这个洞比较好入手,思路也比较清晰,就想着简单复现一下,但是在实际分析之中发现与博客原文写的有较大出入,于是自己一路分析上去,想搞清楚差异的地方探究真正的漏洞原因。
实验环境与原博中一样:
Asx to MP3 Converter 3.1.2(原博中写的是3.0.0,但提供的下载链接是3.1.2,是否是分析出现差异的原因?)
Windows xp sp3
Windbg
IDA pro
OD
软件下载地址:6baK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2W2P5s2m8D9L8$3W2@1i4K6u0V1k6r3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2M7s2m8K6i4K6u0r3j5U0x3I4j5e0R3@1k6e0M7&6k6o6V1&6y4o6q4V1z5o6V1K6x3K6k6T1y4U0M7H3z5r3g2X3y4e0u0S2x3U0m8Q4x3X3c8m8f1#2S2@1L8@1#2b7x3@1y4G2L8Y4k6W2M7Y4c8W2M7W2)9#2k6U0x3I4x3U0q4Q4x3X3g2W2P5r3f1`.
为了保证系统在遇到错误时不至于崩溃,仍能够健壮稳定地继续运行下去,Windows会对运行在其中的程序提供一次补救的机会来处理错误,这种机制称为Windows异常处理机制。S.E.H即异常处理结构体(Structure Exception Handler)S.E.H链表指针和异常处理函数句柄,共计8个字节。
S.E.H结构体存放在系统栈中,线程初始化时,会自动向栈中安装S.E.H作为默认的异常处理。当异常发生时,操作系统会中断程序,并首先从T.E.B的0字节偏移处取出距离栈顶最近的S.E.H中的异常处理函数进行处理。
利用思路基本是:
利用poc生成一个畸形m3u文件,播放器打开文件,程序崩溃
PoC:
原文理想中的情况应该是:
1.使用windbg到达漏洞现场,使用kb发现堆栈调用全是系统dll的调用,无法回溯漏洞发生前的关键函数,于是更改思路;
2.想到该漏洞是一个文件格式的poc,那么在主程序中很有可能会通过fopen调用这个poc打开,以读取其中的文件从而触发文件格式的漏洞,于是想办法通过fopen函数来定位到漏洞发生前的函数;
3.在IDA Pro中找到5个fopen的字符串函数,在这几个地址处用windbg打断点;
4.发现在加载漏洞文件后windbg调用了fopen两次,第一次正常,在第二次时将畸形的文件格式读入到缓冲区中,而此时没有对文件的长度进行任何检查,而是直接读入缓冲区;
5.继续运行再调用后续函数时,返回地址被覆盖,导致出现一个错误,调出SEH,而SEH已经被覆盖,最终程序结束;
最终得出结论:由于对于filename的长度检查不严格,导致直接作为参数回调到外层函数中,因此当外层函数结束时,程序返回到一个不可读的地址,从而触发了异常处理流程,因为文件畸形内容,导致了SEH指针被覆盖,程序可控。
我按照此思路复现实际遇到的情况:
1.使用windbg达到漏洞现场,发现可以回溯漏洞发生前的关键函数,断在了00430402的位置
2.在IDA中同样搜寻到5个fopen的调用并在windbg中对应打了断点,但是在实际调用中加载漏洞文件后windbg调用了fopen仅1次而不是2次,之后又会断在00430402
3.使用OllyDbg分析程序崩溃位置也与原博不同,原博程序是在执行到最后ret 4时,跳转地址被覆盖为414141不可读出现错误,实际调试过程中并未执行到该函数段最后,而是在过程中读写错误崩溃的,具体原因是:
0042BD1D |mov ecx,dword ptr ss:[esp+0xAA48] ;堆栈 ss:[000DBFB4]=41414141
0042BD26 |push ecx ;堆栈000D1568 41414141 AAAA
004303E9 | mov ebx,dword ptr ss:[esp+0x10] ;堆栈 ss:[000D1568]=41414141
004303FE | mov esi,ebx ;ebx=41414141
00430402 |rep movs dword ptr es:[edi],dword ptr ds:[esi] ;ds:[esi]=[41414141]=???
在执行rep movs操作中由于414141地址不可读导致出错,并且此时SEH已然被覆盖了
1.既然没有第二次fopen依然崩溃,问题可能并不出在fopen操作,那么问题出在哪?
2.从图中可知SEH已经被覆盖,那么是在之前何处覆盖的?操作是什么?
此时我发现不止有我对此质疑,网上另一篇帖子也无法复现,和我的情况完全相同,为我提供了下一步的思路
漏洞分析——ASX to MP3 Converter本地代码执行漏洞
从崩溃地址00430402开始向上分析,追溯这一堆A的由来,首先断点依然在fopen处,虽然已经感觉问题不在这里,但还是看一下
从地址428B46调用fopen,在428B72处调用freed读入文件的值,之后一直到fclose都没有任何异常数据,所以漏洞点并不在这部分代码中
继续向下调试,发现在42B62B位置调用MSA2Mfil.Playlist_FindNextItem后就开始有异常数据,可以判断问题出在该函数内部,且在模块列表中发现该函数是在程序自带的Dll库中,是MSA2Mfilter03.dll
同时使用IDA查看MSA2Mfilter03.dll中的代码,在Functions中确实有Playlist_FindNextItem函数
在OD中也进入该dll逐步调试,在10008D55位置第一次出异常数据,在IDA中对应发现在函数sub_10008D20中无论条件如何,最终都会输出该异常数据
继续向下走,依旧是在该dll中,发现在地址1000D3C3处为溢出点,SEH就是在此处被覆盖掉的
ida中查看REP MOVS循环,其实是使用函数strcpy完成循环复制,问题就出现在strcpy函数。
此时我们就可以简单计算一下从覆盖的起点(由图可知D159C)到最近一个SEH(由图可知DBFA4)有多远了,它们相减是43528,看来poc设置50000还是有考虑的,经过多次测算调试,poc中设置43483个A(前面一串路径占了部分位置),后面的八个字符即正好是SEH的位置,后面写利用脚本可以使用该位置。
看到这里有没有朋友发现奇怪的地方,在第二部分原博复现:我实际的操作第3步中分析了程序不像原博中一样执行到了最后而是在中间断开的原因,起端就是如下图这个位置堆栈DBFB4的非法数据mov进了ecx,最终导致内存读写错误,而DBFB4的非法数据从何而来我们从上图就可以一清二楚
如果我们想要复现原博中的情况,让它执行到最后再跳转错误,只需要在构造poc时,中间多垫几层,在DBFB4位置输入一个合法地址就可以了,此时poc应该为
构建地址后,成功继续向下走,分析过程就与原博基本相同了,但是在最后一步retn 4时,发现跳转为90909090也就是nop而不是A,仔细看堆栈,位置为DC5B8正好为防止内存出错构造地址的上一条
则poc进一步改为
至此分析结束,进一步的利用exp可以在这个poc基础上改。
综合看下来,该漏洞并不是由于fopen而产生的,具体产生原因应该是应用程序自带的MSA2Mfilter03.dll组件内Playlist_FindNextItem函数解析恶意.M3U文件时不正确的边界检查引起的,溢出点在1000D3C3地址处,REP MOVS循环,其实是使用函数strcpy完成循环复制,问题就出现在strcpy函数,精心构造的POC可导致SEH指针被覆盖,因此在函数结束时,程序返回到一个不可读的地址,从而触发了异常处理流程,关键节点的地址均可在poc中改变,程序可控。
ASX TO MP3本地代码执行漏洞 31fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4K9r3g2J5k6h3W2K6K9K6m8K6K9r3I4Q4x3X3g2@1L8%4m8Q4x3V1k6H3L8%4y4@1i4K6u0r3x3U0l9I4y4W2)9J5k6o6p5I4i4K6u0V1x3o6b7`.
[翻译]Windows漏洞利用开发 - 第3部分:偏移更改和重定位模块 https://bbs.pediy.com/thread-225831.htm
漏洞分析——ASX to MP3 Converter本地代码执行漏洞 47eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6S2P5h3g2K6j5i4N6&6k6i4u0Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8U0t1H3x3e0W2Q4x3V1j5H3y4#2)9J5c8U0l9^5i4K6u0r3i4K6t1#2c8e0k6Q4x3U0g2n7b7#2)9J5y4e0S2r3i4K6t1#2c8e0k6Q4x3U0g2n7y4q4)9J5y4e0W2q4i4K6t1#2c8e0g2Q4x3U0f1^5z5q4)9J5y4e0R3$3i4K6t1#2c8e0k6Q4x3U0f1&6c8g2)9J5y4e0V1H3i4K6t1#2c8e0u0Q4x3U0f1^5x3q4)9J5y4e0V1@1i4K6t1#2c8e0u0Q4x3U0f1^5x3q4)9J5y4e0V1@1b7g2y4j5i4K6u0V1N6r3!0Q4x3X3c8y4f1o6y4Q4x3X3c8o6L8$3&6$3k6i4u0@1k6i4u0Q4x3U0g2q4y4W2)9J5y4e0W2o6i4K6t1#2b7f1y4Q4x3U0g2q4y4g2)9J5y4e0W2o6i4K6t1#2b7U0m8Q4x3U0g2q4y4q4)9J5y4f1u0n7i4K6t1#2b7e0y4Q4x3U0g2q4y4#2)9J5y4f1p5H3i4K6t1#2z5o6q4Q4x3U0g2q4y4W2)9J5y4e0R3&6i4K6t1#2b7e0N6Q4x3U0g2q4z5q4)9J5y4f1p5I4i4K6t1#2z5p5y4Q4x3U0g2q4y4W2)9J5y4f1u0o6i4K6t1#2z5p5k6Q4x3U0g2q4y4W2)9J5y4f1t1@1i4K6t1#2z5f1g2Q4x3V1j5`.
Windows漏洞利用开发教程 Part 4:SEH 69dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2X3M7X3g2W2j5Y4g2X3i4K6u0W2j5$3!0E0i4K6u0r3j5i4u0@1K9h3y4D9k6i4y4Q4x3V1k6K6P5i4y4@1k6h3#2Q4x3V1j5I4y4K6l9%4x3o6y4Q4x3X3g2Z5N6r3#2D9
poc
=
"\x41"
*
50000
rst
=
open
(
"exploit.m3u"
,
'w'
)
rst.write(poc)
rst.close();
poc
=
"\x41"
*
50000
[注意]看雪招聘,专注安全领域的专业人才平台!
最后于 2022-10-18 16:20
被FSTARK编辑
,原因: 更改细节描述