当前位置: 首页 > news >正文

6.12ctf练习

[西湖论剑 2022]Node Magical Login

源码在这里:GitHub - CTF-Archives/2022-xhlj-web-node_magical_login: A web challenge in 2022 西湖论剑大赛打开

打开环境是个登录框,先进行了扫描和抓包都没有看见什么有价值的东西,看源码

大致连接到存在flag1和flag2


function LoginController(req,res) {try {const username = req.body.usernameconst password = req.body.passwordif (username !== "admin" || password !== Math.random().toString()) {res.status(401).type("text/html").send("Login Failed")} else {res.cookie("user",SECRET_COOKIE)res.redirect("/flag1")}} catch (__) {}
}
function Flag1Controller(req,res){try {if(req.cookies.user === SECRET_COOKIE){res.setHeader("This_Is_The_Flag1",flag1.toString().trim())res.setHeader("This_Is_The_Flag2",flag2.toString().trim())res.status(200).type("text/html").send("Login success. Welcome,admin!")}if(req.cookies.user === "admin") {res.setHeader("This_Is_The_Flag1", flag1.toString().trim())res.status(200).type("text/html").send("You Got One Part Of Flag! Try To Get Another Part of Flag!")}else{res.status(401).type("text/html").send("Unauthorized")}}catch (__) {}
}

从这两部分代码中能够知道登录成功的条件是"admin/生成随机数转换为的字符串",爆破随机数这个不太可能,应该就要从别的地方入手

由于每次调用 Math.random() 都会产生一个新的随机数,密码验证几乎不可能成功,除非每次用户输入的密码恰好是 Math.random().toString() 的结果(这在实际情况下是不可能的)

看登录成功之后会生成一个名为user的cookie,值为SECRET_COOKIE,再重定向到flag1

在下一部分的代码中,当从客户端发送的cookie中user=admin就会返回第一部分的flag

所以直接抓包flag1修改cookie

接下来获取flag2

function CheckController(req,res) {let checkcode = req.body.checkcode?req.body.checkcode:1234;console.log(req.body)if(checkcode.length === 16){try{checkcode = checkcode.toLowerCase()if(checkcode !== "aGr5AtSp55dRacer"){res.status(403).json({"msg":"Invalid Checkcode1:" + checkcode})}}catch (__) {}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})}
}

路由

app.post("/getflag2",(req,res)=> {controller.CheckController(req,res)
})

请求体中的checkcode长度要为16,并且在toLowerCase()转换为小写后字符串=="aGr5AtSp55dRacer"

在 Web 应用的安全性验证中,toLowerCase() 的验证逻辑如果没有针对输入的数据格式做严格的检查,可能会被利用 JSON 数据结构进行绕过

利用JSON数组来绕过

解释一下这个数组的构造思路

JSON数组的结构

{"数组名称":["a","b","c"]}

"aGr5AtSp55dRacer"在数组中为一个值,所以要满足长度为16,还需要添加除了预期字符串之外的一系列数字,验证代码简单地提取 checkcode 字段,而没有检查其类型,就直接使用整个数组,导致 toLowerCase() 等字符串操作被绕过

