天天看點

【網絡安全系列】JavaScript原型鍊污染攻擊總結0 前言1 原理2 利用

文章目錄

  • 0 前言
  • 1 原理
  • 2 利用

0 前言

【網絡安全系列】JavaScript原型鍊污染攻擊總結0 前言1 原理2 利用

原型鍊污染,是 NodeJs 中常見的漏洞,在做 antCTF 時也遇到的原型鍊污染題目,在此記錄自己學習原型鍊污染的過程。

1 原理

0x01 問:原型鍊有什麼作用?

用來做繼承,也就是基于原有的代碼做一定的修改。下面是一個使用原型鍊實作繼承的案例:

function Parent () {
    this.name = 'kevin';
}
Parent.prototype.getName = function () {
    console.log(this.name);
}

function Child () {

}
Child.prototype = new Parent();

var child1 = new Child();
console.log(child1.getName()) // kevin
           

當「方法」的 prototype 指定對象原型之後,當試圖通路該類的對象屬性時,它不僅僅在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依次層層向上搜尋,直到找到一個名字比對的屬性或到達原型鍊的末尾。下面是一個修改 prototype 的案例:

注意:prototype 是「方法特有的」(需要大概了解,後面需要使用到)
  • 方法:類似 C++ 中的類。除了有屬性

    __proto__

    , 還有屬性 prototype,prototype 指向該方法的原型對象。propotype 指定其他對象之後,會包含所有原型對象的屬性和方法
  • 對象:類似 C++ 中的對象。對象隻有屬性

    __proto__

    指向該對象的構造函數的原型對象。對象有

    constructor

    裡面包含該類的 prototype。
// 讓我們從一個函數裡建立一個對象o,它自身擁有屬性a和b的:
let f = function () {
   this.a = 1;
   this.b = 2;
}
/* 這麼寫也一樣
function f() {
  this.a = 1;
  this.b = 2;
}
*/
let o = new f(); // {a: 1, b: 2}

// 在f函數的原型上定義屬性
f.prototype.b = 3;
f.prototype.c = 4;

// 不要在 f 函數的原型上直接定義 f.prototype = {b:3,c:4};這樣會直接打破原型鍊
// o.[[Prototype]] 有屬性 b 和 c
//  (其實就是 o.__proto__ 或者 o.constructor.prototype)
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最後o.[[Prototype]].[[Prototype]].[[Prototype]]是null
// 這就是原型鍊的末尾,即 null,
// 根據定義,null 就是沒有 [[Prototype]]。

// 綜上,整個原型鍊如下:

// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null

console.log(o.a); // 1
// a是o的自身屬性嗎?是的,該屬性的值為 1

console.log(o.b); // 2
// b是o的自身屬性嗎?是的,該屬性的值為 2
// 原型上也有一個'b'屬性,但是它不會被通路到。
// 這種情況被稱為"屬性遮蔽 (property shadowing)"

console.log(o.c); // 4
// c是o的自身屬性嗎?不是,那看看它的原型上有沒有
// c是o.[[Prototype]]的屬性嗎?是的,該屬性的值為 4

console.log(o.d); // undefined
// d 是 o 的自身屬性嗎?不是,那看看它的原型上有沒有
// d 是 o.[[Prototype]] 的屬性嗎?不是,那看看它的原型上有沒有
// o.[[Prototype]].[[Prototype]] 為 null,停止搜尋
// 找不到 d 屬性,傳回 undefined
           

0x02 問:

__proto__

屬性有什麼作用?

每個「對象」都有

__proto__

屬性,指向了建立該對象的構造函數的原型。其實這個屬性指向了 [[prototype]],但是 [[prototype]] 是内部屬性,我們并不能通路到,是以使用

__proto__

來通路。

簡而言之:用 prototype 無法直接通路,需要使用

__proto__

通路。prototype 是一個指針屬性。

這裡有個需要區分的概念:

  • __proto__

    :指向原型對象的構造器。
  • constructor

    :指向目前對象的構造器。
【網絡安全系列】JavaScript原型鍊污染攻擊總結0 前言1 原理2 利用

(圖檔說明:右下角是圖檔的說明,左圖的

__proto__

的箭頭指向原型對象的構造器)

【網絡安全系列】JavaScript原型鍊污染攻擊總結0 前言1 原理2 利用

(圖檔說明:右下角是圖檔的說明,左圖的

constructor

的箭頭指向原型對象的構造器)

0x03 問:原型鍊污染的概念是什麼?

在一個應用中,如果攻擊者控制并修改了一個對象的原型,那麼将可以影響所有和這個對象來自同一個類、父祖類的對象。這種攻擊方式就是原型鍊污染。

基本原理:引用類型的屬性被所有執行個體共享。案例:

function Parent () {
    this.names = ['kevin', 'daisy'];
}
function Child () {

}
Child.prototype = new Parent();

