0x01 源码
./routes/index.js
var blacklist=['127.0.0.1.xip.io','::ffff:127.0.0.1','127.0.0.1','0','localhost','0.0.0.0','[::1]','::1']; // 黑名单主机地址,过滤掉本地回环地址
// get / 请求,返回一个空'{}''
router.get('/', function(req, res, next) {
res.json({});
});
// get /debug 请求
router.get('/debug', function(req, res, next) {
console.log(req.ip);
if(blacklist.indexOf(req.ip)!=-1){ // 如果解析出来的ip地址为本地地址
console.log('res');
var u=req.query.url.replace(/[\"\']/ig,''); // 将get请求中的url项内的'、"替换为空
console.log(url.parse(u).href);
let log=`echo '${url.parse(u).href}'>>/tmp/log`; //拼接出来log字符串
console.log(log); //输出log内容到控制台
child_process.exec(log); //执行拼接出来的log内容 !!!命令注入漏洞点!!!
res.json({data:fs.readFileSync('/tmp/log').toString()}); // 输出/tmp/log文件里的内容到网页
}else{
res.json({});
}
});
// post /debug 请求
router.post('/debug', function(req, res, next) {
console.log(req.body);
if(req.body.url !== undefined) {
var u = req.body.url;
var urlObject=url.parse(u); // 获取http头的url请求头
if(blacklist.indexOf(urlObject.hostname) == -1){ //如果url请求头解析出来的ip地址不在黑名单内,执行以下操作
var dest=urlObject.href;
request(dest,(err,result,body)=>{ //再本地调用get请求
res.json(body);
})
}
else{
res.json([]);
}
}
});
module.exports = router;
./bin/www
#!/usr/bin/env node
var app = require('../app');
var debug = require('debug')('hipsterdevstudios:server');
var http = require('http');
var port = normalizePort(process.env.PORT || '3000'); //端口为默认或3000
app.set('port', port);
var server = http.createServer(app);
0x02 环境
可以在本地启动这个环境以便于测试。在源码目录下:
###在/目录建一个flag文件
vim flag
chmod 777 flag
cd web_babyJS
npm start
如图:
使用burpsuite抓本地回环包的方法:
使用浏览器访问http://ip(本机ip),不要访问127.0.0.1,这样burp才能正常抓包及发包。
0x03 分析
基本可参考上面的源码解析,考察的是命令注入。通过构造特定的log
字符串,在执行child_process.exec(log)
时,完成命令注入操作。可以构造形如cp /flag /tmp/log
的注入命令,借助后面的输出文件readFileSync('/tmp/log')
操作,即可获取flag。
但是怎么能执行到那个位置?get请求的实现中,能到达漏洞点的位置需要url的ip项为本地地址,显然外网访问不可行。但是在post请求的实现里,会进行二次请求,但需要过黑名单,即url里的ip不能是本机ip。绕过的思路是在post中带url参数做get请求,构造过程如下:
1、绕过黑白名单。对
post /debug
请求,可以将ip设置为127.0.0.2
或127.1
以绕过黑名单检查,构造post内容为:url=http://127.0.0.2:3000/debug?url=
,可以绕过post的ip检查,因为这ip表示的还是本机,所以通过本地回环提交给get方法,但是ip地址已经被换为了::ffff:127.0.0.1
-
2、在
get /debug
请求的实现里,会过滤符号'
、"
。在url.js源代码里发现,执行函数url.parse(u).href
时,对URL中表示用户名和密码的字段会被二次解码,即http://pass@user
中pass和user会被二次解码。所以可以将'
符号编码后藏在pass字段,即http://%2527@a
,即可绕过url检查。但在使用burpsuite的body Parameters构造数据包时,服务器接收到数据包后会有一次解码,所以需要将%再一次编码为:http://%252527@a
,如下图:
3、注入指令的构造。需要注入的指令为
cp /flag /tmp/log
。因为无法再在${url.parse(u).href}
中插入一个'
,可以使用linux shell的注释符号"#"来实现单引号的闭合(或使用%00闭合)。空格字符可以用$IFS
替代。那么构造payload为:
http://127.0.0.2:3000/debug?url=http://%252527@a/;cp$IFS/flag$IFS/tmp/log;%2523
0x04 实现
也可以使用另一种数据包构造,效果一样。
写的很菜,请忍一下