【攻防世界】Web | wife_wife 详细题解WP
【攻防世界】 | Web | wife_wife详细题解WP
进入题目环境,首先进入sign up
创建一个admin用户,下方有勾选 is admin
,打上勾
随便填写Invite Code
,进行抓包
如果是新手看不懂是什么意思无所谓,这是一道JavaScript原型练污染
测试了一会儿,发现加上__proto__
这个继承对象的属性payload后,不需要管Invite Code
是什么,也能注册成功
admin/admin登录进去后就能够看到flag
CatCTF{test_flag_h0w_c@n_I_l1ve_w1th0ut_nilou}
一、下面是对__proto__的解释:
在 JavaScript 中,__proto__
是一个对象属性,用于实现原型继承(Prototype Inheritance)。它指向该对象的原型对象(即创建该对象的构造函数的 prototype
属性),从而允许对象继承其他对象的属性和方法。以下是其核心作用和详细说明:
一、基本概念与作用
原型链机制
- JavaScript 是基于原型的语言,对象通过
__proto__
链接到其原型对象,形成原型链。 - 当访问一个对象的属性 / 方法时,JavaScript 首先在对象本身查找,若不存在则沿
__proto__
向上查找,直到原型链末尾(Object.prototype
)或找到为止。
- JavaScript 是基于原型的语言,对象通过
__proto__
与prototype
的区别__proto__
:实例对象的属性,指向其原型对象(即构造函数的prototype
)。prototype
:构造函数的属性,是一个普通对象,用于为实例提供共享属性和方法。
javascript
const obj = new Object();
obj.__proto__ === Object.prototype; // true(实例的 __proto__ 指向构造函数的 prototype)
二、__proto__
的具体用途
动态修改原型
- 可通过
__proto__
为现有对象动态添加或替换原型,改变继承关系。
javascript
const animal = { speak() { console.log("Animal sound"); } };
const dog = {};
dog.__proto__ = animal; // dog 继承 animal 的方法
dog.speak(); // 输出: "Animal sound"
- 可通过
实现继承
- 在 ES6 之前,常通过
__proto__
手动实现继承(ES6 后推荐使用class
和extends
)。
javascript
function Animal() { this.type = "animal"; }
Animal.prototype.speak = function() { console.log("Sound"); }; function Dog() { this.breed = "Labrador"; }
Dog.prototype.__proto__ = Animal.prototype; // Dog 继承 Animal const dog = new Dog();
dog.speak(); // 输出: "Sound"
- 在 ES6 之前,常通过
检查原型链
- 通过
__proto__
可逐层访问对象的原型链,用于调试或验证继承关系。
javascript
const arr = [];
arr.__proto__ === Array.prototype; // true
arr.__proto__.__proto__ === Object.prototype; // true
- 通过
三、注意事项与替代方案
性能与兼容性问题
- 性能开销:动态修改
__proto__
会影响对象属性查找速度,现代引擎对此有优化但仍需谨慎。 - 兼容性:
__proto__
是历史遗留特性(ES6 才标准化),部分旧环境(如 IE10 及以下)不支持。
- 性能开销:动态修改
推荐替代方案
Object.create()
:创建新对象时直接指定原型。
javascript
const animal = { speak() { console.log("Sound"); } };
const dog = Object.create(animal); // 等价于 dog.__proto__ = animal
ES6 class/extends
:更简洁的继承语法(本质仍基于原型)。
javascript
class Animal { speak() { console.log("Sound"); } }
class Dog extends Animal {}
Object.setPrototypeOf()
:显式修改原型(不推荐频繁使用)。
javascript
Object.setPrototypeOf(dog, animal); // 等价于 dog.__proto__ = animal
四、与现代 JavaScript 的关系
__proto__
的设计初衷:提供一种直观但非标准的方式访问原型(早期 JavaScript 未暴露原型机制)。- 现代实践:优先使用
Object.create()
、class
、extends
等语法,避免直接操作__proto__
以提升性能和代码可维护性。
总结
__proto__
是 JavaScript 原型链的核心机制,允许对象继承其他对象的属性和方法。尽管它提供了动态修改继承关系的灵活性,但由于性能和兼容性问题,现代开发中更推荐使用标准化的替代方案。理解 __proto__
有助于深入掌握 JavaScript 的继承原理,但实际编码中应谨慎使用。
二、下面是关于js原型链污染的原理和防御:
一、原型链污染的核心原理
1. JavaScript 原型继承机制
JavaScript 是基于原型的语言,每个对象都有一个内部属性[[Prototype]]
(可通过__proto__
访问),指向其原型对象。当访问一个对象的属性时,JavaScript 会:
- 先在对象本身查找
- 如果找不到,沿原型链向上查找
- 直到
Object.prototype
为止
例如:
const person = { name: "Alice" };
console.log(person.toString()); // 继承自Object.prototype
这里toString()
方法并非person
对象自身的属性,而是通过原型链从Object.prototype
继承而来。
2. 动态原型修改
JavaScript 允许动态修改对象的原型,包括内置对象的原型。原型链污染攻击就是利用这一点,通过修改Object.prototype
或其他原型对象,向其中注入恶意属性或方法。
关键方法包括:
- 直接修改
__proto__
属性 - 使用
Object.setPrototypeOf()
- 利用漏洞函数(如
merge()
、JSON.parse()
的自定义解析器)
二、攻击实例解析
1. 经典案例:利用不安全的对象合并函数
许多 JavaScript 库提供对象合并功能(如lodash.merge
、Object.assign
),但如果实现不当,可能导致原型污染。
漏洞代码示例:
function merge(target, source) {
for (let key in source) {
if (source.hasOwnProperty(key)) {
if (typeof source[key] === 'object' && source[key] !== null) {
if (typeof target[key] !== 'object') {
target[key] = {};
}
merge(target[key], source[key]); // 递归合并
} else {
target[key] = source[key];
}
}
}
return target;
}
// 攻击者可控的输入
const payload = JSON.parse('{"__proto__":{"isAdmin":true}}');
const obj = {};
merge(obj, payload); // 合并操作
console.log(obj.isAdmin); // 输出: undefined
console.log({}.isAdmin); // 输出: true!污染了Object.prototype
攻击路径:
- 攻击者构造特殊 JSON 数据,包含
__proto__
键 - 应用使用有漏洞的合并函数处理该数据
- 合并过程中,
Object.prototype
被修改,添加了isAdmin
属性 - 所有对象(包括新创建的对象)都会继承这个属性
2. 实际应用场景:绕过权限检查
在 Web 应用中,权限检查可能依赖于用户对象的isAdmin
属性:
function checkAdmin(request) {
return request.user.isAdmin === true;
}
// 正常情况下
const normalUser = { username: "user" };
checkAdmin({ user: normalUser }); // false
// 原型污染后
checkAdmin({ user: {} }); // true!空对象也被认为是管理员
三、防御策略详解
1. 禁止直接修改原型
安全合并函数改进:
function safeMerge(target, source) {
for (let key in source) {
if (source.hasOwnProperty(key)) {
// 跳过原型属性
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue;
}
if (typeof source[key] === 'object' && source[key] !== null) {
if (typeof target[key] !== 'object') {
target[key] = {};
}
safeMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
return target;
}
2. 使用不可变对象
通过Object.freeze()
防止对象被修改:
// 冻结Object.prototype
Object.freeze(Object.prototype);
// 尝试污染
const payload = JSON.parse('{"__proto__":{"isAdmin":true}}');
merge({}, payload);
console.log({}.isAdmin); // 输出: undefined(污染失败)
3. 输入验证与过滤
- 对用户输入进行严格验证,拒绝包含
__proto__
、constructor
等敏感键的数据 - 使用安全的 JSON 解析库(如
secure-json-parse
)
const secureParse = require('secure-json-parse');
const input = '{"__proto__":{"evil":"payload"}}';
try {
const data = secureParse(input); // 自动防御原型链污染
} catch (e) {
console.error('恶意输入:', e.message);
}
4. 框架和库的安全配置
- 使用经过安全审计的库
- 避免使用
body-parser
等易受攻击的中间件(改用express.json()
并配置strict: true
) - 定期更新依赖,修复已知漏洞
四、高级渗透测试技巧
1. 自动化检测工具
- 使用 AST(抽象语法树)分析工具检测潜在的原型链污染点
- 结合 fuzz 测试,自动生成恶意输入
- 推荐工具:
PrototypePolluted
、Retire.js
2. 实战中的漏洞挖掘
在代码审计中重点关注
- 递归合并函数
- 动态属性赋值
- 自定义 JSON 解析器
- 使用
eval()
或Function()
动态执行代码的位置
检测代码示例:
// 查找潜在的原型链污染点
const acorn = require('acorn');
const fs = require('fs');
const code = fs.readFileSync('vulnerable.js', 'utf8');
const ast = acorn.parse(code, { ecmaVersion: 2020, sourceType: 'module' });
// 查找所有for...in循环
function findForInLoops(node) {
if (node.type === 'ForInStatement') {
console.log('潜在风险: 未过滤的for...in循环', node.loc);
}
for (let key in node) {
if (typeof node[key] === 'object' && node[key] !== null) {
findForInLoops(node[key]);
}
}
}
findForInLoops(ast);
五、总结
JavaScript 原型链污染是一种隐蔽但危害极大的漏洞,利用了 JavaScript 动态原型系统的特性。作为安全工程师,我们需要:
- 深入理解 JavaScript 原型机制
- 在开发阶段实施严格的防御措施
- 在渗透测试中重点关注潜在风险点
- 持续监控和更新依赖库
【攻防世界】Web | wife_wife 详细题解WP的更多相关文章
- 攻防世界Web区部分题解
攻防世界Web区部分题解 前言:PHP序列化就是把代码中所有的 对象 , 类 , 数组 , 变量 , 匿名函数等全部转换为一个字符串 , 提供给用户传输和存储 . 而反序列化就是把字符串重新转换为 ...
- XCTF攻防世界Web之WriteUp
XCTF攻防世界Web之WriteUp 0x00 准备 [内容] 在xctf官网注册账号,即可食用. [目录] 目录 0x01 view-source2 0x02 get post3 0x03 rob ...
- 攻防世界Web刷题记录(进阶区)
攻防世界Web刷题记录(进阶区) 1.baby_web 发现去掉URLhttp://111.200.241.244:51461/1.php后面的1.php,还是会跳转到http://111.200.2 ...
- CTF--web 攻防世界web题 robots backup
攻防世界web题 robots https://adworld.xctf.org.cn/task/answer?type=web&number=3&grade=0&id=506 ...
- CTF--web 攻防世界web题 get_post
攻防世界web题 get_post https://adworld.xctf.org.cn/task/answer?type=web&number=3&grade=0&id=5 ...
- 攻防世界 web进阶练习 NewsCenter
攻防世界 web进阶练习 NewsCenter 题目是NewsCenter,没有提示信息.打开题目,有一处搜索框,搜索新闻.考虑xss或sql注入,随便输入一个abc,没有任何搜索结果,页面也没有 ...
- 攻防世界web新手区
攻防世界web新手区 第一题view_source 第二题get_post 第三题robots 第四题Backup 第五题cookie 第六题disabled_button 第七题simple_js ...
- XCTF攻防世界web进阶练习—mfw
XCTF攻防世界web进阶练习-mfw题目为mfw,没有任何提示.直接打开题目,是一个网站 大概浏览一下其中的内容,看到其中url变化其实只是get的参数的变化查看它的源码,看到有一个?page=fl ...
- 攻防世界Web刷题记录(新手区)
攻防世界Web刷题记录(新手区) 1.ViewSource 题如其名 Fn + F12 2.get post 3.robots robots.txt是搜索引擎中访问网站的时候要查看的第一个文件.当一个 ...
- 攻防世界 WEB 高手进阶区 csaw-ctf-2016-quals mfw Writeup
攻防世界 WEB 高手进阶区 csaw-ctf-2016-quals mfw Writeup 题目介绍 题目考点 PHP代码审计 git源码泄露 Writeup 进入题目,点击一番,发现可能出现git ...
随机推荐
- 【Java】操作数据库
工具: eclipse MySQL Navicat for MySQL MySQL 连接驱动:mysql-connector-java-5.0.4-bin.jar SQL 代码 CREATE TABL ...
- 学习Django【2】把视图和路由连接起来的是URLconfigs的配置文件
在view.py视图中定义: def detail(request, question_id): return HttpResponse("You're looking at questio ...
- SearXNG私有化部署与Dify集成
一.概述 SearXNG 是一个免费的互联网元搜索引擎,它聚合了来自各种搜索服务和数据库的结果,但摆脱了隐私追踪 -- 用户行为既不会被引擎跟踪也不会被分析. 功能特性 自托管,可以私有化部署 没有用 ...
- idea git建立分支、切换分支、合并分支
为什么要建立分支 git默认的主分支名字为master,一般团队开发时,都不会在master主分支上修改代码,而是建立新分支,测试完毕后,在将分支的代码合并到master主分支上 2.操作如下: 2. ...
- AUTOFAC学习DEMO2-——ContainerBuilder注册三种方式、反射注册
注册组件 通过容器构造器ContainerBuilder注册组件的三种方式: 通过类型反射注册 通过现存实例注册(实现对象的实例) lambda表达式注册(通过可实例化对象的匿名函数注册) 每个组件可 ...
- Nacos简介—3.Nacos的配置简介
大纲 1.Nacos生产集群Web端口与数据库配置 2.Nacos生产集群的Distro协议核心参数 3.Nacos打通CMDB实现跨机房的就近访问 4.Nacos基于SPI动态扩展机制来获取CMDB ...
- 记录一次线上问题排查:JDK序列化问题
场景简要概述 新加了个字段,然后发版,上线就发现了报错 当时这个问题很简单,其实就是用的是 JDK序列化,当时这个类实现了 Serializable接口,但是没显示定义 serialVersionUI ...
- edge浏览器新版,开心的扔掉chrome!还是微软更良心!windows系统全球用也没说啥!让你android能!
直接上图吧,这下google慌了吧!微软给力呀!关键是版本直接最新的chromium!比起qq浏览器70,360浏览器78新的多了: 微软开发,质量信得过,就个人隐私之类的我更相信微软,比起googl ...
- Linux 的那些操作都出自哪里?
Linux 的那些操作都出自哪里? 可以说 Linux 是一种 Unix.Unix 有一个 man 手册,手册包含了安装的软件的使用帮助,遇到问题的解决办法.总之几乎所有的操作都是手册里面有迹可循的, ...
- 【安装】Linux下安装CUDA ToolKit 11.4和cuDNN 8
注意!如果你使用的是pytorch,只需要装好CUDA,不需要装cuDNN.而且完全可以等到报错了再装CUDA,一般情况系统都已经装好CUDA Toolkit了. 除非你只装了低版本的CUDA Too ...