var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy", "yayu"]
           

按照 Java 中正常的繼承,child2.names 應該和原對象一樣,數組中隻有 2 個資料。

2 利用

問:怎麼判斷是否有原型鍊污染?

  • 字元串可以被解析為方法或對象。例如:Json.parse 進行解析、shvl 庫使用點對屬性操作。
  • 對象的鍵和值都可控。target[key] = value,其中 key 和 value 均可控制。

下面是一個原型鍊污染的簡單案例:

function merge(target, source) {
    console.log('merge', target, source);
    // 周遊 source 中的 key。
    for (let key in source) {
        if (key in source && key in target) {
            merge(target[key], source[key])
        } else {
            target[key] = source[key]
        }
    }
}

let o1 = {}         // {} 是一個對象,存在 __proto__ 的 key。
console.log(typeof(o1));
console.log(o1);    // object
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
console.log(typeof(o2));
console.log(o2);    // object

merge(o1, o2)
console.log('o1 proto:', o1.__proto__);
console.log('o2 proto:', o2.__proto__);
console.log('{} proto:', {}.__proto__);
console.log(o1, o2);    // 對象 {} 的原型對象變為 { a: 1 }。

o3 = {}
console.log(o3)
           

實戰1:在 antCTF 中,使用 shvl 庫對鍵值進行操作。

email: async function (req, res) {
    let contents = {};

    Object.keys(req.body).forEach((key) => {
      shvl.set(contents, key, req.body[key]);
    });
    // 周遊請求參數中所有的 key
    // 将鍵和值指派為 contents(shvl 庫的 set 函數)

    contents.from = '"admin" <[email protected]>';

    try {
      await send(contents);
      return res.json({
        message: "Success."
      });
    } catch (err) {
      return res.status(500).json({
        message: err.message
      });
    }
  }
           

問:常見的原型鍊污染方法有哪些?

  • function.__proto__.polluted

  • function.prototype.polluted

  • obj.__proto__.pollluted

    。例如:shvl 隻禁用了

    __proto__

    ,傳送門Security Fix for Prototype Pollution - huntr.dev
  • obj.constructor.polluted

案例:

function person(fullName) {
    this.fullName = fullName;
}
var person1 = new person("Satoshi");
// function:prototype, __prototype
person.prototype.sayHello = 1
console.log(person1.__proto__);
person.prototype.newConstant = 2
console.log(person1.__proto__); 

// object: __prototype__, constructor
person1.__proto__.sayHi= 3
console.log(person1.__proto__);
person1.constructor.prototype.oldConstant = 4
console.log(person1.__proto__);
/*
person { sayHello: 1 }
person { sayHello: 1, newConstant: 2 }
person { sayHello: 1, newConstant: 2, sayHi: 3 }
person { sayHello: 1, newConstant: 2, sayHi: 3, oldConstant: 4 }
*/
 
           
推薦一個非常 nice 的網站:https://book.hacktricks.xyz/ 。收集 hack 中的 tricks

問:原型鍊污染鍊怎麼挖掘?

  1. 尋找 JavaScript 中的危險關鍵字(危險函數)。如:
    1. 子產品:child_process
    2. 函數:eval, spwn, exec, setTimeout, setInteval, Function
  2. 尋找調用關系和可控的參數,并且确定如何進行傳參。
    【網絡安全系列】JavaScript原型鍊污染攻擊總結0 前言1 原理2 利用
    (圖檔說明:先确認危險函數和調用方式,也就是頭部和尾部,再去尋找中間過程)
  3. 充分利用危險函數和能控制的參數。(讀取檔案或者反彈 shell)
  • 目标機器環境如果有 bash,可以反彈 shell。
  • 目标機器環境如果有 Python 等,可以反彈 shell。
  • 目标機器環境如果隻有 sh,将 readflag 執行寫入到其他地方,再利用其他方式讀取。

(拓展閱讀:反彈Shell表)

問:原型鍊污染怎麼防禦?怎麼繞過防禦呢?

  • 如果系統中有鍵值的操作,并且鍵和值來自外部輸入。可以考慮進行過濾:
    • 禁止操作

      constructor

    • 禁止操作

      prototype

    • 禁止操作

      __proto__

繞過防禦:

  • 思考并測試是否過濾完全,具體參考 antCTF 的 8-bit-pub 中的 shvl 庫繞過。

參考教程:

  1. JavaScript深入之繼承的多種方式和優缺點-Github
  2. JS中的prototype、__proto__與constructor-Huawei
  3. 繼承與原型鍊 - JavaScript | MDN
  4. 深入了解 JavaScript Prototype 污染攻擊-phithon
  5. NodeJS - proto & prototype Pollution-HackTricks
  6. AntCTF2021部分WP-HapHp1

繼續閱讀