Skip to content

Instantly share code, notes, and snippets.

@stokito
Last active May 22, 2024 12:51
Show Gist options
  • Save stokito/a9a2732ffc7982978a16e40e8d063c8f to your computer and use it in GitHub Desktop.
Save stokito/a9a2732ffc7982978a16e40e8d063c8f to your computer and use it in GitHub Desktop.
CGI shell scripts samples

CGI samples

CGI Variables

Standard set of Common Gateway Interface environment variable are described in RFC3875. For example:

CONTENT_TYPE=application/x-www-form-urlencoded
GATEWAY_INTERFACE=CGI/1.1
REMOTE_ADDR=192.168.1.180
QUERY_STRING=Zbr=1234567&SrceMB=&ime=jhkjhlkh+klhlkjhlk+%A9%D0%C6%AE%C6%AE&prezime=&sektor=OP
REMOTE_PORT=2292
CONTENT_LENGTH=128
REQUEST_URI=/cgi-bin/printenvs
SERVER_SOFTWARE=busybox httpd/1.35 6-Oct-2004
PATH=/bin:/sbin:/usr/bin:/usr/sbin
HTTP_REFERER=http://192.168.1.1/index1.html
SERVER_PROTOCOL=HTTP/1.0
PATH_INFO=
REQUEST_METHOD=POST
PWD=/www/cgi-bin
SERVER_PORT=80
SCRIPT_NAME=/cgi-bin/printenvs
REMOTE_USER=[http basic auth username]

Example of CGI script that prints them /cgi-bin/printenvs.cgi Environment variables are set up and the script is invoked with pipes for stdin/stdout.

Other samples

httpd expects it's CGI script files to be in the subdirectory cgi-bin under main web directory set by options -h (default is /www, so /www/cgi-bin). The CGI script files must also have permission to be executed (min mode 700) e.g chmod +x /cgi-bin/index.cgi. If directory URL is given, no index.html is found and CGI support is enabled, then cgi-bin/index.cgi will be executed.

BusyBox sources contains two useful CGI programs:

Also there is and example of shell script to process File Upload httpd_post_upload.cgi

