JeecgBoot在国内低代码圈子里用得有多广,做过交付的人心里都有数:后端Spring Boot、前端Vue3、积木报表开箱即用,企业拿来搭管理后台几乎是半天出活。但正因为部署量大、默认配置宽松、外部组件又多,它也是红队和黑产手里“一键getshell”的重灾区。最近我处理了几起JeecgBoot被打穿的应急,攻击者根本没做什么高级操作,就是拿一个漏洞利用工具对着公网IP批量扫,匹配到积木报表或反序列化特征后点几下,一台内网机器就到手了。
这篇文章我不讲怎么用那些工具,而是站在防守视角,把这类“一键getshell工具”背后的利用原理拆开揉碎,再从攻击面梳理、漏洞修复、访问控制、日志检测几个维度给出能直接落地的安全加固实践。无论你是甲方安全工程师、运维,还是正在用JeecgBoot做项目的开发,都可以照着这份清单自查一遍。
1. JeecgBoot为什么总被攻击者盯上:先盘清攻击面
1.1 一个低代码平台的暴露面有多大
JeecgBoot本质上是一个快速开发平台,集成了权限体系、代码生成、报表设计、定时任务、文件管理、在线表单等一大套功能。方便是真的方便,但方便的另一面就是攻击面大。一个典型的JeecgBoot系统,对外暴露的东西远不止登录页,至少包括:
- 用户登录接口和在线API文档(Swagger/Knife4j经常忘关);
- 积木报表(JimuReport)的报表设计、数据源管理、模板相关接口;
- 文件上传、图片预览、附件下载功能;
- 定时任务、消息推送、系统监控等后台功能;
- 底层集成的Shiro权限框架、Fastjson/Jackson序列化组件、WebSocket、Redis等基础组件。
攻击者不需要关心你的业务写得有多好,他们只找“不用登录也能访问”的入口。JeecgBoot最危险的地方恰恰在于:很多接口为了配合报表展示或代码生成,设计时就走的是免鉴权或弱鉴权路径,一旦这些路径带了命令执行或SQL查询能力,那基本等于把服务器钥匙挂在门口。
我在实际巡检中见到过一种典型情况:开发为了图省事,把积木报表模块的接口直接暴露在公网,WAF上也没有加任何针对报表路径的规则。结果就是,别人拿扫描器配一个POC,就能在报表数据源接口传入恶意表达式,直接在服务器上执行系统命令。这个场景在后面我会详细拆。
1.2 按高危到低危排一遍常见攻击面
结合JeecgBoot近几年的公开漏洞和实际攻击工具的关注点,我整理了一张风险清单,排序基本代表了攻击者尝试的先后顺序:
| 攻击面 | 风险等级 | 常见入口/特征 | 被利用后果 |
|---|---|---|---|
| 积木报表模块(JimuReport) | 极高 | /jmreport/路径下的查询、数据源、模板接口 | 未授权远程命令执行,直接getshell |
| 反序列化入口(Shiro/Fastjson等) | 极高 | 登录接口rememberMe、JSON解析参数 | 反序列化RCE,接管服务器 |
| 文件上传 | 高 | 附件上传、头像上传、报表导出导入 | 上传WebShell,获得控制权 |
| SQL注入 | 高 | 报表查询参数、排序字段、数据字典接口 | 拖库、写文件、配合getshell |
| 默认口令/弱口令 | 中高 | 系统管理后台、演示账号、数据库账密 | 低权限登录后提权 |
| 运维组件弱点 | 中 | phpMyAdmin、Redis、Nacos等旁路组件 | 横向移动、写文件getshell |
这张表里把phpMyAdmin单独拎出来,是因为JeecgBoot的部署环境里太常见了:MySQL配一个phpMyAdmin用于运维,密码设成root/123456。攻击者拿到服务器权限后第一件事就是翻配置文件,再通过phpMyAdmin的弱口令或已知漏洞往站点目录写入一句话木马,实现权限维持。所以加固JeecgBoot本身的同时,旁边这些“辅助设施”一定要一起管。
2. “一键getshell”工具是怎么工作的:核心原理拆解
2.1 攻击工具的套路:指纹识别到命令执行的链路
很多刚入门的朋友好奇,所谓的Java反序列化漏洞利用工具v1.7.jar这类工具,到底是怎么做到“一键”的。其实原理不复杂,它们做的事情和漏洞扫描器类似,只是把攻击链条自动化了:
- 第一步,指纹识别。工具会先请求目标站点,通过登录页特征、前端路由、接口路径、返回头中的版本信息,判断目标是不是JeecgBoot,以及大致是哪个版本。
- 第二步,漏洞探测。识别出指纹后,工具会用内置的POC列表去探测已知漏洞。以JeecgBoot为例,重点探测积木报表未授权接口、Shiro反序列化key、常见SQL注入点。
- 第三步,命令执行。只要有一个漏洞探测成功,工具就会在目标服务器上执行一条命令,比如创建管理员账号、下载木马、写入WebShell。这一步通常被包装成“命令执行”或“一键getshell”按钮。
- 第四步,权限维持。getshell之后,工具往往还会自动完成添加计划任务、写入SSH公钥、植入内存马等操作,防止管理员修复后丢失权限。
理解这条链路之后你会发现,“一键getshell”本质上是把一个漏洞的利用细节封装成了点一下就能跑的工具。所以防守的核心不是防工具,而是把每一个可能的漏洞点都补上,让工具探测哪一步都失败。哪怕只堵住了最关键的积木报表和反序列化,攻击者就已经无从下手了。
2.2 积木报表pre-auth RCE为什么是重灾区
在JeecgBoot相关的热搜词里,“积木报表 jeecgboot 3.9.3 pre-auth rce”几乎成了标配。所谓pre-auth RCE,就是无需认证就能达到远程代码执行。这个问题的根源,在于积木报表模块的设计目标:让用户通过在线SQL和模板来设计报表。
具体到我做技术分析时看到的原理,大致是这样一条调用链:
- 积木报表提供了数据源管理、SQL查询、报表模板解析等接口,部分接口没有做严格的登录校验;
- 攻击者访问这些接口时,可以传入SQL语句或模板内容,而报表引擎在处理时会将参数拼接到模板中交给模板引擎解析;
- 模板引擎支持表达式调用,表达式里可以访问对象方法,进而调用
Runtime.getRuntime().exec()这类命令执行方法; - 最终,一个原本用于“根据条件查询报表数据”的请求,变成了在服务器上执行任意命令的入口。
用大白话解释就是:报表模块把“用户输入的模板”当成了“可执行的代码”来渲染,而且这个能力还不要求先登录。攻击者只需要构造一个特殊请求,把命令执行表达式放在参数里,服务器就会老老实实地执行。这也是为什么很多攻击工具扫描到/jmreport路径就直接判定高危,因为一旦存在未授权缺口,利用成本几乎为零。
这里我必须强调一下:积木报表报漏洞不等于JeecgBoot主框架漏洞,但JeecgBoot默认集成并对外暴露了积木报表模块,所以给人的感觉就是“JeecgBoot被打穿”。做修复的时候,两条线要同时走:升级积木报表依赖,同时收紧对外访问。
2.3 反序列化利用工具在JeecgBoot场景里怎么打
反序列化漏洞在Java圈算是老生常谈,JeecgBoot也没能完全避开。它底层使用Shiro做权限控制,Shiro有一个经典的“rememberMe”功能:用户登录后,会在Cookie里写入一段序列化后的身份信息。问题出在两点:
- 如果Shiro的密钥是硬编码或弱密钥,攻击者就可以自己伪造一个rememberMe Cookie;
- 伪造的Cookie里放的是一段精心构造的恶意序列化数据(Gadget链),服务端在反序列化这段数据时,会触发链上的类方法调用,最终执行系统命令。
很多Java反序列化漏洞利用工具的核心能力,就是内置了若干条常见的Gadget链,以及一批已知的Shiro弱密钥。工具扫到目标后,会用弱密钥列表逐个尝试解密流量,再用内置链生成payload。整个过程自动化程度很高,所以看起来就是“一键getshell”。
除了Shiro,JeecgBoot在较老版本里还大量使用了Fastjson做JSON解析,Fastjson的autoType绕过类漏洞也是反序列化工具的重点关注对象。我的建议是:与其一个个追漏洞公告,不如直接把基础组件统一升级到已修复版本,并关闭不必要的反序列化入口。组件升级这件事偷不得懒,因为攻击工具更新POC的速度永远比大部分企业的升级速度快。
3. 从漏洞利用工具到安全加固:可落地的修复与防御方案
3.1 版本先行:先把已知漏洞堵上
安全加固第一步永远是把版本拉上来。JeecgBoot自身的版本迭代很快,积木报表模块也在持续修复漏洞,如果还在用老版本,很多防御措施都等于白做。
参考我经手的几个项目,建议按下表做一次版本核查:
| 组件 | 重点关注 | 加固建议 |
|---|---|---|
| JeecgBoot主框架 | 3.x以下老版本 | 升级到当前稳定版本,并查阅官方升级文档 |
| 积木报表依赖 | 3.9.3及以下版本 | 升级到官方修复后的版本,确认pre-auth RCE点已关闭 |
| Shiro | 1.7及以下版本 | 升级到1.11+,并更换默认rememberMe密钥 |
| Fastjson | 1.2.83以下版本 | 升级到1.2.83及以上,或替换为Jackson |
| 前端依赖 | Vue3/Antd版本老化 | 同步升级前端脚手架,迁移到已修复的依赖版本 |
关于版本升级,我有一个踩过坑的提醒:升级后一定要重启服务并清理临时文件。之前给一个客户升级积木报表,代码包虽然换了,但业务方图省事只热加载了部分模块,老版本的class还在内存里,漏洞照样能打。只有完整重启,新版本才算真正生效。
另外,很多团队在做JeecgBoot升级时会顺手做一次前端改造,比如把Vue3脚手架迁移到Antd4,把老旧的自定义组件替换成官方维护的版本。这一步不仅能改善维护体验,还能顺带修复前端依赖里的XSS等隐患,属于“业务升级与安全加固一起做”的典型操作。
3.2 访问控制与边界收缩
版本修复解决的是“已知漏洞能不能打”的问题,访问控制解决的是“就算有洞,你能不能碰得到”的问题。后者在实际攻防中可能比前者更关键,因为大部分攻击工具是面向公网批量扫描的,一旦目标不出现在公网,扫描器根本无从下手。
我建议从三层做边界收缩:
- 网络层:在云安全组或防火墙层面,把JeecgBoot管理后台、积木报表、API文档等接口的源IP白名单收紧,只允许办公网IP或VPN IP访问。
- 代理层:在Nginx或网关层,对
/jmreport、/sys、/doc.html等敏感路径增加访问控制。如果业务上确实需要公网访问,至少要在反向代理层加一道认证。 - 应用层:关闭Swagger/Knife4j等在线API文档,关闭演示账号和数据初始化功能。
这里给一份Nginx层敏感路径拦截的参考配置:
# 限制后台及报表接口只允许内网IP访问 location ^~ /jmreport/ { allow 10.0.0.0/8; allow 192.168.0.0/16; deny all; proxy_pass http://jeecg-backend; include proxy_params; } location ^~ /sys/ { allow 10.0.0.0/8; allow 192.168.0.0/16; deny all; proxy_pass http://jeecg-backend; include proxy_params; } # 直接拒绝访问在线API文档 location ~ /(doc.html|swagger-ui.html|v2/api-docs) { deny all; return 403; }这份配置并不是银弹,它只解决“源IP不可控”的问题。如果攻击者已经通过WebShell或跳板机拿下了内网某台机器,那么从内网发起请求还是能绕过白名单。所以边界收缩必须和主机安全监测配合使用,不能只做一层。
3.3 应用层加固:最小化功能与默认配置改造
很多JeecgBoot系统被攻破,不是因为漏洞多高级,而是因为“什么功能都开着、什么口令都默认”。应用层加固的核心思路就一句话:把用不到的全关掉,把默认的全改掉。
我的推荐做法至少包括以下几项:
- 禁用或移除积木报表模块。如果业务报表已经迁移或不需要,最彻底的做法是从依赖里排除积木报表,或者在启动配置里关闭报表模块。
- 修改Shiro默认密钥。在配置文件里显式设置一个足够复杂的随机密钥,长度不低于32字节,不要再使用官方示例里的值。
- 清理演示账号和默认账号。很多系统被登录后都是通过演示账号直接进后台,再配合后台功能写文件才getshell。初始化后必须删除或禁用所有演示账号,并强制修改管理员密码。
- 数据库权限最小化。应用连接数据库不要用
root,单独创建一个账号,只授予业务库的增删改查权限,取消FILE、SUPER等敏感权限。 - 文件上传目录禁止执行脚本。在Nginx层或应用层设置上传目录只读,不让
jsp、jspx等脚本文件在该目录运行。 - 定时任务接口加鉴权。JeecgBoot定时任务支持在线编写执行逻辑,如果这个接口能未授权访问,攻击者可以借此直接执行代码,务必保证后台功能都有完整鉴权。
应用层加固是最花时间但也最有效的部分。我见过一个客户,什么组件都不升级,只做了“删除积木报表+改Shiro密钥+上传目录禁执行”这三件事,之后再被攻击工具扫描,扫描结果直接判定为“未发现可用漏洞”。攻击者讲究的是投入产出比,打不进去就会换下一个目标。
3.4 监测与溯源:日志、告警、检测规则
加固做得再完善,也不能保证百分百不出事。安全建设的另一半是监测能力,至少在被打穿之后能及时发现,在攻击者还没拿到核心数据之前切断路径。
先从访问日志入手。建议在Nginx或网关层记录并告警以下特征:
- 路径包含
/jmreport、/api、/sys等敏感关键字,且来源IP为公网地址; - 请求参数中包含
Runtime、exec、ProcessBuilder、Class.forName等命令执行特征; - 请求参数中包含
@type、rmi、ldap、JNDI等反序列化利用特征; - 短时间内来自同一IP的多个接口探测请求。
在WAF或自建日志平台上,可以加类似这样的检测规则:
# 检测报表模块的疑似命令执行请求 if ($request_uri ~* "jmreport.*(Runtime|exec|ProcessBuilder|bash -c|cmd\.exe)") { return 403; } # 检测反序列化利用特征 if ($request_uri ~* "(@type|rmi:|ldap:|jndi:|JdbcRowSetImpl|TemplatesImpl)") { return 403; }主机侧的监测和网络侧同等重要。getshell之后,攻击者通常会留下各种痕迹,常用的排查点包括:
- 最近被修改或新增的
jsp、jspx、war文件; - 定时任务和计划任务里是否有可疑条目;
- 系统用户列表里是否多了不认识的账号;
- 异常的外连IP、回调shell的进程;
- 数据库
general_log或binlog里是否有通过phpMyAdmin执行的异常SQL。
加固的时候我建议把这份清单直接做成一个巡检脚本,定期自动跑一遍,输出新增文件和异常账号。防守方和攻击者拼的就是时间差,越早发现,损失越小。
3.5 升级过程中顺手处理前端技术债
前文提到了Vue3和Antd4,这在JeecgBoot生态里是一个绕不开的话题。很多团队还在用老版本的Vue2脚手架,前端依赖里积累了一批已知的XSS和原型链污染漏洞。攻击者在getshell之前,也会先尝试通过前端漏洞拿到更高权限的Cookie。
我的建议是:借这次安全整改的窗口,把前端脚手架一起升级。JeecgBoot官方已经在Vue3 + Antd4的路线上走了很久,迁移过来之后不仅能修复前端依赖漏洞,还能让后续维护和版本跟进更顺畅。当然,前端迁移是有业务成本的,不能为了安全而无限期停摆,但至少在“已知依赖漏洞清单”里给它排个优先级,别让它一直挂在外面。
4. 实战排查实录:如何确认是否被getshell以及常见误区
4.1 一次典型的JeecgBoot入侵排查流程
接到“服务器异常”告警之后,我一般会按下面的顺序排查,这个流程基本能覆盖大部分JeecgBoot被打穿的场景:
- 第一步,确认系统版本。先查JeecgBoot版本、积木报表版本、Shiro版本,判断是否有已知漏洞与其匹配,这一步既是为了确认攻击路径,也是为了给后续修复定版本基线。
- 第二步,翻访问日志。重点看Nginx或应用日志里,是否有针对
/jmreport、登录接口、上传接口的异常访问记录。日志里出现了exec、Runtime、bash等关键字,基本可以确认攻击入口。 - 第三步,排查主机痕迹。执行
find查找最近新增的jsp/jspx文件,检查crontab计划任务,查看正在运行的Java进程有没有异常子进程,检查/tmp等目录是否有可疑文件。 - 第四步,追踪内网横向。如果数据库主机、Redis主机也在同一内网,还要检查这些组件是否被爆破、是否被写入恶意文件。很多攻击者在拿到应用服务器之后,会立刻翻配置文件里的数据库口令,然后通过phpMyAdmin这类运维入口横向移动。
主机侧的几个常用排查命令,可以直接保存下来:
# 查找近期新增的JSP/WebShell文件 find / -name "*.jsp" -mtime -7 -type f 2>/dev/null # 查看计划任务 crontab -l cat /etc/crontab # 查看可疑进程与外连 ps aux | grep -E "nc |bash|wget|curl" ss -antp | grep ESTABLISHED # 查看新增用户 awk -F: '$3==0 || $3>=1000 {print $1, $3, $6}' /etc/passwd注意一条原则:排查顺序一定是“先保数据、留证据,再处理”。拿到入侵迹象后,先把日志、进程快照、恶意文件备份下来,再清马、修补。很多团队一上来就把木马删了、进程杀了,结果溯源分析时啥都没有,攻击者从哪里进来的、改过什么文件,全成了谜。
4.2 常见误区:只堵一个点远远不够
做安全加固最怕的是“以为自己加固了”。这里列几个我在实际项目中反复见到的误区,每一个都对应着一次真实的事故:
- 误区一:只升级积木报表,不升Shiro。攻击工具是同时探测多个漏洞点的,只堵住报表入口,反序列化入口照样能进来。
- 误区二:只杀木马,不修漏洞。把WebShell删了就觉得安全了,但上传入口还在、弱口令还在,第二天攻击者换个姿势又能进来。
- 误区三:内网系统不需要防护。JeecgBoot很多部署在内网,但内网不等于安全。一旦办公网某台电脑中毒,攻击者会以内网为跳板横向移动,内网系统反而会因为疏于防护而更容易被打穿。
- 误区四:关掉漏洞利用工具就等于安全。工具只是放大器,攻击者手里还有无数种手工利用方式。真正的安全取决于系统本身的暴露面和脆弱性,而不是攻击者手头有什么。
- 误区五:备份等于加固。很多团队有备份机制,但这只是最后一道恢复手段。如果攻击者已经在服务器上驻留了一个月,备份里同样可能有后门。
这些误区归纳起来,就是一个核心问题:把安全当成“单点动作”而不是“体系工程”。真正有效的加固必须覆盖漏洞修复、边界控制、口令管理、日志监测、备份校验全流程。
4.3 事后加固清单速查表
排查和应急处置完成之后,建议按下面的清单逐项落实,然后把每一项的执行结果写进整改报告:
| 动作 | 优先级 | 说明 |
|---|---|---|
| 升级JeecgBoot及积木报表到已修复版本 | 紧急 | 关闭pre-auth RCE等已知漏洞入口 |
| 更换Shiro密钥并升级版本 | 紧急 | 杜绝反序列化伪造Cookie |
| 清理演示账号、修改管理员密码 | 紧急 | 防止登录后台后进一步getshell |
| 修改数据库口令并限制权限 | 高 | 防止拖库和横向移动 |
| 上传目录禁止执行脚本 | 高 | 阻断WebShell上传落地 |
| Nginx/WAF加敏感路径拦截 | 高 | 让攻击工具扫描无法命中 |
| 关闭在线API文档 | 中 | 减小信息泄露面 |
| 清理高危端口和运维组件入口 | 中 | 包括phpMyAdmin、Redis、Nacos等 |
| 日志接入告警平台 | 中 | 保证下次能第一时间感知 |
| 建立定期巡检脚本 | 低 | 持续发现新增文件、异常账号、异常进程 |
这份清单可以直接作为项目整改的验收标准。我在实际交付中就是拿着这张表逐项打勾,凡是能全部通过的,后续被“一键getshell工具”打穿的概率会大幅下降。
从我个人的应急处置经验来看,JeecgBoot被打穿的系统,十个里有七八个都是同一个组合:公网暴露、版本滞后、默认口令没人管。漏洞利用工具只是把这套问题变成了“流水线式”的利用,真正的问题从来都在我们自己的暴露面上。如果你正在维护JeecgBoot,不管是给自己用还是给客户交付,我建议把上面这份加固清单当成上线前的强制检查项。安全这个事,最好的结果就是永远用不上应急响应这套流程。