Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
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.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

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

CVE-2022-46169

Step-by-step analysis of CVE-2022-46169: unauthenticated remote code execution in Cacti via authentication bypass and command injection, with Docker lab setup and exploitation walkthrough.

View Repository
172 years agoNot yet reviewed

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 - 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:

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:

-   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.

Download Tool