zoukankan      html  css  js  c++  java
  • Nodejs-原型链污染

    原型链污染

    javascript 原型链

    在javascript中,继承的整个过程就称为该类的原型链。

    每个对象的都有一个指向他的原型(prototype)的内部链接,这个原型对象又有它自己的原型,一直到null为止。

    在javascript中一切皆对象,因为所有的变量,函数,数组,对象 都始于object的原型即object.prototype,但只有类有对象,对象没有,对象有的是__proto__

    like:

    日期时:

    f -> Data.prototype -> object.prototype->null

    函数时:

    d -> function.prototype -> object.prototype->null

    数组时:

    c -> array.prototype -> object.prototype->null

    类时:

    b -> a.prototype -> object.prototype->null

    当要使用或输出一个变量时:首先会在本层中搜索相应的变量,如果不存在的话,就会向上搜索,即在自己的父类中搜索,当父类中也没有时,就会向祖父类搜索,直到指向null,如果此时还没有搜索到,就会返回 undefined。

    原型链污染就是:在我们想要利用的代码之前的赋值语句如果可控的话,我们进行 ——__proto__ 赋值,之后就可以利用代码了。

    原型链污染一般会出现在对象、或数组的键名或属性名可控,而且是赋值语句的情况下。

    例一:

    var mds = [];
    
    for  (var i=0;i<3;i++){
      mds[i]=[null,null,null];
    }
    Array(3) [ null, null, null ]  
    
    var row="__proto__"
    undefined
    
    var co="admin"
    undefined
    
    mds[row][co]="sunsec"
    "sunsec"
    
    var c=[]
    undefined
    
    c[co]
    "sunsec"  
    

    这里我们创建了两个数组,一个是mds,另一个是c,此时我们将mds的mds.__proto__上一条链的[co]为sunsec。

    题一:
    const express = require('express')         //关于require,require是一个函数
    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的条件就是user.admintoken && req.query.querytoken && md5(user.admintoken) === req.query.querytoken,但是我们无法掌控到admintoken。

    但是在api这个接口的地方,是可以接收data,row,col所以此时我们可以进行原型链污染,在这里user.admintoken并没有被赋值,我们可以进行污染,只要将

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

    client.row设置为__proto__即可,再将col设为admintoken,此时data就是我们设置的值了。

    此时在admin路由处传入querytoken。

    题二
    '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));
        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}`);
    

    flag获取条件: admin.аdmin == 1)

    看到这处路由可以发现admin的admin属性是不存在的。

    看到函数merge:

    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
    }
    

    这里进行了对象的合并,但是键值是可以被控制的。

    注意:在创建字典的时候,__proto__,不是作为一个键名,而是已经作为__proto__给其父类进行赋值

    所以当我们设置:
    var test = {"test":"aa","__proto__":{"admin":1}}

    此时:test有两个键值,分别为test以及admin。

    那么我们再将其跟另一个test1进行对象的合并,此时看一下test1,会发现他不具备admin属性。

    如何避免:

    利用JSON.parse,JSON.parse 会把一个json字符串 转化为 javascript的object。

  • 相关阅读:
    Mac 虚拟机VMware Fusion显示内部错误的解决方法
    Linux系统中的引导过程与服务控制
    linux系统中如何删除lvm分区
    Linux命令下: LVM逻辑卷配置过程详解(创建,增加,减少,删除,卸载)
    解决:rm: 无法删除"tomcat": 设备或资源忙
    LVM操作过程
    Linux命令下进行硬盘挂载、分区、删除分区,格式化,卸载方法
    finger 命令查询用户名、主目录、停滞时间、登录时间
    【MySQL】MySQL5.7传统复制切换为GTID复制
    深入理解MySQL系列之锁
  • 原文地址:https://www.cnblogs.com/ophxc/p/13298896.html
Copyright © 2011-2022 走看看