前言
依旧是爆了零的web呢
官方wp:https://github.com/ChaMd5Team/Venom-WP/tree/main/2024VenomCTF
Web
Archived elephant(Unsolved)
java代码审计 +
Ueditor
存在JSON
注入隐患 +fastjson<1.2.68 common-io
任意文件写 +beetl
模板白名单绕过
题目给的Dockerfile里中科大源寄了,自己换了个源(虽然后面附件更新了)
可用源的地址:https://launchpad.net/ubuntu/+archivemirrors0
FROM tomcat:8-jdk8
COPY files /tmp/files/
RUN mv /tmp/files/flag.sh / && \
mv /tmp/files/start.sh / && \
mv /tmp/files/db.sql / && \
chmod +x /flag.sh /start.sh && \
sed -i 's@//.*archive.ubuntu.com@//mirrors.tuna.tsinghua.edu.cn@g' /etc/apt/sources.list && \
sed -i 's@//.*deb.debian.org@//mirrors.tuna.tsinghua.edu.cn@g' /etc/apt/sources.list && \
apt-get update -y && \
apt-get install libaio1 libnuma1 psmisc libmecab2 libatomic1 libncurses6 libsasl2-2 perl -y && \
DEBIAN_FRONTEND=noninteractive dpkg -i /tmp/files/mysql-common_5.7.29-1debian10_amd64.deb && \
DEBIAN_FRONTEND=noninteractive dpkg -i /tmp/files/mysql-community-client_5.7.29-1debian10_amd64.deb && \
DEBIAN_FRONTEND=noninteractive dpkg -i /tmp/files/mysql-client_5.7.29-1debian10_amd64.deb && \
DEBIAN_FRONTEND=noninteractive dpkg -i /tmp/files/mysql-community-server_5.7.29-1debian10_amd64.deb && \
DEBIAN_FRONTEND=noninteractive dpkg -i /tmp/files/mysql-server_5.7.29-1debian10_amd64.deb && \
rm -rf /tmp/files && \
rm -rf /var/lib/apt/lists/*
COPY src/elephant.war /usr/local/tomcat/webapps/ROOT.war
CMD /start.sh
db.sql文件给了账密:admin:admin
直接登录即可
来到/upload
看一下对应的代码
@RequestMapping({"/upload"})
public String upload(HttpServletRequest request, Model model, HttpSession session, @RequestParam(value = "action",required = false) String action) throws URISyntaxException {
String user = (String)session.getAttribute("user");
if (!"admin".equals(user)) {
model.addAttribute("message", "no way");
return "upload";
} else if (action == null) {
model.addAttribute("message", "传个文件吧");
return "upload";
} else {
String json = (new ActionEnter(request, UploadController.class.getResource("/").toURI().getPath() + this.rootPath)).exec();
String contextPath = request.getContextPath();
String handlerOut = this.uploadService.uploadHandle(action, json, contextPath);
model.addAttribute("message", handlerOut);
return "upload";
}
}
白名单后缀名限制
public static int getFileType(String ext) {
if (!"doc".equals(ext) && !"docx".equals(ext) && !"xls".equals(ext) && !"xlsx".equals(ext) && !"ppt".equals(ext) && !"pptx".equals(ext) && !"pdf".equals(ext) && !"xml".equals(ext) && !"json".equals(ext) && !"txt".equals(ext) && !"log".equals(ext) && !"md".equals(ext)) {
if (!"bmp".equals(ext) && !"jpg".equals(ext) && !"jpeg".equals(ext) && !"gif".equals(ext) && !"png".equals(ext)) {
if (!"mp3".equals(ext) && !"wav".equals(ext) && !"mid".equals(ext) && !"aif".equals(ext)) {
if (!"flv".equals(ext) && !"swf".equals(ext) && !"mkv".equals(ext) && !"avi".equals(ext) && !"rm".equals(ext) && !"rmvb".equals(ext) && !"mpeg".equals(ext) && !"mpg".equals(ext) && !"ogg".equals(ext) && !"ogv".equals(ext) && !"mov".equals(ext) && !"wmv".equals(ext) && !"mp4".equals(ext) && !"webm".equals(ext)) {
return !"rar".equals(ext) && !"zip".equals(ext) && !"tar".equals(ext) && !"gz".equals(ext) && !"7z".equals(ext) && !"bz2".equals(ext) && !"cab".equals(ext) && !"iso".equals(ext) ? 9 : 8;
} else {
return 4;
}
} else {
return 3;
}
} else {
return 2;
}
} else {
return 1;
}
}
hackjs(复现)
const express = require('express')
const fs = require('fs')
var bodyParser = require('body-parser');
const app = express()
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.post('/plz', (req, res) => {
venom = req.body.venom
if (Object.keys(venom).length < 3 && venom.welcome == 159753) {
try {
if(venom.hasOwnProperty("text")){
res.send(venom.text)
}else{
res.send("no text detected")
}
} catch {
if (venom.text=="flag") {
let flag=fs.readFileSync("/flag");
res.send("Congratulations:"+flag);
} else {
res.end("Nothing here!")
}
}
} else {
res.end("happy game");
}
})
app.get('/',
function(req, res, next) {
res.send('<title>oldjs</title><a>Hack me plz</a><br><form action="/plz" method="POST">text:<input type="text" name="venom[text]" value="ezjs"><input type="submit" value="Hack"></form> ');
});
app.listen(80, () => {
console.log(`listening at port 80`)
})
首先是要进入catch语句,就要让venom.hasOwnProperty("text")
报错
方法也很简单,参考:https://www.cnblogs.com/wengxuesong/p/5613189.html
我们使用dict对象的hasOwnProperty方法,但其实它自身并没有这个方法,而是继承自Object.prototype对象。如果dict字典对象有一个同为”hasOwnProperty”名称的属性,那么原型中的hasOwnProperty方法不会被访问到。这里会优先读取自身包含的属性,找不到才会从原型链中查找
所以只需要传入venom[welcome]=159753&venom[hasOwnProperty]=
即可进入catch
测试的时候发现传venom[__proto__]
进去没被检测成键
然后我不会了。。做的时候一直想用hasOwnProperty
键构造原型链污染text
其实依旧是传三个参数进去,但是hasOwnProperty
这里可以前面可以带个__proto__
,用来绕过键长检测
payload:
venom[__proto__][hasOwnProperty]=&venom[text]=flag&venom[welcome]=159753
Misc
checkin
f12在控制台发现hint:Quick pass cheat: I heard that Venom is ChaMd5’s, here is a mysterious string for you. 88d18c420654d158d22b65626bc7a878
一眼md5,somd5爆破一下得到flag:flag{We1c0m3_VCTF_2024}