Table of Contents
Sneaky Patch — TryHackMe CTF Writeup #
Platform: TryHackMe
Category: Linux Forensics / Kernel
Difficulty: Medium
Date: 2026-03-27
Author: t0nt0n
Reading time: ~5 min
Overview #
A live Linux system is suspected of running a kernel-level backdoor. Traditional detection tools have failed — the intruder has established deep persistence via a malicious Loadable Kernel Module (LKM). Goal: identify the module, understand how it works, and extract the flag.
Reconnaissance #
Identifying the suspicious module #
1lsmod
One module stands out immediately: spatch — named after the challenge "Sneaky Patch", size 12288, with no dependencies and Used by: 0.
1cat /proc/modules | grep spatch
2# spatch 12288 0 - Live 0x0000000000000000 (OE)
The (OE) flag means:
O= Out-of-tree (not part of the official kernel)E= unsigned/externally built
The load address 0x0000000000000000 is also abnormal.
Locating the module file #
1find / -name "spatch.ko" 2>/dev/null
2# /usr/lib/modules/6.8.0-1016-aws/kernel/drivers/misc/spatch.ko
1modinfo /usr/lib/modules/6.8.0-1016-aws/kernel/drivers/misc/spatch.ko
modinfo output
description: Cipher is always root
author: Cipher
license: GPL
name: spatch
vermagic: 6.8.0-1016-aws SMP mod_unload modversions
"Cipher is always root" — the author left a calling card.
Exploitation / Analysis #
Reading kernel symbols #
1grep spatch /proc/kallsyms
Relevant spatch symbols
cipher_bd_init [spatch]
cipher_bd_exit [spatch]
execute_command [spatch]
read_command_output [spatch]
proc_write [spatch]
proc_entry [spatch]
proc_fops [spatch]
The module:
- Creates a
/procentry for communication (proc_entry,proc_fops) - Accepts commands via
proc_write - Executes OS commands as root (
execute_command,read_command_output)
Finding the /proc backdoor #
1find /proc -maxdepth 1 -type f | grep -v "^/proc/[0-9]"
Found: /proc/cipher_bd
Interacting with the backdoor #
Writing commands to /proc/cipher_bd executes them as root, with output going to dmesg:
1echo "id" | sudo tee /proc/cipher_bd
2sudo dmesg | tail -5
[CIPHER BACKDOOR] Module loaded. Write data to /proc/cipher_bd
[CIPHER BACKDOOR] Executing command: id
[CIPHER BACKDOOR] Command Output: uid=0(root) gid=0(root) groups=0(root)
Extracting the flag #
The flag was hardcoded directly in the .ko binary:
1strings /usr/lib/modules/6.8.0-1016-aws/kernel/drivers/misc/spatch.ko | grep -i cipher
Output includes:
Here's the secret: 54484d7b73757033725f736e33346b795f643030727d0a
Decode:
1echo "54484d7b73757033725f736e33346b795f643030727d0a" | xxd -r -p
Flag #
Reveal Flag
THM{sup3r_sn34ky_d00r}
Tools Used #
lsmod//proc/modules— kernel module enumerationmodinfo— module metadatagrep /proc/kallsyms— kernel symbol inspectionstrings— static analysis of the.kobinary/proc/cipher_bd— backdoor interaction interfacedmesg— kernel log output
What Didn't Work #
cat /proc/cipher_bd— returnsInput/output error; the module only supports writes, output goes to dmesg insteadcat /root/flag.txtvia backdoor — file doesn't exist at that path; flag was embedded in the module itselffind /for flag files — unnecessary;stringson the.kowas sufficient
Lessons Learned #
lsmodflags(OE)= out-of-tree + unsigned — always investigate these- Kernel module symbols in
/proc/kallsymsreveal exactly what a module does before you even run it stringson a.kobinary is the fastest first step — authors sometimes hardcode secrets directly- LKM backdoors often create
/procentries as their C2 interface; always check for non-standard/procfiles - A rootkit running as root via kernel module can hook
getdentsto hide files fromls/find— but its own proc interface bypasses those hooks