环境及链接介绍
1. poc及漏洞信息来源:https://www.exploit-db.com/exploits/47839
2. 软件链接 https://www.top-password.com/download/MSNPRSetup.exe
3. 实验环境:windows 10
4. 调试工具:IDA 7.5 以及 Windows商店的WinDbg Preview
简介
这个漏洞的实质是一个栈溢出漏洞,而且没有加任何限制,因为好长时间没有调试过了,拿这个洞熟悉一下,顺便搞清楚出现这个洞的原因是什么。
调试过程
1.复现
第一步就是按照exploit-db上介绍的步骤来一遍验证这个软件是存在这个DOS漏洞的。
具体的步骤:
1) 安装MSN软件
2) 运行它提供的poc脚本生成CRASH.txt,就是9000个’A’
3) 运行MSN软件,点击Enter Key
4) 然后把9000个’A’复制出来,分别粘贴到User Name 和 Registration Code两个框里面
5) 点击OK,软件崩溃,证实存在漏洞。
2.原因
1) 使用windbg打开MSN软件,等程序加载完以后,点击’Go’按钮或者在命令行中输入’g’命令,MSN界面出现。
2) 按照复现中的步骤把程序搞崩
3) 这个时候windbg就会定点到程序崩溃的地址如下图所示
图中标记的地址就是程序的崩溃点,地址是0x00406e72
4) 使用IDA打开MSN软件,注意是使用32位的那个打开
5) 然后使用快捷键”G”,打开跳转框,输入00406e72,点击确定跳转到目标地址
就是我打断点标红的位置
6) 为了方便查看代码逻辑,直接F5反汇编。(这里第一次进来的时候,这个位置没有被识别成一个函数,所以我在0x00406DF4的位置Creat了一个函数出来,操作就是在选中这一行,鼠标右键,点击Create function,这样就能F5反汇编了)
跳转到崩溃点00406e72。
7) 可以看到这里是一个循环,*v3一直向v3[v4]的地方输入数据,这个循环的作用就是一个一个的复制字符串,而且没有看到长度限制,故而实现溢出,造成崩溃。
3.精确POC中字符串的长度
在第二步原因中我们大概已经知道崩溃是如何造成的,反观exploit-db中给出的poc非常的粗糙,明显就是想省劲,给了一个非常大的字符串实现溢出,为了进一步了解,现在需要精确一下poc中的字符串长度
在反汇编中看的不太清楚,我们回到汇编代码中看一下附近的代码
.text:00406E70 loc_406E70: ; CODE XREF: sub_406DF4+86↓j
.text:00406E70 mov cl, [eax]
.text:00406E72 mov [edx+eax], cl
.text:00406E75 add eax, 1
.text:00406E78 cmp cl, bl
.text:00406E7A jnz short loc_406E70
上面的代码就是c语言,大意就是把eax寄存器指向的地址的数据放到[edx+eax]里面去,下面验证一下,这些数据是不是’A’
点击windbg中的Restart按钮,重新调试一遍,不过这次在点击’Go’之前先运行命令’bp 0x00406E65’现在崩溃前的位置下个断点,然后点击’GO’命令,在MSN界面中输入字符串,点击OK,windbg就会运行到我们设置断点的位置,到现在程序还没崩,说明基本上就是后面的循环出问题了。
在命令行中输入’p’命令(单步步过,执行一遍运行一步),运行到循环中,可以看到cl的值是0x00000041,就是’A’的ASCII码,所以我们猜测的是正确的。
现在回到汇编代码
.text:00406E70 loc_406E70: ; CODE XREF: sub_406DF4+86↓j
.text:00406E70 mov cl, [eax]
.text:00406E72 mov [edx+eax], cl
.text:00406E75 add eax, 1
.text:00406E78 cmp cl, bl
.text:00406E7A jnz short loc_406E70
既然是[edx+eax]的地方发生溢出,那就探究一下[edx+eax]的地址是个啥,往前找一下eax和edx的数据来源
.text:00406DF4 sub_406DF4 proc near
.text:00406DF4
.text:00406DF4 arg_1FC = byte ptr 200h
.text:00406DF4 arg_1FD = byte ptr 201h
.text:00406DF4 arg_3FC = dword ptr 400h
---省略部分无关代码---
.text:00406E65 loc_406E65: ; CODE XREF: sub_406DF4+63↑j
.text:00406E65 mov eax, [esi]
.text:00406E67 lea edx, [esp+10h+arg_1FC]
.text:00406E6E sub edx, eax
.text:00406E70
.text:00406E70 loc_406E70: ; CODE XREF: sub_406DF4+86↓j
.text:00406E70 mov cl, [eax]
.text:00406E72 mov [edx+eax], cl
.text:00406E75 add eax, 1
.text:00406E78 cmp cl, bl
.text:00406E7A jnz short loc_406E70
从上面的代码中可以看到,edx来自于[esp+10h+arg_1FC],esp是栈顶,arg_1FC的大小是200h,大概可以确定edx的值,再看后面后一个操作是sub edx, eax
这句话的意思就是 edx = edx – eax,所以后面循环中的[edx+eax],实际上就是[esp+10h+arg_1FC]的值,我们都知道溢出是向栈底溢出,只要可以覆盖的掉栈底,就可以造成崩溃,那我们就探究一下距离栈底的距离。
从windbg中可以看到,ebp = 0x0019f0f4 ,esp = 0x0019e564 所以到栈底的距离是 0x0019f0f4 – (0x0019e564 + 200h + 10h) = 980h
所以只需要填充980h个’A’就可以造成崩溃,就是2432个’A’,尝试一下,可以造成崩溃,然后试一下2431个’A’,程序弹出弹框提示错误,说明不能造成崩溃,假想成立。
后来想了一下,这里有两个输入框,难道都是从这里崩溃的吗?那用一个行不行,所以再次运行了一遍MSN,这次只在User Name的输入框里面输入2432个’A’,下面那个先不管,也可以崩溃,那说明我们找到的点00406E72是输入框User Name的崩溃点,那下面的Registration Code可以独立造成崩溃吗?
再试一遍,这次只在Registration Code输入框中输入2432个’A’,点击OK,然后软件弹框报错,说明Registration Code崩溃点不在这儿,那就继续调试,按照原因中的操作步骤,只不过这次在向MSN界面填充的时候,只在Registration Code中填入9000个’A,然后程序崩溃,windbg给出的崩溃点是0x00406ef2,如下图所示
再去IDA里面看一下0x00406ef2的代码
反汇编代码
v9 = *v8;
v10 = (char *)(&v15 - *v8);
do
{
v11 = *v9;
v9[(_DWORD)v10] = *v9;
++v9;
}
汇编代码
.text:00406EE1 loc_406EE1: ; CODE XREF: sub_406DF4+DF↑j
.text:00406EE1 mov eax, [esi]
.text:00406EE3 lea edx, [esp+10h]
.text:00406EE7 sub edx, eax
.text:00406EE9 lea esp, [esp+0]
.text:00406EF0
.text:00406EF0 loc_406EF0: ; CODE XREF: sub_406DF4+106↓j
.text:00406EF0 mov cl, [eax]
.text:00406EF2 mov [edx+eax], cl
.text:00406EF5 add eax, 1
.text:00406EF8 cmp cl, bl
.text:00406EFA jnz short loc_406EF0
不能说很相似,可以说是一模一样了,唯一一个不同的点,就是lea edx, [esp+10h]
,也就是说这次的距离栈底的距离应该是,ebp = 0x0019f0f4 ,esp = 0x0019e564 所以到栈底的距离是 0x0019f0f4 – (0x0019e564 + 10h) = B80h,两处代码在同一个函数里面,所以ebp和esp没变,就是2944个’A’才行。
尝试一下,只在Registration Code输入2944个’A’可以造成崩溃,但是2943个就会弹出错误,说明这个值是正确的。
结论
两个输入框都可以单独的造成崩溃,分别是User Name的2432个’A’和Registration Code的2944个’A’。
如果想要进一步实现利用,即把poc编写为exp也可以在此基础上进行。
这个比较简单,适合新手跟着做,体验一下缓存区溢出的调试的感觉。