Prototype Pollution
オブジェクトのプロパティアクセスに影響を与えることができる
プロトタイプとは?
JavaScriptにおける継承を実現する機構のこと
オブジェクト指向言語で言えば親クラスのようなもの
詳しくはMDNを読んでください
重要なこと
オブジェクトのプロトタイプは、 obj.__proto__
で取得できる
標準で非推奨の機能なので、将来削除される可能性がある
JavaScriptのプロパティアクセスは、プロトタイプに対して該当するプロパティがあるかどうか再帰的に調べて、あればその値を、無ければ undefined
を返すようになっている
A -> B -> C
というプロトタイプチェーンがあるときに A.foo
にアクセスした場合
A
に foo
というプロパティがあれば、それを返す
A
に foo
というプロパティが無ければ、 A
のプロトタイプの B
に foo
というプロパティがあるか調べる
B
に foo
があればそれを、無ければ B
のプロトタイプの C
を調べ...
基本的にオブジェクトはすべて Object.prototype
を継承している
Number
も String
も Array
も Function
も Object.prototype
をプロトタイプチェーンに持つ
JavaScriptのプロパティアクセスはドットでも添字でもアクセスできる
a["b"]["c"]
と a.b.c
は等価
a["__proto__"]
でプロトタイプにアクセスできる
Prototype Pollutionとは?
プロトタイプのプロパティに対して悪意のある書き込みを行い、そのオブジェクトをプロトタイプに持つ全てのオブジェクトのプロパティアクセスに対して影響を与える攻撃
obj[A][B] = C
のような処理があり、攻撃者がA,B,Cを操作できるとき典型的なPrototype Pollutionの脆弱性が生まれる
poc.jsconst obj = {}; // プロトタイプチェーンは obj => Object.prototype => null
// 攻撃者がObject.prototype.pollutedに書き込みを行う
obj["__proto__"]["polluted"] = "pwned";
// 別のオブジェクトを作成する
const obj2 = {}; // プロトタイプチェーンは obj2 => Object.prototype => null
// obj["__proto__"] === obj2["__proto__"] なので obj2.__proto__.polluted も"pwned"になる
console.log(obj2.polluted); // => pwned
上書きできるのは元のオブジェクトで定義されていないプロパティだけ
poc.jsconst obj = {hoge: "original"}; // {hoge: "original"} => Object.prototype => null
obj["__proto__"]["hoge"] = "pwned";
// objにhogeプロパティが存在するため、プロトタイプにさかのぼらずにobj.hogeを出力する
console.log(obj.hoge) // => "original";
定義されてないプロパティを上書きするだけで脆弱性になるの?
危険なオプションを有効にしたり、外部からの書き込みが想定されていない部分に書き込んだりすることで攻撃につなげたりできる
対策
Object.prototype.hasOwnProperty
を用いて上書きするプロパティが自分で定義したものか確認する
obj = {a: 1}
のとき
Object.prototype.hasOwnProperty.call(obj, "a") === true
Object.prototype.hasOwnProperty.call(obj, "toString") === false
(objで定義したものではないので)
これを使って obj[A] = B
の前にAをチェックする
{}
ではなく Object.create(null)
を利用する
Object.create(null)
とするとプロトタイプを持たない(プロトタイプがnullの)オブジェクトを作成できる
プロトタイプを持たないので、 Object.prototype
などを汚染できない
オブジェクトの代わりに new Map()
を使えないか考える
攻撃テクニック
obj.__proto__
以外にも obj.constructor.prototype
でプロトタイプにアクセスできる
__proto__
を弾くだけでは不十分
Gadgetを探す
一般に適用できるテクニックはないので、問題ごとにGadgetを探す必要があることが多い
既存のCVEや既知のgadgetを使うときもある
ソースコードやライブラリを調べてPrototype Pollutionで悪用できそうな未定義のプロパティを探す
if (A.B) { ~ }
や、 if (A[B]) { ~ } // Bは操作可能
が狙い目
過去の例
Asian Cyber Security Challange 2021 - Cowsay as a Service
child_process.spawnSync
の options.shell
を汚染して任意コード実行
CakeCTF 2022 - Panda Memo
テンプレートエンジン mustache
のキャッシュの汚染
MapleCTF 2022 - Viene Library
node-fetch
の X-HTTP-Method-Override
を汚染してMethodを上書き
参加者ごとにインスタンスが提供されたり、毎回インスタンスが破棄される問題はPrototype Pollution (?)
Prototype Pollutionは性質上環境を永続的に汚染するので、複数の参加者がひとつのサーバーを共有する問題を壊れないように作るのは難しい
そのためPrototype Pollutionの問題はインスタンスは破棄されたり分けられたりすることが多いので、その形式ならPrototype Pollutionだろうというジンクス
もう古いかも
資料
クライアントサイドで発見されたPrototype Pollutionと悪用できる汚染先
CTFのWebセキュリティにおけるJavaScript,nodejsまとめ(Prototype pollution, 難読化)
Prototype pollutionの攻撃手法に関する発表のスライド