前言

赛后自学了nodejs原型链污染后来尝试做这个题,难度不算太大,但是绕过姿势非常奇怪没见过,写一篇总结记录一下做法

wp

首先打开环境发现是一个登录框,题目有附件我们下载查看附件

最关键的就是controller.js和main.js两个文件,index.html是前端代码用处不大

代码审计

controller.js

 const fs = require("fs");//引入js模块,为flag1和flag2提供读文件操作
const SECRET_COOKIE = process.env.SECRET_COOKIE || "this_is_testing_cookie"
//设置SECRET_COOKIE的值,如果环境变量process.env中存在SECRET.COOKIE就把这个常量赋值为环境变量中的值,如果环境变量中没有则设置为"this_is_testing_cookie"
const flag1 = fs.readFileSync("/flag1")//读flag1中的值
const flag2 = fs.readFileSync("/flag2")//读flag2中的值

//merge函数,nodejs原型链污染的关键函数,文章后面会放我对这个函数在nodejs原型链污染作用上的理解
function merge(target, source) {
for (let key in source) {
if (key == "__proto__") {
continue;//过滤了__proto__键,本题不能用__proto__来进行原型链污染,但prototype仍然可以使用
}
if (key in target && key in source) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}

//设置一个LoginController函数,描述登录逻辑
function LoginController(req, res) {
try {
let user = {}//设置一个空对象user
merge(user, req.body)//req.body是一个属性,通常用于存储与请求相关的主体内容,尤其是在POST请求中。当客户端向服务器发送数据(例如,通过表单提交或JSON数据),这些数据将被解析并存储在req.body中

if (user.username !== "admin" || user.password !== Math.random().toString()) {
res.status(401).type("text/html").send("Login Failed")//判断用户名或者密码的逻辑
} else {
//登录成功的逻辑,这里是本题nodejs原型链污染的重要利用点
res.cookie("user", SECRET_COOKIE)
req.user = "admin"
res.redirect("/flag1")
}
} catch (e) {
console.log(e)
res.status(401).type("text/html").send("What the heck")
}
}

//设置一个Flag1Controller函数,描述flag1的获得逻辑
function Flag1Controller(req, res) {
try {
if (req.cookies.user === SECRET_COOKIE || req.user == "admin") {
//这个判断使用逻辑或,只要两者有一个判断为true,整个判断为true,所以只要满足req.user == "admin"即可
res.setHeader("This_Is_The_Flag1", flag1.toString().trim())
//把flag1的内容放在相应包中
res.status(200).type("text/html").send("Login success. Welcome,admin!")
} else {
res.status(401).type("text/html").send("Unauthorized")
}
} catch (__) { }
}

//设置一个Flag2Controller函数,描述flag2的获得逻辑
function Flag2Controller(req, res) {
//三元运算符,如果req.body.checkcode的值存在则保留原有的值,如果不存在则复制为1234
let checkcode = req.body.checkcode ? req.body.checkcode : 1234;
console.log(req.body)
if (checkcode.length === 16) { //判断checkcode的长度要等于16
try {
checkcode = checkcode.toLowerCase() //把checkcode的值中的大写字母变成小写字母
if (checkcode !== "aGr5AtSp55dRacer") { //很明显经过前面的操作这里无法直接满足,所以可以通过触发 toLowerCase() 的错误来绕过条件判断,可以通过传递一个非字符串类型且长度为16的值(比如数组),这样执行checkcode时会抛出错误(因为非字符串无法调用该方法),错误被catch捕获后,代码会继续执行catch后面的内容
res.status(403).json({ "msg": "Invalid Checkcode1:" + checkcode })
}
} catch (__) { }
//得到flag2
res.status(200).type("text/html").json({ "msg": "You Got Another Part Of Flag: " + flag2.toString().trim() })
} else {
res.status(403).type("text/html").json({ "msg": "Invalid Checkcode2:" + checkcode })
}
}

module.exports = {
LoginController,
Flag1Controller,
Flag2Controller
}

main.js

 const express = require("express")
const fs = require("fs")
const cookieParser = require("cookie-parser");
const controller = require("./controller")

const app = express();
const PORT = Number(process.env.PORT) || 80
const HOST = '0.0.0.0'


app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.json())

