找回密码
 立即注册
首页 业界区 业界 【攻防世界】Web | wife_wife 详细题解WP

【攻防世界】Web | wife_wife 详细题解WP

郦珠雨 2025-7-5 22:18:32
【攻防世界】 | Web | wife_wife详细题解WP

1.png

进入题目环境,首先进入sign up创建一个admin用户,下方有勾选 is admin,打上勾
随便填写Invite Code,进行抓包
2.png

如果是新手看不懂是什么意思无所谓,这是一道JavaScript原型链污染
3.png

测试了一会儿,发现加上__proto__ 这个继承对象的属性payload后,不需要管Invite Code是什么,也能注册成功
4.png

5.png

admin/admin登录进去后就能够看到flag
  1. CatCTF{test_flag_h0w_c@n_I_l1ve_w1th0ut_nilou}
复制代码
一、下面是对__proto__的解释:

在 JavaScript 中,__proto__ 是一个对象属性,用于实现原型继承(Prototype Inheritance)。它指向该对象的原型对象(即创建该对象的构造函数的 prototype 属性),从而允许对象继承其他对象的属性和方法。以下是其核心作用和详细说明:
一、基本概念与作用


  • 原型链机制

    • JavaScript 是基于原型的语言,对象通过 __proto__ 链接到其原型对象,形成原型链
    • 当访问一个对象的属性 / 方法时,JavaScript 首先在对象本身查找,若不存在则沿 __proto__ 向上查找,直到原型链末尾(Object.prototype)或找到为止。

  • __proto__ 与 prototype 的区别

    • __proto__:实例对象的属性,指向其原型对象(即构造函数的 prototype)。
    • prototype:构造函数的属性,是一个普通对象,用于为实例提供共享属性和方法。
    javascript
    1. const obj = new Object();
    2. obj.__proto__ === Object.prototype; // true(实例的 __proto__ 指向构造函数的 prototype)
    复制代码
二、__proto__ 的具体用途


  • 动态修改原型

    • 可通过 __proto__ 为现有对象动态添加或替换原型,改变继承关系。
    javascript
    1. const animal = { speak() { console.log("Animal sound"); } };
    2. const dog = {};
    3. dog.__proto__ = animal; // dog 继承 animal 的方法
    4. dog.speak(); // 输出: "Animal sound"
    复制代码
  • 实现继承

    • 在 ES6 之前,常通过 __proto__ 手动实现继承(ES6 后推荐使用 class 和 extends)。
    javascript
    1. function Animal() { this.type = "animal"; }
    2. Animal.prototype.speak = function() { console.log("Sound"); };
    3. function Dog() { this.breed = "Labrador"; }
    4. Dog.prototype.__proto__ = Animal.prototype; // Dog 继承 Animal
    5. const dog = new Dog();
    6. dog.speak(); // 输出: "Sound"
    复制代码
  • 检查原型链

    • 通过 __proto__ 可逐层访问对象的原型链,用于调试或验证继承关系。
    javascript
    1. const arr = [];
    2. arr.__proto__ === Array.prototype; // true
    3. arr.__proto__.__proto__ === Object.prototype; // true
    复制代码
三、注意事项与替代方案


  • 性能与兼容性问题

    • 性能开销:动态修改 __proto__ 会影响对象属性查找速度,现代引擎对此有优化但仍需谨慎。
    • 兼容性:__proto__ 是历史遗留特性(ES6 才标准化),部分旧环境(如 IE10 及以下)不支持。

  • 推荐替代方案

    • Object.create()
      :创建新对象时直接指定原型。
      javascript
      1. const animal = { speak() { console.log("Sound"); } };
      2. const dog = Object.create(animal); // 等价于 dog.__proto__ = animal
      复制代码
    • ES6 class/extends
      :更简洁的继承语法(本质仍基于原型)。
      javascript
      1. class Animal { speak() { console.log("Sound"); } }
      2. class Dog extends Animal {}
      复制代码
    • Object.setPrototypeOf()
      :显式修改原型(不推荐频繁使用)。
      javascript
      1. 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为止
例如:
  1. const person = { name: "Alice" };
  2. 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),但如果实现不当,可能导致原型污染。
