Tutorials

Server CGI HowTo

Supposed you are running a server, and want to execute unix shell scripts to produce web content to be displayed by a web browser. Then the method of choice is CGI, the Common Gateway Interface.

How CGI Works

The CGI scripts are regulare Unix shell scripts stored on the web server (usually in the cgi/bin directory of the web server’s html root directory) which get invoked by pointing an url at the script.

Then the response visible in the web browser is the output of the script to the standart output stream. This means that the output of the script is transferred by the http protocol from the server to the browser.

Example:

If you want to deliver the uptime as response of the server www.example.com, then you need to copy the following script under the file name “uptime” to the cgi/bin directory of the server and point your browser to www.example.com/cgi/bin/uptime:

#!/bin/bash

echo "Content-type: text/html"
echo ""

echo '<html>'
echo '<head>'
echo '<title>System Uptime</title>'
echo '</head>'
echo '<body>'

  PATH="/bin:/usr/bin"
  export $PATH

echo '<h3>'
  hostname
echo '</h3>'
  uptime

echo '</body>'
echo '</html>'

exit 0

The above shell script needs to be executable:

sudo chmod +x uptime

To see an example cgi run, navigate here:

A CGI example with forms:

#!/bin/bash

echo "Content-type: text/html"
echo ""

echo '<html>'
echo '<head>'
echo '<title>CGI Form Example</title>'
echo '</head>'
echo '<body>'

  PATH="/bin:/usr/bin"
  export $PATH

  # create a html get action for a form
  echo "<form method=GET action=\"${SCRIPT}\">"\
       '<table>'\
       '  <tr><td>Input</TD><TD><input type="text" name="VAL" size=30></td></tr>'\
       '</table>'\
       '<br><input type="submit" value="Submit Form"></form>'

  # make sure we have been invoked properly
  if [ "$REQUEST_METHOD" != "GET" ]; then
     echo "<hr>script error: cannot complete request<hr>"
     exit 1
  fi

  # if no search arguments, exit gracefully
  if [ -z "$QUERY_STRING" ]; then
     exit 0
  else
     # extract the submitted data we are looking for with sed
     VAL=`echo "$QUERY_STRING" | sed -n 's/^.*VAL=\([^&]*\).*$/\1/p' | sed "s/%20/ /g"`

     # display the extracted value below the form
     echo "Value: $VAL"
  fi

echo '</body>'
echo '</html>'

exit 0

The above form contains a single text field. When a user enters string value in that field, the value end up in the VAL shell variable. For this simple demo, we just display the value below the form.

To see an example cgi run, navigate here:

For a more elaborate example, see here:

CGI Security

To avoid cross site scripting, the apache server replaces all meta-characters in the query response string with a hex-code. For example the back-tick “`” is replaced with “%60”. If that was not the case, a fatal exploit is easily constructed:

Consider an input value of “`rm -rf *`” that gets assigned to a variable V in the shell script. When we have a command like ‘echo $$$V’ the “rm” shell command would end up being executed instead of being echoed to the output stream. That would be fatal. So all variables in a shell script need to be quoted for safety reasons like this: ‘echo "$$$V"

As a general rule of thumb, we need to be careful about filtering the input to avoid cross site scripting.

This means that all meta-characters need to be stripped off the input. Or better the other way round: Just do not strip characters that are part of a valid input. For example for an email address as input, one needs to filter all characters but a-z, A-Z, 0-9 and .@_- :

VAL="${VAL/\%40/@}" # undo replacement of @ with %40
VAL=`echo "$VAL" | sed "s/[^a-zA-Z0-9.@_\-]//g"` # remove all meta characters for safety

ShellShock

You may have noticed that the above cgi script utilizes the BASH shell. This shell is vulnerable to the so called ShellShock exploit, which allows to start arbitary programs with root access. To be on the safe side, the command “bash —version” should output a version equal to or higher than 3.2.52 on MacOS X 10.6.8 (may be different for other Linux and MacOS X versions).

You can test, whether or not your bash is vulnerable with the following command line:

env X="() { :;} ; echo vulnerable" /bin/sh -c "echo mac"

If the above line outputs “vulnerable mac”, please follow the patch guide on

  http://readwrite.com/2014/09/26/macs-apple-vulnerable-shellshock-bug-fix-patch

to make your Mac safe again.

Here is the patch procedure for MacOS X 10.6.8 in short:

curl https://opensource.apple.com/tarballs/bash/bash-92.tar.gz | tar zxf -
cd bash-92/bash-3.2
curl https://ftp.gnu.org/pub/gnu/bash/bash-3.2-patches/bash32-052 | patch -p0
sudo xcodebuild
cd ../build/Release
sudo cp bash /bin/
sudo cp sh /bin/

Now the vulnerability test should output:

/bin/sh: warning: X: ignoring function definition attempt
/bin/sh: error importing function definition for `X'
mac

Your Mac is safe!

Options: