目錄:
目錄:
一、概念
二、舉例
三、 實(shí)操了解
總結(jié)
四、拋出原題,歷年原題復(fù)現(xiàn)
第一題:
五、分析與原理
?第二題:
八、分析與原理
九、具體操作,payload與結(jié)果
結(jié)果:?
一、概念
? JavaScript 規(guī)定,所有對(duì)象都有自己的原型對(duì)象(prototype)。一方面,任何一個(gè)對(duì)象,都可以充當(dāng)其他對(duì)象的原型;另一方面,由于原型對(duì)象也是對(duì)象,所以它也有自己的原型。因此,就會(huì)形成一個(gè)“原型鏈”(prototype chain):對(duì)象到原型,再到原型的原型……
cat.color--->Animal.prototype--->aaa.prototype--->xxx.--->object.protoype--->null
如果一層層地上溯,所有對(duì)象的原型最終都可以上溯到`Object.prototype`,即`Object`構(gòu)造函數(shù)的`prototype`屬性。也就是說(shuō),所有對(duì)象都繼承了`Object.prototype`的屬性。這就是所有對(duì)象都有`valueOf`和`toString`方法的原因,因?yàn)檫@是從`Object.prototype`繼承的。`Object.prototype`的原型是`null`。`null`沒(méi)有任何屬性和方法,也沒(méi)有自己的原型。因此,原型鏈的盡頭就是`null`。
二、舉例
三、 實(shí)操了解
在我們了解到了具體的“子類(lèi)繼承父類(lèi)”的關(guān)鍵點(diǎn)后,我們進(jìn)行實(shí)操看一道題
function P() {
}
var p = new P();
console.info(p.constructor === P)
console.info(p.constructor === P.prototype.constructor)
console.info(p.hasOwnProperty('constructor'))
思路:三小問(wèn)的具體思路:
-
console.info(p.constructor === P);
: 這行代碼比較了對(duì)象p
的構(gòu)造函數(shù)是否等于構(gòu)造函數(shù)P
。由于p
是通過(guò)構(gòu)造函數(shù)P
創(chuàng)建的,所以p.constructor
應(yīng)該指向P
,因此這里的比較結(jié)果為true
。 -
console.info(p.constructor === P.prototype.constructor);
: 這行代碼比較了對(duì)象p
的構(gòu)造函數(shù)是否等于構(gòu)造函數(shù)P.prototype.constructor
。在這里,P.prototype.constructor
默認(rèn)指向P
,因?yàn)?P.prototype
是從構(gòu)造函數(shù)P
繼承而來(lái)的。因此這里的比較結(jié)果也為true
。 -
console.info(p.hasOwnProperty('constructor'));
: 這行代碼檢查對(duì)象p
是否直接擁有自己的屬性'constructor'
。然而,'constructor'
實(shí)際上是從P.prototype
繼承而來(lái)的,因?yàn)?p
是通過(guò)new P()
創(chuàng)建的,它的原型是P.prototype
。因此,這里的比較結(jié)果為false
。 -
總結(jié):代碼展示了對(duì)象
p
與構(gòu)造函數(shù)P
及其原型之間的關(guān)系。對(duì)象p
是通過(guò)構(gòu)造函數(shù)P
創(chuàng)建的,它的原型指向了P.prototype
。這些比較和檢查操作說(shuō)明了原型鏈在 JavaScript 中的工作方式。
總結(jié)
從以上的簡(jiǎn)單拋出和講解我們不難看出原型鏈污染玩的就是兩個(gè)重要的概念1.原型繼承2.屬性查找機(jī)制,讓我們維持著這兩個(gè)理念看看題型,在題型中理解原型鏈污染
-
原型繼承: JavaScript中的對(duì)象可以通過(guò)原型繼承從其他對(duì)象繼承屬性和方法。每個(gè)對(duì)象都有一個(gè)指向其原型的鏈接,通過(guò)這個(gè)鏈接可以訪問(wèn)原型對(duì)象的屬性和方法。
-
屬性查找機(jī)制: 當(dāng)訪問(wèn)一個(gè)對(duì)象的屬性或方法時(shí),JavaScript引擎會(huì)首先在對(duì)象本身查找,如果找不到,它會(huì)沿著原型鏈向上查找,直到找到屬性或方法或者到達(dá)原型鏈的末端。
四、拋出原題,歷年原題復(fù)現(xiàn)
第一題:
const express = require('express')
var hbs = require('hbs');
var bodyParser = require('body-parser');
const md5 = require('md5');
var morganBody = require('morgan-body');
const app = express();
var user = []; //empty for now
var matrix = [];
for (var i = 0; i < 3; i++){
matrix[i] = [null , null, null];
}
function draw(mat) {
var count = 0;
for (var i = 0; i < 3; i++){
for (var j = 0; j < 3; j++){
if (matrix[i][j] !== null){
count += 1;
}
}
}
return count === 9;
}
app.use(express.static('public'));
app.use(bodyParser.json());
app.set('view engine', 'html');
morganBody(app);
app.engine('html', require('hbs').__express);
app.get('/', (req, res) => {
for (var i = 0; i < 3; i++){
matrix[i] = [null , null, null];
}
res.render('index');
})
app.get('/admin', (req, res) => {
/*this is under development I guess ??*/
console.log(user.admintoken);
if(user.admintoken && req.query.querytoken && md5(user.admintoken) === req.query.querytoken){
res.send('Hey admin your flag is <b>flag{prototype_pollution_is_very_dangerous}</b>');
}
else {
res.status(403).send('Forbidden');
}
}
)
app.post('/api', (req, res) => {
var client = req.body;
var winner = null;
if (client.row > 3 || client.col > 3){
client.row %= 3;
client.col %= 3;
}
matrix[client.row][client.col] = client.data;
for(var i = 0; i < 3; i++){
if (matrix[i][0] === matrix[i][1] && matrix[i][1] === matrix[i][2] ){
if (matrix[i][0] === 'X') {
winner = 1;
}
else if(matrix[i][0] === 'O') {
winner = 2;
}
}
if (matrix[0][i] === matrix[1][i] && matrix[1][i] === matrix[2][i]){
if (matrix[0][i] === 'X') {
winner = 1;
}
else if(matrix[0][i] === 'O') {
winner = 2;
}
}
}
if (matrix[0][0] === matrix[1][1] && matrix[1][1] === matrix[2][2] && matrix[0][0] === 'X'){
winner = 1;
}
if (matrix[0][0] === matrix[1][1] && matrix[1][1] === matrix[2][2] && matrix[0][0] === 'O'){
winner = 2;
}
if (matrix[0][2] === matrix[1][1] && matrix[1][1] === matrix[2][0] && matrix[2][0] === 'X'){
winner = 1;
}
if (matrix[0][2] === matrix[1][1] && matrix[1][1] === matrix[2][0] && matrix[2][0] === 'O'){
winner = 2;
}
if (draw(matrix) && winner === null){
res.send(JSON.stringify({winner: 0}))
}
else if (winner !== null) {
res.send(JSON.stringify({winner: winner}))
}
else {
res.send(JSON.stringify({winner: -1}))
}
})
app.listen(3000, () => {
console.log('app listening on port 3000!')
})
五、分析與原理
取flag的條件是 傳入的querytoken要和user數(shù)組本身的admintoken的MD5值相等,且二者都要存在。由代碼可知,全文沒(méi)有對(duì)user.admintokn 進(jìn)行賦值,所以理論上這個(gè)值時(shí)不存在的,但是下面有一句賦值語(yǔ)句:
?結(jié)果:
?第二題:
'use strict';
const express = require('express');
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser');
const path = require('path');
const isObject = obj => obj && obj.constructor && obj.constructor === Object;
function merge(a, b) {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
function clone(a) {
return merge({}, a);
}
// Constants
const PORT = 8080;
const HOST = '0.0.0.0';
const admin = {};
// App
const app = express();
app.use(bodyParser.json())
app.use(cookieParser());
app.use('/', express.static(path.join(__dirname, 'views')));
app.post('/signup', (req, res) => {
var body = JSON.parse(JSON.stringify(req.body)); {"__proto__": {"admin":1}}
var copybody = clone(body)
if (copybody.name) {
res.cookie('name', copybody.name).json({
"done": "cookie set"
});
} else {
res.json({
"error": "cookie not set"
})
}
});
app.get('/getFlag', (req, res) => {
var аdmin = JSON.parse(JSON.stringify(req.cookies))
if (admin.аdmin == 1) {
res.send("hackim19{}");
} else {
res.send("You are not authorized");
}
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
八、分析與原理
分析:小例題:obj[a][b] = value
。如果攻擊者可以控制a
和value
,則可以將a的值設(shè)置為__proto__
,并且將使用值value
為應(yīng)用程序的所有現(xiàn)有對(duì)象定義屬性b
分析整個(gè)代碼:首先定義一個(gè)函數(shù)merge,關(guān)于合并兩個(gè)對(duì)象的設(shè)計(jì)是非常不安全的。由于執(zhí)行merge()的庫(kù)的最新版本已經(jīng)打了補(bǔ)丁,這道題目使用了舊方法合并對(duì)象,從而易受到攻擊。
在上面的代碼中,我們可以快速注意到的一點(diǎn)是將2 個(gè)“admins”定義為const admin
和var admin
。理想情況下,js中不允許將const變量再次定義為var,所以在題目中有一個(gè)需要我們搞懂的點(diǎn)便是,其中一個(gè)是正常的a,而另一個(gè)是其他的a(同形異義字)。
原理:對(duì)象遞歸合并、按路徑定義屬性、對(duì)象克隆
九、具體操作,payload與結(jié)果
從源代碼入手:? Merge()
函數(shù)是以一種可能發(fā)生原型污染的方式編寫(xiě)的。這是問(wèn)題分析的關(guān)鍵。
? ? 易受攻擊的函數(shù)是在通過(guò)clone(body)
訪問(wèn)/signup
時(shí)被調(diào)用的,因此我們可以在注冊(cè)時(shí)發(fā)送JSON有效負(fù)載,這樣就可以添加admin屬性并立即調(diào)用/getFlag
來(lái)獲取Flag。
? ?如前所述,我們可以使用__proto__
(points to constructor.prototype)來(lái)創(chuàng)建值為1的admin
屬性。
執(zhí)行相同操作的最簡(jiǎn)單的payload:
{"__proto__": {"admin": 1}}
結(jié)果:?
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-637208.html
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-637208.html
到了這里,關(guān)于網(wǎng)絡(luò)安全之原型鏈污染的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!