app.use(express.static('static'))

//登陆页面
app.get("/", (res) => {
res.sendFile(__dirname, "static/index.html")
})

//传值使用post方式
app.post("/", (req, res) => {
controller.LoginController(req, res)
})

//flag1要发送get请求
app.get("/flag1", (req, res) => {
controller.Flag1Controller(req, res)
})

//flag2要发送post请求,可以在登录成功页面抓包post方式在flag2路由下传值
app.post("/flag2", (req, res) => {
controller.Flag2Controller(req, res)
})

app.listen(PORT, HOST, () => {
console.log(`Server is listening on Host ${HOST} Port ${PORT}.`)
})

解题

代码审计后本题的思路就非常明显了,首先绕过登录判断成功登录后访问flag1拿到前半段flag,然后访问flag2绕过判断拿到后半段flag

登录

首先我们随便输入用户名密码,抓包,因为源代码中假如登录成功会执行

 res.cookie("user", SECRET_COOKIE)
req.user = "admin"
res.redirect("/flag1")

所以我们要利用nodejs原型链污染把req.user设置为admin

payload

 {
"username":"admin",
"password":"123",
"constructor":{
"user":"admin"
}
}

把content-type改成application/json后发送请求包

flag1

执行了catch(e)中的内容,看起来登录好像已经绕过了,我们尝试手动访问flag1

成功访问,我们修改请求包为get访问flag1

成功拿到前半段flag1

flag2

接下来就是第二段flag

我做的时候竟然纳闷flag2这段代码是哪个页面的逻辑,想了半天才想到去main.js里面去看看,真服了我这脑子

