MSN Password Recovery 1.30 -DOS分析

 

环境及链接介绍

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也可以在此基础上进行。

这个比较简单,适合新手跟着做,体验一下缓存区溢出的调试的感觉。

(完)