文中涉及操作均來自靶場,切勿用于非授權滲透測試!
一、JavaScript原型鏈基礎
要理解原型污染漏洞,首先需要掌握JavaScript中原型(prototype)和原型鏈(prototype chain)的基本概念。
1.1 什么是原型
JavaScript是一種基于原型的語言,每個對象都有一個原型對象,對象以其原型為模板,從原型繼承方法和屬性。原型對象也可能擁有原型,并從中繼承方法和屬性,這種關系被稱為原型鏈。
function Person(name, age) {this.name = name;this.age = age;
}let p1 = new Person('Alice', 25);
console.log(p1);
在這個例子中,p1
是Person
構造函數的一個實例,它擁有name
和age
屬性,同時還有一個__proto__
屬性指向Person.prototype
。
原型鏈關系驗證
console.log(p1.__proto__ === Person.prototype); // true ?
console.log(p1.__proto__.__proto__ === Object.prototype); // true ?
console.log(p1.__proto__.__proto__.__proto__); // null ?
原型鏈圖示
關鍵點說明
第一層:p1.__proto__
指向 Person.prototype
,這是構造函數為所有實例提供的共享原型對象
第二層:Person.prototype
本身也是一個對象,它的 __proto__
自然指向 JavaScript 的根原型 Object.prototype
終點:所有原型鏈最終都會通過 Object.prototype.__proto__
指向 null
,這是原型鏈的終點
特別說明
在ES6中更推薦使用 Object.getPrototypeOf()
而不是直接訪問 __proto__
:
console.log(Object.getPrototypeOf(p1) === Person.prototype); // true
構造函數本身的 __proto__
指向 Function.prototype
(這是另一個獨立的原型鏈):
console.log(Person.__proto__ === Function.prototype); // true
1.2 所有函數都是 Function
的實例
Person
是構造函數
當你定義 function Person() {}
,Person
就是一個用于創建對象的構造函數。
但它同時也是一個函數對象,因此它繼承自 Function.prototype
:
console.log(Person.__proto__ === Function.prototype); // true
Function
自身也是構造函數
Function
是 JavaScript 內置的構造函數,用于創建所有函數(包括它自己!)。
有趣的是,Function
的 __proto__
也指向 Function.prototype
:
console.log(Function.__proto__ === Function.prototype); // true
這是 JavaScript 中唯一一個 __proto__
指向自身 prototype
的構造函數。
1.3 Object
的構造函數角色
Object
是頂級構造函數
Object
是所有對象的基類(包括函數對象)。
普通對象(如 {}
或 new Person()
)的原型鏈最終都會指向 Object.prototype
。
Object
與 Function
的關系
Object
本身是一個函數(構造函數),因此它也繼承自 Function.prototype
:
console.log(Object.__proto__ === Function.prototype); // true
而 Function.prototype
本身是一個對象,它的 __proto__
又指向 Object.prototype
:
console.log(Function.prototype.__proto__ === Object.prototype);
1.4 完整的原型鏈關系圖
function Person() {}
const p1 = new Person();// 1. 實例的原型鏈
console.log(p1.__proto__ === Person.prototype); // true
console.log(p1.__proto__.__proto__ === Object.prototype); // true
console.log(p1.__proto__.__proto__.__proto__ === null); // true// 2. 構造函數的原型鏈
console.log(Person.__proto__ === Function.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true (特殊!)
console.log(Object.__proto__ === Function.prototype); // true// 3. 原型對象的原型鏈
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
終極關系總結(關鍵記憶點)
對象 __proto__ 指向 說明
p1 (實例) Person.prototype 實例指向構造函數的原型
Person (函數) Function.prototype 所有函數繼承自 Function
Person.prototype Object.prototype 默認構造函數的原型繼承自 Object
Function Function.prototype 唯一自引用的構造函數
Object Function.prototype Object 本身是函數
Function.prototype Object.prototype 函數原型的父級是對象原型
Object.prototype null 原型鏈的終點
二、原型污染漏洞原理
2.1 什么是原型污染
原型污染是指攻擊者通過某種手段修改JavaScript對象的原型(通常是Object.prototype
),從而影響所有基于該原型的對象的行為。
在Lodash的案例中,某些函數(如defaultsDeep
)未能正確處理特殊屬性(如constructor
和__proto__
),導致攻擊者可以通過精心構造的輸入修改Object.prototype
。
2.2 Lodash漏洞示例
Lodash的defaultsDeep
函數用于遞歸合并對象的屬性。在4.17.12之前的版本中,它存在原型污染漏洞:
const payload = '{"constructor": {"prototype": {"vuln": true}}}';_.defaultsDeep({}, JSON.parse(payload));if({}.vuln === true) {alert('lodash 存在原型污染漏洞!');}else{console.log('no vuln!');}
2.3 解析_.defaultsDeep 為何會導致漏洞
當執行_.defaultsDeep({}, JSON.parse(payload));
,遞歸地將 payload
的屬性合并到空對象 {}
中。Lodash 未對特殊屬性名(constructor
和 prototype
)做安全過濾。
執行流程:
1. 創建惡意對象
const payload = '{"constructor": {"prototype": {"vuln": true}}}';
2. _.defaultsDeep({}, JSON.parse(payload))的過程
_.defaultsDeep(object, sources)
是 Lodash 提供的一個遞歸合并方法,用于將 sources
(源對象)的屬性深度合并到 object
(目標對象)中,但僅當目標對象的對應屬性為 undefined
時才覆蓋。
JSON.parse(payload)
解析 JSON 字符串,生成 JavaScript 對象
源對象 sources:{ constructor: { prototype: { vuln: true } } }
目標對象 object: 空對象 {}
(所有屬性初始為 undefined
)
關鍵點:constructor.prototype
是 JavaScript 中訪問 Object.prototype
的路徑。
- 合并過程
檢查目標對象的 constructor
屬性 → undefined
,直接合并源對象的 constructor
遞歸檢查 constructor.prototype
→ 目標對象無此屬性,繼續合并。
最終將 { vuln: true }
賦值到 Object.prototype
(即 constructor.prototype
指向的原型對象)。
2.4 漏洞危害
原型污染可能導致多種安全問題:
屬性注入:向全局對象注入惡意屬性
拒絕服務(DoS):覆蓋關鍵方法導致應用崩潰
遠程代碼執行(RCE):在某些特定環境下可導致代碼執行
安全機制繞過:可能繞過輸入驗證或其他安全控制
## 感謝您的關注
echo "QmlsaWJpbGkgc2VhcmNoICdQZW5UZXN0M3JfWmVybGsnIGZvciBtb3JlIHZpZGVvLCBUaGFuayB5
b3UgZm9yIHlvdXIgc3VwcG9ydCEK"|base64 -d