每日一题 —— [PolarCTF]PHP是世界上最好的语言¶
打开题目,发现一页的代码,根据提示,flag 在 $flag
变量里,也就是说只需要打开 ./flag.php
就可以了。
接着往下看,key1
和 key2
没有什么用,直接掠过。然后是 isset
函数,表明了你不可以通过 GET 和 POST 方式提交 flag1
和 flag2
参数。
接着往下看,有两行代码,分别是 parse_str
和 extract
。他们的本质都是一样的,一个把 GET 的参数进行了解析与变量覆盖,一个把 POST 参数进行了变量覆盖。最后,题目要求我们 flag1
和 flag2
参数都要等于 8gen1
。也就是说这道题的考点之一就是,如何将 flag1
参数传进去,并且成功覆盖。
我们先通过 response 以及 Wappalyzer 插件来获知,这个服务器使用的 php 版本为 7.0.33
,也就是说我们的 parse_str
方法可以只用一个参数,把 url 后面的 GET 请求字符串直接解析为 php 变量,而不用先存储在数组中。
我们不难发现,变量覆盖的代码有两行,分别是解析 GET 和解析 POST。既然我们不能通过 GET/POST 方式传入 flag1
,那么我们能不能把 flag1
从 GET 传给 POST? 经过尝试,意识到 $_POST
本身也是一个变量,数组类型的。那么我们就可以给这个数组类型添加两个元素进去。因此我们的 GET payload 就这么写:
在 URL 中传参是可以传入同名变量的,而且如果这个变量名后面跟着中括号的话,这个参数会被视作数组。也就是说,我们传入的 _POST[flag1]=8gen1
就会被解析为 $_POST['flag1'] = '8gen1'
,然后再由第二条 extract($_POST)
,我们就得到了 $flag1
。同理,$flag2
也得到了,并且他们的值都是 8gen1
。
现在,我们进入了第二步。在这里要求我们的 POST 方法中传入一个 504_SYS.COM
的参数。因为 php 不允许传入非法字符,而 .
号其实就是非法字符之一,所以如果我们直接传参,它就会帮我们转换成 504_SYS_COM
了。这个其实就是 php 的非法传参漏洞。这个漏洞直到 php8 才被修复,所以我们在这里利用这个漏洞:当 php <= 7.4 时,php 只会处理参数名的第一个非法字符。通常我们使用 [
这个符号。
这样子的话,这个参数就会被转换成 504_SYS.COM
,完成了传参的任务。
接下来的话是最后一步。这是一个 RCE,有一大串符号被过滤。看样子我们需要套个娃。首先先是简单地显示一下当前目录的文件。利用 scandir()
方法来查看当前目录:
然后就能看到当前目录下的所有文件。
但是我们其实早就知道 flag 在 flag.php
里面了,所以我们需要正经的 RCE。本来还在苦恼无参数 RCE, 但是其实这道题只是一个简单的套娃 RCE. 我们在外层使用 eval
函数,中间一层只需要使用 hex2bin
方法就可以绕过符号的过滤了。至于最内层的 16 进制字符串,我们不能直接写入,因为需要引号包裹。所以我们可以通过变量传入。这里我们借助 GET 方法。
其中,参数 1
中的 16 进制字符串编码自 system('tac flag.php');
, 也就是反向读取 flag.php
防止浏览器解析。
那么,我们的最终 payload 就是:
?_POST[flag1]=8gen1&_POST[flag2]=8gen1&1=73797374656d282774616320666c61672e70687027293b
POST:
504[SYS.COM=1&sys=eval(hex2bin($_GET[1]));
文章热度:0次阅读