{"checkcode":["aGr5AtSp55dRacer",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}

记得把类型改为json,否则会报错

[NCTF 2018]Easy_Audit

源码

<?php
highlight_file(__FILE__);
error_reporting(0);
if($_REQUEST){foreach ($_REQUEST as $key => $value) {if(preg_match('/[a-zA-Z]/i', $value))   die('waf..');}
}if($_SERVER){if(preg_match('/yulige|flag|nctf/i', $_SERVER['QUERY_STRING']))  die('waf..');
}if(isset($_GET['yulige'])){if(!(substr($_GET['yulige'], 32) === md5($_GET['yulige']))){         //日爆md5!!!!!!die('waf..');}else{if(preg_match('/nctfisfun$/', $_GET['nctf']) && $_GET['nctf'] !== 'nctfisfun'){$getflag = file_get_contents($_GET['flag']);}if(isset($getflag) && $getflag === 'ccc_liubi'){include 'flag.php';echo $flag;}else die('waf..');}
}?>

借这题也是比较清楚的认识了php的超级全局变量,现在开始一层一层分析源码

第一层
if($_REQUEST){foreach ($_REQUEST as $key => $value) {if(preg_match('/[a-zA-Z]/i', $value))   die('waf..');}
}

foreach,一个遍历数组并把值赋给value,但是这个$_REQUEST是什么?

$_REQUEST 是 PHP 中的一个超级全局变量数组,用于收集通过 GET、POST 和 COOKIE 发送到当前脚本的请求数据。它整合了 $_GET$_POST$_COOKIE 三个数组中的数据。

同时$_REQUEST还具有一个特性,当同时通过POST和GET传参时,如果POST和GET中含有相同的变量时,只会获取POST传参的变量值

那么第一层就利用POST传参进行变量覆盖,且post传参的值中不能出现字母,绕过第一层的判断

第二层

if($_SERVER){if(preg_match('/yulige|flag|nctf/i', $_SERVER['QUERY_STRING']))  die('waf..');
}

$_SERVER也是一个超级全局变量, $_SERVER['QUERY_STRING']指获取get传参的变量和值

http://xxx.com?a=123
那么 $_SERVER['QUERY_STRING']就获取a=123

所以就限制了get传参中不能出现被正则过滤的变量,编码绕过就行

第三层
if(isset($_GET['yulige'])){if(!(substr($_GET['yulige'], 32) === md5($_GET['yulige']))){         //日爆md5!!!!!!die('waf..');}else{if(preg_match('/nctfisfun$/', $_GET['nctf']) && $_GET['nctf'] !== 'nctfisfun'){$getflag = file_get_contents($_GET['flag']);}if(isset($getflag) && $getflag === 'ccc_liubi'){include 'flag.php';echo $flag;}else die('waf..');}
}
一.md5强比较

GET传参yulige,截取前32位和md5加密后的值相同,其实就是一个md5的强比较,传入数组为空即成立

二.正则表达式的绕过

"$"表示正则匹配的位置,这里放在了末尾说明在字符串的末尾进行正则匹配,所以只要在"nctfisfun"前面加数字或者字母就行,只要末尾能匹配到要求字符串就行

三.php伪协议

file_get_contents读取$flag,赋值给getflag==='ccc_liubi',用data伪协议写入就行

所以完整的GET传参payload

yulige[]=1&nctf=1nctfisfun&flag=data://text/plain,ccc_liubi

POST传参

yulige=1&nctf=1&flag=1

http://www.lryc.cn/news/374691.html

相关文章:

  • 海豚调度异常处理: 使用 arthas 在内存中删除启动失败的工作流
  • 在Qt中,QSerialPort::write(data) 和 readAll() 有什么关联和联系
  • 第 2 章:Spring Framework 中的 IoC 容器
  • 构造函数、实例、原型对象三者之间的关系
  • 人工智能抢走了他们的工作。现在他们得到报酬,让它听起来像人类
  • 大模型微调出错的解决方案(持续更新)
  • 企业多云策略的优势与实施指南
  • vue分页
  • 服务器上设置pnpm环境变量
  • Java中BIO、NIO、AIO详解
  • cloud_enum:一款针对不同平台云环境安全的OSINT工具
  • 图像的对比度和亮度
  • 手撕设计模式——计划生育之单例模式
  • Mac M3 Pro 部署Flink-1.16.3
  • Mysql 的分布式策略
  • 记录一个利用winhex进行图片隐写分离的
  • 压缩映射定理证明
  • Ubuntu20.04.6操作系统安装教程
  • (分治算法3)leecode 53 最大子数组和(最大子段和)
  • 【C++】模板初级
  • eslint 使用单引号,Prettier使用双引号冲突
  • 进化生物学的数学原理 知识点总结
  • 如何挑到高质量的静态IP代理?
  • vagrant putty错误的解决
  • 图像分割——U-Net论文介绍+代码(PyTorch)
  • C#进阶-ASP.NET的WebService跨域CORS问题解决方案
  • 如何利用TikTok矩阵源码实现自动定时发布和高效多账号管理
  • Java高级编程技术详解:从多线程到算法优化的全面指南
  • Redis 分布式锁过期了,还没处理完怎么办?
  • Vue2+Element-ui后台系统常用js方法