cve-2017-11882 poc分析

目录

CVE-2017-11882 poc分析

思路:由样本poc出发,寻找漏洞触发点

0x00 工具&实验环境

0x01 分析行为

第一步:观察poc行为

​ 打开11882.rtf (样本文件)后,弹出一个计算器,如下图所示:

​ ok,既然弹出了计算器,那么我就用windbg附加到Word程序用bp命令下那几个函数的断点嘛(CreateProcess系列、WinExec、ShellExecute系列),然后再把11882.rtf脱进被附加调试器的Word程序。

​ 但是结果出乎我意料,计算器都弹出来了,windbg却还是BUSY状态(没有触发断点)。我的第一反应就是,难道windbg出bug了?于是我再三检查,查阅可以弹出计算器的函数,试了几次,都没有断下来。于是,我请教了一位搞逆向很厉害的树老哥。老哥直接给了我方法,解开了我的疑惑。

第二步:找出计算器被弹出的地方

树老哥 是这样做的:

首先他打开pchunter查看计算器进程的进程信息,结果如下图:

​ 发现有什么不对劲的地方了吗?calc的父进程居然不是WINWORD.EXE!(calc.exe的父进程id不等于WINWORD.EXE的进程ID),那就说明计算器不是由WINWORD.EXE进程打开的。那么根据calc的父进程ID查找,得出calc是由进程ID为2440的cmd.exe创建的。而cmd.exe的父进程ID是2476,但是当前进程列表中并没有进程ID是2476的进程信息。说明那个进程ID为2476的进程是个一秒男,放出了cmd.exe就没了。

​ 那么怎么去找到是哪个进程创建了cmd.exe呢?这个时候就可以上进程监控神器:Process Monitor了。Process Monitor 是windows下高级实时监听工具,用于监视文件系统、注册表、进程和线程的活动。

下面是具体操作步骤:

  • 首先打开process monitor开始监控,双击打开11882.rtf样本文件,待弹出计算器程序后,停止process monitor的监控(不关监控的话,会记录很多其他信息,不利于分析)。

  • 打开pchuntor查看cmd.exe的父进程id,得到cmd.exe的父进程id为428

  • 再在Process Monitor中的记录大量记录信息进行PID过滤,得到是EQNEDT32.EXE这个程序创建的cmd.exe。它的路径也可以在下图中得到:

  • 点中任意一个条目,ctrl+p可以得到EQNEDT32.EXE的父进程ID

  • ok,同理使用Process Monitor的过滤功能,得到原来是svchsot.exe创建的EQNEDIT32.EXE。

  • 在看看winword.exe中的进程相关信息,发现WINWORD.EXE是通过调用系统服务启动的公式编辑器EQNEDT32.EXE(可以选中下图中选中那项,再Ctrl+P查看这一条记录的栈信息)

    根据大佬解释,这是一种叫做decom的调用方法(后台调用com组件)。

    好了,既然已经找到了弹出计算器的进程是EQNEDT32.EXE,那么我们就打开这个程序,然后用windbg附加它,bp 那几个函数,再打开11882.rtf样本文件,看看能不能断下来。嘿嘿断下来了,可以看到执行的命令参数为:cmd.exe /c calc.exe &一堆乱码

    好,下面进行栈回溯,看看是哪儿出了问题。

0x02 调试定位漏洞触发点

  • 查看栈回溯信息如下:

    0:000> kb 4
    ChildEBP RetAddr  Args to Child              
    0018f1d0 00430c18 0018f354 00000000 0018f1f0 kernel32!WinExec
    WARNING: Stack unwind information not available. Following frames may be wrong.
    0018f214 004218e4 0018f354 0018f5e4 0018f7e4 eqnedt32!MFEnumFunc+0x241b
    0018f304 004214e2 0018f354 72ed005a 00000001 eqnedt32!FMDFontListEnum+0x650
    0018f330 0043b466 0018f354 72ed005a 0018f5e4 eqnedt32!FMDFontListEnum+0x24e

    可以定位到返回位置为0x00430c18,IDA 查看该位置处的代码如下:

    .text:00430C0C                 push    1               ; uCmdShow
    .text:00430C0E                 mov     eax, [ebp+lpCmdLine]
    .text:00430C11                 push    eax             ; lpCmdLine
    .text:00430C12                 call    ds:WinExec      ; 调用的它
    .text:00430C18                 cmp     eax, 20h
    .text:00430C1B                 jnb     loc_430C43
  • 那么是从哪儿执行到call ds:WinExec的呢?只有看栈的信息(或者使用ctrl+f8或ctrl+f7的方法,不建议使用)。很明显栈被破坏了,只看得到最近的一个返回地址是0x004218e4,IDA查看该位置处的代码如下:

    .text:004218DE                 push    eax             ; lpString1
    .text:004218DF                 call    sub_4115A7 ;调用的它
    .text:004218E4                 add     esp, 4
    .text:004218E7                 test    eax, eax
    .text:004218E9                 jz      loc_421919
  • 好,使用OD来附加(因为windbg不好保存断点记录,也不好及时的看堆栈的信息,所以现在换OD上)。先打开EQNEDT32.EXE,用OD附加后在0x4218DF处下断。打开11882.rtf样本poc文件,发现断到了0x4218DF处。

    然后单步步入,看看执行到call ds:WinExec的调用顺序是啥子样子的。有耐心的分析出来是这样的:

    sub_4115A7--->sub_41160F--->ret--->call ds:WinExec

    Tip:我在进入了sub_41160F后(已经使用了bp WinExec函数下断),使用Ctrl+F8,让其自动单步步过找下一个call的时候,发现其实就没有下一个call了。

    意思是:当自动单步步过停止的时候,按esc键先是回到call ds:WinExec处,再按一下esc键,就回到了sub_41160F函数的最后0x00411874 处了,而该处是一条ret指令。

    所以我在这条ret指令处下了断点,重新调试,发现断到这个ret指令的时候,栈顶的值为0x00430c12,也就是call ds:WinExec指令的地址。

    所以判断应该是栈溢出,覆盖了函数的返回地址。

  • 那么是哪儿把sub_41160F的返回地址给覆盖成了0x430c12了呢?先不管,先看看进入sub_41160F函数的时候栈的情况:可以发现刚进入的时候,返回地址是没有出问题的

  • 继续单步调,发现到了这儿,返回地址就马上从正确的0x004115d8变为了,0x00430c12。

0x03 分析漏洞成因

​ 下面来分析一下这个sub_41160F函数:

int __cdecl sub_41160F(char *a1, char *a2, int a3)
{
  int result; // eax
  char v4; // [esp+Ch] [ebp-88h]
  char v5; // [esp+30h] [ebp-64h]
  __int16 v6; // [esp+51h] [ebp-43h]
  char *v7; // [esp+58h] [ebp-3Ch]
  int v8; // [esp+5Ch] [ebp-38h]
  __int16 v9; // [esp+60h] [ebp-34h]
  int v10; // [esp+64h] [ebp-30h]
  __int16 v11; // [esp+68h] [ebp-2Ch]
  char v12; // [esp+6Ch] [ebp-28h]
  int v13; // [esp+90h] [ebp-4h]

  LOWORD(v13) = -1;
  LOWORD(v8) = -1;
  v9 = strlen(a1);
  strcpy(&v12, a1); //这儿造成的 :覆盖返回地址
  _strupr(&v12);
 /*
 .....省略一部分
 */
  return result;
}

堆栈图:

strcpy(&v12,a1) 太明显不过的栈溢出了,对栈上进行字符串拷贝未做长度检测,如果a1所指向的字符串的长度大于或等于48个字节,就可以完全覆盖掉返回地址。调试的时候发现a1所指向的内存为下图所示:

的确如此,((DWORD*)a1)[11] = 0x00430c12 就是call ds:WinExec的地址。

当然,读取a1的过程我也分析了下:分析思路是 根据栈回溯,结合OD,IDA调试查找。是在sub_43b418中调用sub_4164fa读取的a1,读取终止条件是一直读到\0结束。

_BYTE *__cdecl sub_4164FA(_BYTE *a1)
{
  char *v1; // ST0C_4
  _BYTE *result; // eax

  do
  {
    v1 = a1++;
    *v1 = sub_416352();//从一块固定内存块中读取
  }
  while ( *v1 );//读到\0结束
  result = a1;
  *a1 = 0;
  return result;
}

所以关键就是 看怎么才能构造个样本,让我们执行到这个地方,公式编辑器是将样本的哪个地方加载到了那个固定内存块中,如果可以找到,那么就可以构造样本了。

经过回溯栈,找关键call 的方式,我发现 好像并不是公式编辑器解析的文本文档。

全是ole32的rpc*调用。等于说,我分析不动了。那就先暂时分析到这儿,等我功力上去了,再来搞。

0x04 总结

​ 微软和Intel处理器的成功很大一部分原因是因为他们的产品始终都尽最大努力向下兼容。但是这样也造成了极大的安全隐患。比如这个公式编辑器,超级超级老了,不支持ASLR,DEP等漏洞缓解措施。但是为了保持向下兼容,尽管都office 2016了,微软还是将它保留了下来。

​ 所以,很多人都会选择去挖那些微软为了兼容性而保留下来的软件的漏洞。我估计不止office软件,像服务器上的软件比如:SQL Server系列的软件 肯定也有很多。

​ 怎么去挖呢???来个高人指点指点我吧。

猜你喜欢

转载自www.cnblogs.com/amaza/p/10101375.html