漏洞代码示例:
  1. function merge(target, source) {
  2.     for (let key in source) {
  3.         if (source.hasOwnProperty(key)) {
  4.             if (typeof source[key] === 'object' && source[key] !== null) {
  5.                 if (typeof target[key] !== 'object') {
  6.                     target[key] = {};
  7.                 }
  8.                 merge(target[key], source[key]); // 递归合并
  9.             } else {
  10.                 target[key] = source[key];
  11.             }
  12.         }
  13.     }
  14.     return target;
  15. }
  16. // 攻击者可控的输入
  17. const payload = JSON.parse('{"__proto__":{"isAdmin":true}}');
  18. const obj = {};
  19. merge(obj, payload); // 合并操作
  20. console.log(obj.isAdmin); // 输出: undefined
  21. console.log({}.isAdmin); // 输出: true!污染了Object.prototype
复制代码
攻击路径:

  • 攻击者构造特殊 JSON 数据,包含__proto__键
  • 应用使用有漏洞的合并函数处理该数据
  • 合并过程中,Object.prototype被修改,添加了isAdmin属性
  • 所有对象(包括新创建的对象)都会继承这个属性
2. 实际应用场景:绕过权限检查

在 Web 应用中,权限检查可能依赖于用户对象的isAdmin属性:
  1. function checkAdmin(request) {
  2.     return request.user.isAdmin === true;
  3. }
  4. // 正常情况下
  5. const normalUser = { username: "user" };
  6. checkAdmin({ user: normalUser }); // false
  7. // 原型污染后
  8. checkAdmin({ user: {} }); // true!空对象也被认为是管理员
复制代码
三、防御策略详解

1. 禁止直接修改原型

安全合并函数改进:
  1. function safeMerge(target, source) {
  2.     for (let key in source) {
  3.         if (source.hasOwnProperty(key)) {
  4.             // 跳过原型属性
  5.             if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
  6.                 continue;
  7.             }
  8.             if (typeof source[key] === 'object' && source[key] !== null) {
  9.                 if (typeof target[key] !== 'object') {
  10.                     target[key] = {};
  11.                 }
  12.                 safeMerge(target[key], source[key]);
  13.             } else {
  14.                 target[key] = source[key];
  15.             }
  16.         }
  17.     }
  18.     return target;
  19. }
复制代码
2. 使用不可变对象

通过Object.freeze()防止对象被修改:
  1. // 冻结Object.prototype
  2. Object.freeze(Object.prototype);
  3. // 尝试污染
  4. const payload = JSON.parse('{"__proto__":{"isAdmin":true}}');
  5. merge({}, payload);
  6. console.log({}.isAdmin); // 输出: undefined(污染失败)
复制代码
3. 输入验证与过滤


  • 对用户输入进行严格验证,拒绝包含__proto__、constructor等敏感键的数据
  • 使用安全的 JSON 解析库(如secure-json-parse)
  1. const secureParse = require('secure-json-parse');
  2. const input = '{"__proto__":{"evil":"payload"}}';
  3. try {
  4.     const data = secureParse(input); // 自动防御原型链污染
  5. } catch (e) {
  6.     console.error('恶意输入:', e.message);
  7. }
复制代码
4. 框架和库的安全配置


  • 使用经过安全审计的库
  • 避免使用body-parser等易受攻击的中间件(改用express.json()并配置strict: true)
  • 定期更新依赖,修复已知漏洞
四、高级渗透测试技巧

1. 自动化检测工具


  • 使用 AST(抽象语法树)分析工具检测潜在的原型链污染点
  • 结合 fuzz 测试,自动生成恶意输入
  • 推荐工具:PrototypePolluted、Retire.js
2. 实战中的漏洞挖掘

在代码审计中重点关注

  • 递归合并函数
  • 动态属性赋值
  • 自定义 JSON 解析器
  • 使用eval()或Function()动态执行代码的位置
检测代码示例:
  1. // 查找潜在的原型链污染点
  2. const acorn = require('acorn');
  3. const fs = require('fs');
  4. const code = fs.readFileSync('vulnerable.js', 'utf8');
  5. const ast = acorn.parse(code, { ecmaVersion: 2020, sourceType: 'module' });
  6. // 查找所有for...in循环
  7. function findForInLoops(node) {
  8.     if (node.type === 'ForInStatement') {
  9.         console.log('潜在风险: 未过滤的for...in循环', node.loc);
  10.     }
  11.     for (let key in node) {
  12.         if (typeof node[key] === 'object' && node[key] !== null) {
  13.             findForInLoops(node[key]);
  14.         }
  15.     }
  16. }
  17. findForInLoops(ast);
复制代码
五、总结

JavaScript 原型链污染是一种隐蔽但危害极大的漏洞,利用了 JavaScript 动态原型系统的特性。作为安全工程师,我们需要:

  • 深入理解 JavaScript 原型机制
  • 在开发阶段实施严格的防御措施
  • 在渗透测试中重点关注潜在风险点
  • 持续监控和更新依赖库

来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册