flag2是post传值,这里需要绕过的关键判断是

 if (checkcode.length === 16) { //判断checkcode的长度要等于16
try {
checkcode = checkcode.toLowerCase() //把checkcode的值中的大写字母变成小写字母
if (checkcode !== "aGr5AtSp55dRacer")

很明显如果我们的字符串大写字母转为小写字母后肯定不能满足这个判断的字符串,因为里面还有大写字母。代码审计中提到了这里的绕过方式,我们利用数组绕过

 {
"checkcode":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
}

成功拿到第二段flag,拼起来就是完整的flag。

[nodejs原型链污染及绕过]校赛第四次纳新赛 bypass WP的更多相关文章

  1. 【web安全】Nodejs原型链污染分析

    Nodejs原型链污染分析 什么是js原型? 可以将js原型理解为其他OOP语言中的类,但还是有细微区别. 1. function F(){...} 2. var f = new F(); 分析: 1 ...

  2. redpwnctf-web-blueprint-javascript 原型链污染学习总结

    前几天看了redpwn的一道web题,node.js的web,涉及知识点是javascript 原型链污染,以前没咋接触过js,并且这个洞貌似也比较新,因此记录一下学习过程 1.本机node.js环境 ...

  3. 原型链污染(Node.js污染,javasrcipt原型链污染的)

    学习链接: https://www.jianshu.com/p/6e623e9debe3 关于NJS  https://xz.aliyun.com/t/7184 相关题是 GYCTF  ez_expr ...

  4. javascript 原型链污染

    原理①javascript中构造函数就相当于类,并且可以将其实例化 ②javascript的每一个函数都有一个prototype属性,用来指向该构造函数的原型同样的javascript的每一个实例对象 ...

  5. 初探JavaScript原型链污染

    18年p师傅在知识星球出了一些代码审计题目,其中就有一道难度为hard的js题目(Thejs)为原型链污染攻击,而当时我因为太忙了(其实是太菜了,流下了没技术的泪水)并没有认真看过,后续在p师傅写出w ...

  6. js原型链污染详解

    前言 之前打某湖论剑,两道js的题,给我整懵逼了,发现以前都没对js做过多少研究,趁着被毒打了,先研究一波js原型链,未雨绸缪. 基础 protype 首先我们研究js原型链,得搞明白原型是什么,这里 ...

  7. JavaScript原型链及其污染

    JavaScript原型链及其污染 一.什么是原型链? 1.JavaScript中,我们如果要define一个类,需要以define"构造函数"的方式来define: functi ...

  8. Nodejs-原型链污染

    原型链污染 javascript 原型链 在javascript中,继承的整个过程就称为该类的原型链. 每个对象的都有一个指向他的原型(prototype)的内部链接,这个原型对象又有它自己的原型,一 ...

  9. js中的原型,原型链和继承

    在传统的基于Class的语言如Java.C++中,继承的本质是扩展一个已有的Class,并生成新的Subclass. 由于这类语言严格区分类和实例,继承实际上是类型的扩展.但是,JavaScript最 ...

  10. Javascript之其实我觉得原型链没有难的那么夸张!

    原型链.闭包.事件循环等,可以说是js中比较复杂的知识了,复杂的不是因为它的概念,而是因为它们本身都涉及到很多的知识体系.所以很难串联起来,有一个完整的思路.我最近想把js中有点意思的知识都总结整理一 ...

随机推荐

  1. ubuntu 22.04 deskop 无法打开terminal

    系统语言设置的问题,改为汉语即可

  2. vue浏览器插件及安装

    vue浏览器插件及安装 插件下载: 链接:https://pan.baidu.com/s/1Wu4a4skkJ-i5ccydRnn8qg 提取码:dwux 然后打开浏览器,F12,有这个vue就成功了

  3. Delphi 使用API函数AnimateWindow实现窗体特效功能

    API函数 AnimateWindow 使用: 函数功能:窗体显示和隐藏时产生特殊的动画效果:可以产生两种类型的动画效果: 滚动动画 和 滑动动画 函数原型:BOOL AnimateWindow(HW ...

  4. storm部署文档

    背景 这篇笔记原来是记录在印象笔记中的,没有发布到博客中,这次我重新整理一下发布上来,希望给读者以参考. Storm的部署手册 Zookeepr的部署 首先下载安装包:apache-zookeeper ...

  5. TJSON的烦人的泄漏

    System.Json中的JSON应该说还是好用的,因为相关superObject的json使用,转换过来概念思路上有点混淆搞不清. 正题:老是泄漏.一会儿是TJSONArray,一会儿是TJSONO ...

  6. springboot将vo生成文件到目录

    依赖 org.springframework spring-mock 2.0.8 com.alibaba fastjson 1.2.62 service实现 public RestResponseBo ...

  7. Jmeter参数化总结

    参数化步骤: 1.连接数据库 2.获取account表手机号数据 3.获取手机号个数 4.增加For Each控制器 5.将请求添加至循环控制器里面 脚本:循环登录.jmx 页面如下: 下面主要说明F ...

  8. 关于while循环与for循环

    首先看for循环 def test(): print('1') print('2') for i in range(3): test() 执行后的结果,按照下图所示,for循环就是把指定的函数重复执行 ...

  9. 工良出品 | 长文讲解 MCP 和案例实战

    作者:痴者工良 博客地址:https://www.whuanle.cn/ 示例项目地址:https://github.com/whuanle/mcpdemo 近期 MCP 协议越来越爆火,很多开发者都 ...

  10. 题解:P10858 [HBCPC2024] Long Live

    给你两个数 x,yx,yx,y 让你找到一组 a,ba,ba,b,使 lcm⁡(x,y)gcd⁡(x,y)=ab\sqrt{\frac{\operatorname{lcm}(x,y)}{\gcd(x, ...