shell.sh

Résumé

This write-up covers the discovery of a hidden vhost (grafana.planning.htb), exploitation of a Grafana vulnerability (CVE-2024-9264), obtaining a reverse shell, credential recovery, SSH connection to enzo, and then escalation to root via a vulnerable cron.

Recon — nmap

We do an nmap to scan service on 10.10.11.68

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 62:ff:f6:d4:57:88:05:ad:f4:d3:de:5b:9b:f8:50:f1 (ECDSA)
|_ 256 4c:ce:7d:5c:fb:2d:a0:9e:9f:bd:f5:5c:5e:61:50:8a (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://planning.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Add the output on /etc/hosts

10.10.11.68 planning.htb

Discovering a hidden vhost with ffuf

We do a FUZZ to looking for another DNS

┌──(kali㉿kali)-[~]
└─$ ffuf -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -u
'http://planning.htb' -H "Host:FUZZ.planning.htb" -fs 178
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://planning.htb
:: Wordlist : FUZZ:
/usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt
:: Header : Host: FUZZ.planning.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 178
________________________________________________
grafana [Status: 302, Size: 29, Words: 2, Lines: 3, Duration: 68ms]

Explanation:

The -fs 178 option filters responses of size 178 (default page returned for non-existent hosts).

The server responds differently for the “grafana” subdomain.

Result:

An active vhost has been discovered: grafana.planning.htb

The HTTP response has a 302 code (redirection), which indicates the presence of an accessible service behind this vhost

grafana.planning.htb (302) — add on /etc/hosts.

Exploitation — Grafana (CVE-2024-9264)

Check the exposed web page.

Identify the version of Grafana and search for any known vulnerabilities (LFI, RCE, default credentials).

We find a public exploit for CVE-2024-9264 allowing RCE via a crafted payload.

Contenu du fichier /var/lib/grafana/.env montrant les credentials

We make a git clone of the git project on our attacker machine kali

Then we create shell.sh and mount a python http.server 8000

shell.sh

Then we create a listening port on port 4444 and then we launch the exploit

nc

Rev shell

rev-shell

Then we try to found creds or sensitive information

After a large énumeration i found creds of enzo on /var/lib/grafana env

# Example location found:
/var/lib/grafana/.env
# read
cat /var/lib/grafana/.env
creds

SSH → enzo

Then we connect on ssh enzo

creds

And found the user flag

creds

? How it’s work — CVE-2024-9264 (Grafana)

Grafana's experimental SQL Expressions feature allows the evaluation of duckdb queries containing user input.

These queries are not sufficiently sanitized before being passed to duckdb, resulting in command injection and a local file inclusion vulnerability. Any user with VIEWER privileges or higher is capable of executing this attack.

The duckdb binary must be present in Grafana's $PATH for this attack to work; by default, this binary is not installed in Grafana distributions.

Le PoC abuse de la source de données « Expression » de Grafana (DuckDB dans le backend) : il envoie une expression SQL (via l'API POST /api/ds/query?ds_type=__expr__&expression=true) que Grafana évalue avec DuckDB.

Deux mécanismes sont utilisés :

  • read_blob(path) (DuckDB + extension) pour lire un fichier distant et renvoyer son contenu.
  • installation et chargement de l’extension shellfs puis usage de read_csv(“... |”) pour exécuter une commande et rediriger sa sortie vers un fichier temporaire, que le PoC relit ensuite via read_blob

So the exploit flow: inject a malicious DuckDB expression → DuckDB (on the server) performs operations on the FS / loads extension → the attacker reads the result via the JSON response.

Sources: Grafana advisory, NVD, public proof of concept, and analyses

Escalation → root via Cron

Now we do a privilege escalation on root user

Then After a large énumeration i found password on /otp/crontabs/crontab.db

creds

I also found a open port on 127.0.0.1:8000

creds

I am doing port forwarding.

creds

Then i connect on it http://127:0.0.1:8000 with creds wich i found before

creds

Now I am on the last step, which is to create a new cronjob to escalate to the root user.

creds

The job added in the Crontab interface executes the following command:

 cp /bin/bash /tmp/bash && chmod u+s /tmp/bash

This command copies the bash executable to the /tmp directory, then applies the SUID bit to this binary. Since the cron job runs with root privileges, the new /tmp/bash file belongs to root and is marked SUID

Consequence: any user can run /tmp/bash -p and obtain a shell with root privileges, allowing complete privilege escalation on the system

Then i run it

And do ./bash -p to get root bash and found root flag

creds

Conclusion & recommandations

Update Grafana and apply patches related to CVE-2024-9264.

Affected versions:

  • Grafana OSS and Enterprise versions 11.0.0 - 11.0.5, 11.1.0 - 11.1.6, and 11.2.0 - 11.2.1.

Corrected versions:

  • 11.0.5+security-01 and higher

Grafana has released special versions to fix this vulnerability. To analyze the patch, the following commands can be used to compare the changes:

git checkout v11.0.5+security-01 
git diff 0421a8911cfc05a46c516fd9d033a51e52e51afe 70316b3e1418c9054017047e63c1c96abb26f495

Cela révèle que la fonctionnalité Expressions SQL a simplement été supprimée des versions vulnérables.

+++ b/pkg/expr/sql/db.go
@@ -0,0 +1,26 @@
+package sql
+
+import (
+       "errors"
+
+       "github.com/grafana/grafana-plugin-sdk-go/data"
+)
+
+type DB struct {
+}
+
+func (db *DB) TablesList(rawSQL string) ([]string, error) {
+       return nil, errors.New("not implemented")
+}
+
+func (db *DB) RunCommands(commands []string) (string, error) {
+       return "", errors.New("not implemented")
+}
+
+func (db *DB) QueryFramesInto(name string, query string, frames []*data.Frame, f *data.Frame) error {
+       return errors.New("not implemented")
+}
+
+func NewInMemoryDB() *DB {
+       return &DB{}
+}
@@ -85,7 +84,7 @@ func (gr *SQLCommand) Execute(ctx context.Context, now time.Time, vars mathexp.V
 
        rsp := mathexp.Results{}
 
-       duckDB := duck.NewInMemoryDB()
+       duckDB := sql.NewInMemoryDB()
        var frame = &data.Frame{}
        err := duckDB.QueryFramesInto(gr.refID, gr.query, allFrames, frame);
        if err != nil {

The patch completely removes SQL expressions, preventing any possibility of exploitation.

Additional recommendations:

  • Avoid storing credentials in plain text in accessible folders.
  • Restrict the local interface and access to 127.0.0.1 via firewall/bound interfaces.
  • Avoid cron jobs written via the web interface without strict validation.