#!/bin/sh
# Echo CGI envs as headers and body
CONTENT=$(cat -)
printf "Content-Length: ${#CONTENT}\r\n"
printf "Content-Type: text/html\r\n"
printf "ENV_REQUEST_METHOD: \"$REQUEST_METHOD\"\r\n"
printf "ENV_CONTENT_TYPE: \"$CONTENT_TYPE\"\r\n"
printf "ENV_CONTENT_LENGTH: \"$CONTENT_LENGTH\"\r\n"
printf "ENV_REMOTE_ADDR: \"$REMOTE_ADDR\"\r\n"
printf "ENV_REMOTE_PORT: \"$REMOTE_PORT\"\r\n"
printf "ENV_SERVER_PORT: \"$SERVER_PORT\"\r\n"
printf "ENV_REQUEST_URI: \"$REQUEST_URI\"\r\n"
printf "ENV_QUERY_STRING: \"$QUERY_STRING\"\r\n"
# all headers from request now available with the HTTP_ prefix
printf "ENV_HTTP_HOST: \"$HTTP_HOST\"\r\n"
printf "ENV_HTTP_USER_AGENT: \"$HTTP_USER_AGENT\"\r\n"
printf "ENV_HTTP_ACCEPT: \"$HTTP_ACCEPT\"\r\n"
printf "ENV_HTTP_REFERER: \"$HTTP_REFERER\"\r\n"
# username from basic auth (e.g. without password)
printf "ENV_REMOTE_USER: \"$REMOTE_USER\"\r\n"
printf "ENV_SCRIPT_NAME: \"$SCRIPT_NAME\"\r\n"
printf "ENV_PATH_INFO: \"$PATH_INFO\"\r\n"
printf "ENV_PATH: \"$PATH\"\r\n"
printf "ENV_PWD: \"$PWD\"\r\n"
printf "ENV_SERVER_PROTOCOL: \"$SERVER_PROTOCOL\"\r\n"
printf "ENV_SERVER_SOFTWARE: \"$SERVER_SOFTWARE\"\r\n"
printf "\r\n"
printf "$CONTENT"
#!/usr/bin/perl
use strict;
use warnings;
print "Content-type: text/html\r\n";
print "\r\n";
my $name = '';
if ($ENV{QUERY_STRING}) {
($name) = $ENV{QUERY_STRING} =~ /^name=(.*)$/;
}
print "Hello $name\n";
#!/bin/sh
# Imitation of webdav to read and edit a file
if [ "$REMOTE_USER" != "admin" ]; then
printf "Status: 403\r\n"
printf "\r\n"
printf "Only admin can change users but you are %s" "$REMOTE_USER"
exit
fi
if [ "$REQUEST_METHOD" = "GET" ]; then
printf "Content-Type: text/plain\r\n"
printf "\r\n"
cat /etc/hosts
elif [ "$REQUEST_METHOD" = "PUT" ]; then
CONTENT=$(cat -)
printf "%s" "$CONTENT" > /etc/hosts
printf "Status: 204\r\n"
printf "\r\n"
else
printf "Status: 405\r\n"
fi
#!/usr/bin/python
print("Content-type:text/html\r\n")
print("\r\n")
print('<html>')
print('<head>')
print('<title>Hello from CGI in Python</title>')
print('</head>')
print('<body>')
print('<h1>Hello World!</h2>')
print('</body>')
print('</html>')
#!/bin/sh
# GPLv2. From BuxyBox https://git.busybox.net/busybox/tree/networking/httpd_post_upload.cgi
# post_upload.htm example:
# <html>
# <body>
# <form action=/cgi-bin/httpd_post_upload.cgi method=post enctype=multipart/form-data>
# File to upload: <input type=file name=file1> <input type=submit>
# </form>
# POST upload format:
# -----------------------------29995809218093749221856446032^M
# Content-Disposition: form-data; name="file1"; filename="..."^M
# Content-Type: application/octet-stream^M
# ^M <--------- headers end with empty line
# file contents
# file contents
# file contents
# ^M <--------- extra empty line
# -----------------------------29995809218093749221856446032--^M
file=$(mktemp)
CR=`printf '\r'`
# CGI output must start with at least empty line (or headers)
printf '\r\n'
IFS="$CR"
read -r delim_line
IFS=""
while read -r line; do
test x"$line" = x"" && break
test x"$line" = x"$CR" && break
done
cat >"$file"
# We need to delete the tail of "\r\ndelim_line--\r\n"
tail_len=$((${#delim_line} + 6))
# Get and check file size
filesize=`stat -c"%s" "$file"`
test "$filesize" -lt "$tail_len" && exit 1
# Check that tail is correct
dd if="$file" skip=$((filesize - tail_len)) bs=1 count=1000 >"$file.tail" 2>/dev/null
printf "\r\n%s--\r\n" "$delim_line" >"$file.tail.expected"
if ! diff -q "$file.tail" "$file.tail.expected" >/dev/null; then
printf "<html>\n<body>\nMalformed file upload"
exit 1
fi
rm "$file.tail"
rm "$file.tail.expected"
# Truncate the file
dd of="$file" seek=$((filesize - tail_len)) bs=1 count=0 >/dev/null 2>/dev/null
printf "<html>\n<body>\nFile upload has been accepted"
#!/bin/bash
# list files in a directory. Please not that it needs for Bash
printf "Content-Type: text/html\r\n"
printf "\r\n"
echo "<p>List of files in </strong><code>/</code></p>"
files=($(ls /))
for file in "${files[@]}"
do
echo "<code>$file</code><br>"
done
#!/bin/sh
# print all env variables
printf "Content-Type: text/plain\r\n"
printf "\r\n"
echo "Environment variables:"
env
#!/bin/sh
# Redirect from HTTP to HTTPS
printf "Status: 302 Redirect\r\n"
printf "Location: https://$SERVER_NAME:$SERVER_PORT/\r\n"
printf "\r\n"
#!/bin/sh
printf "Content-Type: text/html\r\n"
printf "\r\n"
cat <<EOF
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CGI render HTML sample</title>
</head>
<body>
<h1>Hello from CGI</h1>
</body>
</html>
EOF
#!/bin/sh
# Slowly read and write back the request
printf "Content-Type: text/plaint\r\n"
printf "\r\n"
set | while read line; do echo $line; sleep 1; done
@nebuxadnezzar
Copy link

nebuxadnezzar commented Apr 24, 2024

here is my version of index.cgi

#!/bin/bash +x

folder=${REQUEST_URI#${SCRIPT_NAME}}
echo "Content-Type: text/html; charset=UTF-8"
echo ""
cat  <<EOF
<!doctype html>
<html lang="en-US">
<head>
<meta http-equiv="Pragma" content="no-cache">
<style>
table {
    font-size: 18px;
    }
.fc{padding-left: 10px}
</style>
</head>
<body>
EOF

echo '<h2>'
if [ ! -z "$folder" -a "$folder" != " " -a "$folder" != '/' ]; then
    echo "Index of $folder<br/>"
    f=${REQUEST_URI##*/}
    printf "<a href=\"%s\">../</a>\n" ${REQUEST_URI%/${f}}
else
    echo "Index of /"
    folder=""
fi;

echo '</h2>'

dirs=( $(find ..${folder} -maxdepth 1 -mindepth 1 -type d -print | sort) )
echo '<table>'
for ((i = 0; i < ${#dirs[@]}; i++ )) ;do
    d=${dirs[$i]}
    n=${d#..${folder}}
    d=`echo $d|sed -E "s|\.\.+|$SCRIPT_NAME|g"`
    printf "<tr><td><a href=\"%s\">%s</a></tr></td>\n" $d $n
done
echo '</table>'

files=( $(find ..${folder} -maxdepth 1 -mindepth 1 -not -type d -print | sort) )
echo '<table>'
for ((i = 0; i < ${#files[@]}; i++ )) ;do
    f=${files[$i]}
    #echo $f
    n=${f#..${folder}/}
    printf "<tr><td class=\"fc\"><a href=\"${folder}/%s\">%s</a></tr></td>\n" $n $n
done
cat <<EOF
 </table>
 </body>
 </html>
EOF

@nebuxadnezzar
Copy link

some improvement to printenv.cgi

#!/bin/sh
# print all env variables
printf "Content-Type: text/html\r\n"
printf "\r\n"
echo "Environment variables:"
echo "<pre>"
env | sort
echo "</pre>"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment