前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MS08-067漏洞调试分析详解

MS08-067漏洞调试分析详解

作者头像
安恒网络空间安全讲武堂
发布2018-02-06 15:18:57
2K0
发布2018-02-06 15:18:57
举报

MS08-067漏洞调试分析详解

一、前言

在《Metasploit渗透测试魔鬼训练营》中有对MS08-067漏洞原理的分析,不过作者的文笔十分晦涩难懂,读起来十分难消化,我反复阅读钻研了几遍,配合实践分析,对该部分的内容大致理解了一些,按照清晰的思路记录了这篇文章,并画了漏洞产生的流程图,增强了对漏洞组件溢出方式、shell插入方式的理解。

二、 简介

MS08-067漏洞是一个经典的、影响广泛的远程代码执行漏洞,在MSRPC over SMB通道调用Server服务程序中的NetPathCanonicalize函数时触发。

NetPathCanonicalize函数在远程访问其他主机时会调用NetpwPathCanonicalize函数,对远程访问的路径进行规范化。在路径规范化的操作中,服务程序对路径字符串的地址空间检查存在逻辑漏洞。

所谓路径规范化,就是将路径字符串中的【/】转换为【\】,同时去除相对路径【\.\】和【\..\】。如:

**/*/./** => **\*\**

**\*\..\** => **\**

攻击者通过精心设计输入路径,可以在函数去除【\..\】字符串时,把路径字符串中的内容复制到路径字符串之前的地址空间中(低地址),达到覆盖函数返回地址,执行任意代码的目的。

三、环境

  • 分析环境:Windows Server 2003 SP0 EN(10.10.10.130)
  • 攻击机:Kali-2017.1(10.10.10.128)
  • 漏洞组件:netapi32.dll(版本5.2.3790.0)
  • 工具:VMware WorkStation 12 Pro、OllyDbg 2.0、IDA Pro、Metasploit

四、调试

(一)组件静态分析

1. 函数流程分析

根据Microsoft提供的资料,定位到包含MS08-067漏洞的系统模块netapi32.dll(路径C:\windows\system32\)。

在Win2k3靶机中打开路径c:\windows\system32,找到netapi32.dll并拖入IDA Pro中,待其自动分析结束后,在左侧Fuction window查看此动态链接库的函数,找到漏洞所在的NetpwPathCanonicalize函数。

查找动态链接库中的函数

双击NetpwPathCanonicalize函数,定位到该函数位置,观察其流程图,发现NetpwPathCanonicalize并没有直接进行输入路径和规范化,而是继续调用了下级函数CanonicalizePathName。

函数流程图

2. 函数代码分析

IDA分析 NetpwPathCanonicalize 函数代码(F5 + 整理 + 主要代码):

2.1 函数声明及参数

代码语言:js
复制
DWORD NetpwPathCanonicalize(
    LPWSTR PathName, //需要标准化的路径
    LPWSTR Outbuf, //存储标准化后的路径的Buffer
    DWORD OutbufLen, //Buffer长度
    LPWSTR Prefix, //可选参数,当PathName是相对路径时有用
    LPDWORD PathType, //存储路径类型
    DWORD Flags // 保留,为0
 )

2.2 主要代码

代码语言:js
复制
int __stdcall NetpwPathCanonicalize(LPWSTR PathName, LPWSTR Outbuf, DWORD OutbufLen, LPWSTR Prefix, LPDWORD PathType, DWORD Flags)
{
  bool v7;
  int result;
  v7 = !Prefix || !*Prefix;
  Prefix = (LPWSTR)*PathType;
  if ( *PathType || (result = NetpwPathType(PathName, (int)&Prefix, 0), !result) )
    {
      if ( v7 || (result = NetpwPathType(Prefix, (int)&Flags, 0), !result) )
      {
        if ( OutbufLen != 0 )
        {
          *Outbuf = 0;
          result = CanonPathName(Prefix, PathName, Outbuf, OutbufLen, 0); //===================》

核心函数,主要处理在这里,问题也出在这里

代码语言:js
复制
          if ( !result )
            result = NetpwPathType(Outbuf, (int)PathType, 0);
        }
        else
        {
          result = 2123;
        }
      }
    }
  return result;
}
int __stdcall CanonPathName(LPWSTR PathPrefix, LPWSTR PathName, LPWSTR Buffer, DWORD BufferSize, LPDWORD RetSize)
{
  size_t preLen;
  size_t pathLen;
  wchar_t pathBuffer[MAX_PATH*2 + 1];
  if ( PathPrefix && *PathPrefix )
  {
    preLen = wcslen(PathPrefix);
    if ( preLen != 0)
    {
      if ( preLen > 520 )   //520 = sizeof(pathBuffer) - 1
        return 0x7Bu;    // ERROR_INVALID_NAME
      wcscpy(pathBuffer, PathPrefix);
      if ( pathBuffer[preLen-1] != '\\' && pathBuffer[preLen-1] != '/') //=============================》判断前缀是否以'\'或'/'结尾
          wcscat(pathBuffer, L"\\");
          ++preLen;
      }
      if ( PathName[0] == '\\' || PathName[0] == '/' )
        ++pathLen;
    }
  }
  else
  {
    pathBuffer[0] = 0;
  }
  pathLen = wcslen(PathName);
  if (pathLen + preLen > sizeof(pathBuffer) - 1)
    return 0x7Bu; // ERROR_INVALID_NAME
  wcscat(pathBuffer, PathName);
  if ( pathBuffer )
  {
    do //===========================》该循环把路径中的'/'转换成'\'
    {
      if ( *pathBuffer == '/' )
        *pathBuffer = '\\';
      ++pathBuffer;
    }
    while ( *pathBuffer );
  }
  if ( !sub_71C4A2CA() && !ConPathMacros(pathBuffer) )
//=================》ConPathMacros中存在缓冲区溢出
    return 0x7Bu;
  pathLen = 2 * wcslen(&pathBuffer) + 2;
  if ( pathLen > BufferSize )
  {
    if ( RetSize )
      *RetSize = pathLen;
    result = 0x84Bu;
  }
  else
  {
    wcscpy(Buffer, &pathBuffer);
    result = 0;
  }
  return result;
}

(二) 漏洞动态调试

在基本的静态分析后,接下来利用动态调试来看一下函数CanonicalizePathName进行了哪些操作导致漏洞发生。

1. 调试前准备

1.1 附着到svchost.exe进程

根据资料,调用漏洞服务Server的进程为svchost.exe,其中命令行为【C:\Windows\System32\svchost.exe-k netsvcs】的进程是目标所在。

通过wmic查看命令行参数为svchost.exe -k netsvcs的进程pid为924。

查看svchost.exe -k netsvcs的进程pid

打开OllyDbg,点击file->attach,可以发现有很多svchost.exe进程,这里通过PID区分,前面知道svchost.exe –k netsvcs的pid为924(十进制),而这里的PID是16进制,924(Dex)= 39c(Hex),所以这里附着到PID为00000039C的svchost.exe进程上。

附着到进程

1.2 定位函数地址并下断点

点击View->Executable modules查看所有可执行模块(Alt+E),双击选中netapi32。

选中netapi32模块

在cpu指令窗口右键选Search for查找exec(label) in current module当前模块名称(Ctrl+N),找到函数NetpwPathCanonicalize,地址为0x71C44A3E,并在此地址按下F2下断点。

NetpwPathCanonicalize函数的地址

2. 追踪漏洞触发过程

1.1 NetpwPathCanonicalize中断

回到OD的CPU指令窗口,按F9运行。

在渗透测试主机Metasploit终端加载渗透模块后,输入命令exploit执行

执行exploit

分析环境中的svchost程序中断在NetpwPathCanonicalize函数的入口地址处。

该函数的传入参数如下所示:

NetpwPathCanonicalize传入参数

esp [esp] * 注释 *

00ECF924 02248D34 指向待整理路径

00ECF928 022321D8 指向输出路径buffer

00ECF92C 000003F1 输出buffer的长度

00ECF930 02248FB0 指向prefix,值为 \x5C\x00 ,即unicode ‘\’

00ECF934 02248FB4 指向路径类型,值为 0x1001

00ECF938 00000000 WORD Flags保留,值为0

1.2 CanonicalizePathName中断

结合IDA Pro对函数NetpwPathCanonicalize的流程分析,在地址0x71C44A9E处将调用下一级函数CanonicalizePathName,在该地址处按F2下断点。

CanonicalizePathName 函数的地址

按F9运行到该地址,然后按F7跟入函数CanonicalizePathName。传入参数如下所示:

CanonicalizePathName传入参数

esp [esp] * 注释 *

00ECF8FC 02248FB0 prefix,值为\x5C\x00

00ECF900 02248D34 指向待整理路径

00ECF904 022321D8 指向输出路径的buffer

00ECF908 000003F1 输出buffer的长度

00ECF90C 00000000 WORD Flags保留,值为0

从上面两个函数的传递的参数可以推出,函数CanonicalizePathName将待整理的路径字符串进行规范化,然后保存到预先分配的输出路径缓冲区buffer中。接下来分析该函数具体对待整理路径是如何处理的。

1.3待整理路径结构

在OD选中左下部分内存显示窗口后,右键 Go to -> Expression(Ctrl+G),输入待整理路径的地址0x02248D34,查看待整理路径的结构。

待整理路径信息

路径是一个Unicode字符串,以【\x5C\x00】(Unicode字符 ”\”)开始,到【\x00\x00】结束,中间包含一些随机的大小写字母,较长一段不可显示的字符是经过编码的Shellcode,其中最关键的部分是两个连在一起的【\..\..\】,这是表示父目录的相对路径。整个待整理路径形如:

\********\..\..\***

1.4 整理路径前的预操作

继续跟踪调试,在待整理路径所在内存地址0x02248D34 处开头的4个字节上右键->Breakpoint->Memory->Read设内存访问断点。

下内存访问断点

按F9运行,程序第一次中断在0x71C516B1。这段汇编是把待整理路径的第一个字符和【\x5C】(即Unicode字符”\”)进行比较,所以这里是检查待整理路径的第一个字符的函数。

第1次中断

继续按F9运行,第二次中断在0x77B04E36,这里是调用msvcrt.dll模块的wcslen函数,用于计算路径的长度。

第2次中断

继续按F9运行,第三次中断在0x77BD4017,这里是在msvcrt.dll模块的wcscat函数中。栈中保存的返回地址为0x71C44B14。

第3次中断

分析栈中两个参数,第一个是目的地址,指向一段以【\x5c\x00】开头的内存空间;第二个是源地址, 右键Follow in Dump显示其指向指向上述待整理路径前两字节【\x5c\x00】后的内容。

wcscat函数传入参数

在MSDN(https://msdn.microsoft.com/zh-cn/library/h1x0y282.aspx)查询wcscat函数知,该函数把src指向的宽字符串添加到dest结尾处,覆盖dest结尾处的【\0】并添加【\0】。因此,程序将把待整理路径全部复制到新申请的内存即dest处,地址为0x00F0F4DC,新路径的前缀为【\】,暂且称其为temp。

因为只能有一个内存断点,所以在0x00ECF4DC 前4字节下硬件断点,右键Hardware->access->DWord类型。

下硬件断点

1.5 复制路径到缓冲区

由于这里探究的是路径规范化的操作过程,所以只留意是否对原始待整理路径或temp中的路径字符串进行的操作,无关的跳过。

F9继续运行,第4次中断在0x77BD4010 ,内存里显示这里将src的前两个字符复制到了dest的【\x5C\x00】后面,这是由于这两个字节设了断点的原因。

第4次中断

F9,第5次中断在0x71C44B1C,位于wcscat函数内,内存显示已将src复制到dest。

第5次中断

src复制到dest

1.6 记录并跳过无关中断

以下11次中断均没有对待处理路径字符串做实质性操作,所以属于无关中断,仅做简单记录不做具体研究。

F9,第6次中断在0x71C44B1C,仍然位于wcscat函数内。

第6次中断

F9,第7次中断在0x71C44B36,由0x71C516CD跳转而来。

第7次中断

F9,第8次中断在0x71C44B2A,由0x71C44B36跳转而来。

第8次中断

F9,第9、10次都中断在0x77F47D13,栈中显示其从ntdll.dll返回而来,属于操作系统内核操作,跳过。

第9、10次中断

F9,第11次中断在0x77F4A419,栈中显示其从ntdll.dll返回而来,跳过。

第11次中断

F9,第12次中断在0x77F4A9B1, ntdll.dll调用。

第12次中断

F9,第13次中断在0x77F4A750, ntdll.dll调用。

第13次中断

F9,第14次中断在0x77F4B781,ntdll.dll调用。

第14次中断

F9,第15次中断在0x71C44BA5,netapi32调用。

第15次中断

F9第16次中断在0x71C44BC0,netapi32调用。

第16次中断

F9第17次中断在0X91C4491F,netapi32调用。

第17次中断

1.7 第一次路径规范化

F9,第18次中断在0x77BD4D36处,属于wcscpy函数,此时将调用函数进行第一次规范化,对待整理的路径进行实质性操作。

第17次中断

如图,当前参数src值为0x00EC6E0,指向【\..\***】;参数dest值为0x00ECF4DC,指向temp中的第一个字符【\】。

wcscpy参数值

而此时wcscpy源地址src在edx寄存器中,指向【\..\***】;目的地址dest在ecx寄存器中,指向待整理路径第一个字符【\】,如图

寄存器地址

所以,这次字符串复制操作就是去掉第一个表示父目录的相对路径,即待整理路径temp中的第一个【\】和第一个【\..\】之间的内容成为无用路径被抛弃。操作完成后,temp中的路径字符形如【\..\***】。

可以推出,由于还存在一个【\..\】父目录路径,所以整理之后的路径字符串还需要一次规范化操作,以去掉第二个表示父目录的相对路径。

1.8 第二次路径规范化

知道了每次路径规范化都会调用wcscpy函数,接下来删除0x00ECF4DC的硬件断点,直接在wcscpy函数的入口地址0x77BD4D28处下断点。

wcscpy函数入口断点

F9运行后中断在wcscpy函数入口0x77BD4D28处,调用wcscpy函数传入的参数如图:

wcscpy函数传入参数

esp [esp] * 注释 *

00ECF4AC 00ECF494 目的地址,指向的内存区域值为\x5c\x00,即【\】

00ECF4B0 00ECF4E2 源地址,指向第二个相对路径【\..\】的最后一个斜杠,即【\***】

正常情况下,这次规范化处理会和第一次执行同样的操作,去除第二个相对路径【\..\】,从而完成第二次的路径规范化。但这里出现了一个意外的情况,temp的首地址是0x00ECF4DC,而此次字符串复制操作的目的地址dest却在0x00ECF494,在temp之前,如图。

第二次路径规范化时调用wcscpy函数

同时注意到,栈指针ESP值为0x00ECF4A8,该地址指向wcscpy函数的返回地址0x71C52FD4。ESP到复制目的dest地址0x00ECF494只有0x14字节,于是,函数wcscpy如果继续执行,将用源字符串src覆盖wcscpy函数的返回地址。

执行到retn命令,可以看到返回地址变成了0x0100129E,,该地址的指令为:

00100129E FFD6 call esi

执行 call esi(ES=0x00F0F4DE)指令,正好将EIP指向复制尽量的字符串中构造好的第8字节空指令,接着是【\xeb\x62】(jmp 0x62),此jmp指令跳过中间的随机字符串,指向经过编码的Shellcode,如图。

返回地址被覆盖

所以这里是由于内存0x00F0F494处的一个【\】(0x5C),使得出现在处理父母了相对路径【\..\】时往前溢出了待处理路径,从而将字符串覆盖到函数wcscpy返回地址的位置,跳转到shellcode造成远程代码执行。

(三) 漏洞原理解析

经过前面的动态调试分析,在这里详细梳理漏洞触发的过程及原理。

1. 路径处理流程

  • 检查待整理路径的第一个字符
  • 调用msvcrt.dll模块的wcslen函数计算路径长度
  • 调用msvcrt.dll模块的wcscat函数把待整理路径src全部复制到新申请的内存buffer中
  • 调用wcscpy函数,去掉待整理路径中第一个表示父目录的相对路径复制到temp,如:

\******\..\..\*** ====> \..\***

  • 循环调用wcscpy,直到路径整理完毕

2. 路径复制

在这里知道了,在规范化复制时要寻找表示父目录的【\..\】字符串及其前面的一个【\】字符串,将这一段去掉并将新路径复制。

如如图,第一次检查时去掉了第一个相对路径并复制到缓冲区

路径字符串复制过程

但是,当【\..\】字符串在路径字符串的最前面时,那么其前面的一个【\】就在缓冲区外面了,就是在这里产生了向前(低地址)的溢出。

高址向低址溢出

3. 缓冲区溢出

返回查看之前的代码发现,正常情况下,在每次向缓冲区中复制字符串时,无论是用 wcsccpy 还是 wcscat,在复制前总要比较源字符串的长度,保证长度小于207(Hex,即520(Dex)),否则不会继续复制,这一策略确保缓冲区不会向高地址溢出,即当前函数返回时不会发生问题。

而在这个漏洞中,在规范化表示路径,寻找父目录的【\..\】字符串前面的【\】字符时,程序做了判断和边界检查:如果当前比较字符的地址与源字符串地址相同,就表明整个字符串已经查找完毕,程序就会停止查找。

代码语言:js
复制
int __stdcall CanonPathName(LPWSTR PathPrefix, LPWSTR PathName, LPWSTR Buffer, DWORD BufferSize, LPDWORD RetSize)
{
  size_t preLen;
  size_t pathLen;
  wchar_t pathBuffer[MAX_PATH*2 + 1];
  if ( PathPrefix && *PathPrefix )
  {
    preLen = wcslen(PathPrefix);
    if ( preLen != 0)
    {
      if ( preLen > 520 )   //路径字符串长度需小于520(Dex)
        return 0x7Bu;    // ERROR_INVALID_NAME
      wcscpy(pathBuffer, PathPrefix);
      if ( pathBuffer[preLen-1] != '\\' && pathBuffer[preLen-1] != '/') //=============================》判断前缀是否以'\'或'/'结尾
          wcscat(pathBuffer, L"\\");
          ++preLen;
      }
      if ( PathName[0] == '\\' || PathName[0] == '/' )
        ++pathLen;
    }
  }
  else
  {
    pathBuffer[0] = 0;
  }

然而它唯独漏了一种,就是当父目录相对路径【\..\】字符串在源字符串的开头时,在开始查找时比较的字符串(【\】到【\..\】)位于缓冲区之外的情况,这导致了复制的字符串向低地址的溢出,造成函数wcscpy的返回地址被覆盖,从而跳向攻击者精心设计的shellcode,这就是造成该漏洞的原因。

缓冲区溢出流程

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-12-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 恒星EDU 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
网站渗透测试
网站渗透测试(Website Penetration Test,WPT)是完全模拟黑客可能使用的攻击技术和漏洞发现技术,对目标系统的安全做深入的探测,发现系统最脆弱的环节。渗透测试和黑客入侵最大区别在于渗透测试是经过客户授权,采用可控制、非破坏性质的方法和手段发现目标和网络设备中存在弱点,帮助管理者知道自己网络所面临的问题,同时提供安全加固意见帮助客户提升系统的安全性。腾讯云网站渗透测试由腾讯安全实验室安全专家进行,我们提供黑盒、白盒、灰盒多种测试方案,更全面更深入的发现客户的潜在风险。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档