Avoiding a GET CGI hack attack

Discussion in 'Perl Misc' started by Robert Stelmack, Jul 16, 2004.

  1. I posted this message also at: comp.inforsystems.www.authoring.cgi, but
    after one day it did not show up, so I try here since it both a CGI and a
    Perl question.

    I am trying to make my Perl CGI script secure. Our non-profit web site was
    hacked and the index page changed. This in itself is just an annoyance, but
    it could have just as easily to alter or delete any file.

    The web host system administration was kind enough to look over some logs
    and identified the method used to hack the site. This was do to a "bug" in
    the script ~/www/cgi-bin/index.cgi This CGI script is something I built and
    functionally does what I want it to, but obviously poorly written. I have
    blocked the script for the time being with a chmod 000 to disallow any
    access.

    Here is the way that the system administrator said that the script was
    compromised:

    xxx.xxx.xxx.xxx - - [09/Jul/2004:06:07:43 -0500] "GET
    /cgi-bin/index.cgi?path=|echo%20\"<html><head><title>H@ck3d!</title></head><
    body%20bgcolor=black%20text=yellow><h1>Hacked!%20What%20such%20a%20weak%20si
    te!%20Please%20fix%20this%20as%20soon%20as%20possible!%20Or%20your%20site%20
    will%20be%20more%20vunerable!</h1><center><h3><i>Nowhere%20Man</i></h3></cen
    ter></body></html>\"%20>%20../index.htm|

    He said that I would need additional input checking to prevent this from
    happening again.

    Here is the script, pared down to show what it basically does. Yes, and
    before anyone says anything: it is a crap script, I didn't RTFM, and there
    is a better way to do it ;-)

    #!/usr/bin/perl

    open(STDERR,'>&STDOUT'); ## Assign error messages to STDOUT to view

    $| = 1;

    print "Content-Type: text/html\n\n"; #live feed to browser

    push(@INC, "../"); ## Location of cgi-lib.pl (located in this directory)

    require "cgi-lib.pl" || die "Can't find cgi-bin.pl REASON: $! ";

    ## Looks up the files in the passed directory name with the suffix ".dbf"

    &ReadParse(*input);

    $PATH = @input{'path'}; ## The Name of the DIRECTORY

    print "<html><head><title>Index Page</title></head><body>";

    {foreach $file (<$PATH/*.dbf>)

    {

    $_ = $file;

    print "PWD for files found: $_<br>\n";

    }

    }

    print "</body></html>";

    I have read several Perl and security references and I would like to see if
    this is all I need to do:

    &ReadParse(*input);

    $PATH = @input{'path'}; ## The Name of the DIRECTORY

    if ( $PATH =~ /[`\$\\"';&>]/ ) {die} ## Die if special characters are used

    Not sure if the reg expression is correct, but is this the input checking
    the system administrator was referring to? Or, do you have a good generic
    suggestion to prevent this type of hacking that a simple user could
    implement?

    There were other ways to secure the script, but many of then required system
    administrative access that I don't have.

    I will add the -w -T for the perl execution and the "use strict; use
    warnings;" once I clean up the script and use some of the OOP in the CGI.pm
    module. And I will try to understand the scope (ie. "my variable") and
    properly code the variables.

    I will also add this, as suggested by perlsec:

    delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safe

    ----------------------------------------

    References used:

    http://www.oreilly.com/catalog/cgi2/chapter/ch08.html

    http://search.cpan.org/~jhi/perl-5.8.0/lib/CGI.pm

    perlsec
     
    Robert Stelmack, Jul 16, 2004
    #1
    1. Advertising

  2. Robert Stelmack wrote:
    > I posted this message also at: comp.inforsystems.www.authoring.cgi,
    > but after one day it did not show up,


    Please see http://www.thinkspot.net/ciwac/howtopost.html for info
    about a first time post to ciwac.

    <snip>

    > Here is the script, pared down to show what it basically does.
    > Yes, and before anyone says anything: it is a crap script, I didn't
    > RTFM, and there is a better way to do it ;-)


    Since you realize that, you posted here prematurely. You should have
    made an effort to improve the script first.

    > &ReadParse(*input);
    >
    > $PATH = @input{'path'}; ## The Name of the DIRECTORY


    At this point, $PATH may contain basically anything.

    > {foreach $file (<$PATH/*.dbf>)


    And here you pass $PATH to a construct that invokes system commands.

    This is a good example of what tainted mode is good for. If -T had
    been enabled, Perl would have complained.

    > I have read several Perl and security references and I would like
    > to see if this is all I need to do:
    >
    > &ReadParse(*input);
    >
    > $PATH = @input{'path'}; ## The Name of the DIRECTORY
    >
    > if ( $PATH =~ /[`\$\\"';&>]/ ) {die} ## Die if special characters
    > are used
    >
    > Not sure if the reg expression is correct, but is this the input
    > checking the system administrator was referring to? Or, do you
    > have a good generic suggestion to prevent this type of hacking that
    > a simple user could implement?


    Well, you missed one of the most important advices in "perldoc
    perlsec": "It's better to verify that the variable has only good
    characters (for certain values of ``good'') rather than checking
    whether it has any bad characters. That's because it's far too easy to
    miss bad characters that you never thought of."

    Also, the above does not untaint $PATH.

    What you should have done is ensuring that $PATH only contains 'safe'
    characters, for instance like this:

    if ( $PATH =~ /^([-/\w])$/ ) {
    $PATH = $1;
    } else {
    die "Unsafe character(s) in \"path\"";
    }

    Then you would have been able to run the script in taint mode, and the
    hack you let us know about would not have been possible.

    > I will add the -w -T for the perl execution and the "use strict;
    > use warnings;" once I clean up the script and use some of the OOP
    > in the CGI.pm module. And I will try to understand the scope (ie.
    > "my variable") and properly code the variables.
    >
    > I will also add this, as suggested by perlsec:
    >
    > delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safe


    That sounds good. But why didn't you do that before posting? :)

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
     
    Gunnar Hjalmarsson, Jul 16, 2004
    #2
    1. Advertising

  3. Gunnar Hjalmarsson wrote:
    >
    > if ( $PATH =~ /^([-/\w])$/ ) {


    Make that:

    if ( $PATH =~ /^([-\/\w])$/ ) {
    -----------------------^

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
     
    Gunnar Hjalmarsson, Jul 16, 2004
    #3
  4. Robert Stelmack

    Joe Smith Guest

    Gunnar Hjalmarsson wrote:

    > What you should have done is ensuring that $PATH only contains 'safe'
    > characters, for instance like this:
    >
    > if ( $PATH =~ /^([-\/\w])$/ ) {
    > $PATH = $1;
    > } else {
    > die "Unsafe character(s) in \"path\"";
    > }


    I'd like to point out to Robert that he should be aware of attacks
    that use "../", as in "../../../../../etc/passwd". Since the
    above regex (corrected) does not accept periods, it foils that
    particular attack.
    -Joe
     
    Joe Smith, Jul 16, 2004
    #4
  5. Joe Smith wrote:
    > Gunnar Hjalmarsson wrote:
    >> What you should have done is ensuring that $PATH only contains
    >> 'safe' characters, for instance like this:
    >>
    >> if ( $PATH =~ /^([-\/\w])$/ ) {
    >> $PATH = $1;
    >> } else {
    >> die "Unsafe character(s) in \"path\"";
    >> }

    >
    > I'd like to point out to Robert that he should be aware of attacks
    > that use "../", as in "../../../../../etc/passwd". Since the above
    > regex (corrected) does not accept periods, it foils that particular
    > attack.


    True, but note that the OP has

    foreach $file (<$PATH/*.dbf>)

    , so nothing prevents you from simply stating the path '/etc'. I
    suppose that the best protection in this case against that kind of
    illegitimate access is the file extension '.dbf'.

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
     
    Gunnar Hjalmarsson, Jul 16, 2004
    #5
  6. Robert Stelmack

    krakle Guest

    "Robert Stelmack" <robert.h.stelmack*REMOVE*@boeing.com> wrote in message news:<>...
    > #!/usr/bin/perl


    Always run perl scripts under the 'T' and 'w' switches using the
    strict pragma. Warnigns help too.. It forces you to write "clean" perl
    by scoping variables and prevents you from certain dangerious coding
    methods.

    #!/usr/bin/perl -Tw
    use strict;
    use warnings;

    Ofcourse your script will have to be re-written.. But let's say you
    decide not to do that.. You can patch up that bug with the
    following...

    > {foreach $file (<$PATH/*.dbf>)
    >
    > {
    >
    > $_ = $file;
    >
    > print "PWD for files found: $_<br>\n";
    >
    > }
    >
    > }


    Firstly I would of thought the first line would of caused an error
    since it doesn't exactly go by coding standards... I assume each .dbf
    (DataBase File) located at the path ($path) contain a plaintext
    password and this script loops through the path and displays all
    passwords... IF all the .dbf files are in a single location cut out
    the parameter passed to the script and just use a non-user defined
    variable in the script. IF, the .dbf files are in multiple directories
    (but a set number) just store the paths in a HASH (path as value.. a
    keyname as the name) and pass a parameter through the script wiht just
    the keyname and fetch the path that way. IF the .dbf files are
    dynamically made in random locations you may want to use the "open"
    command and do some regex's on the parameter passed...

    > $PATH = @input{'path'}; ## The Name of the DIRECTORY
    >
    > if ( $PATH =~ /[`\$\\"';&>]/ ) {die} ## Die if special characters are used


    Your script allowes for anyone to execute unix commands.. so it
    seems.. Sure you can STRIP all non A-Z 0-9 characters all you want BUT
    that doesn't stop anyone from using unix commands to delete files...or
    do anything such as 'cat' dbf files to get passwords...
     
    krakle, Jul 16, 2004
    #6
  7. krakle wrote:
    > Your script allowes for anyone to execute unix commands.. so it
    > seems.. Sure you can STRIP all non A-Z 0-9 characters all you want
    > BUT that doesn't stop anyone from using unix commands to delete
    > files...or do anything such as 'cat' dbf files to get passwords...


    Sounds scaring. Could you please show us how you delete a file through

    foreach $file (<$PATH/*.dbf>)

    if $PATH only may contain letters and digits.

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
     
    Gunnar Hjalmarsson, Jul 16, 2004
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Onur Bozkurt

    Attack a page to a mail

    Onur Bozkurt, Jun 30, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    438
    Onur Bozkurt
    Jun 30, 2003
  2. Sati
    Replies:
    6
    Views:
    402
    Dino Chiesa [Microsoft]
    Nov 19, 2003
  3. Zed A. Shaw
    Replies:
    4
    Views:
    193
    Zed A. Shaw
    Oct 25, 2006
  4. James
    Replies:
    1
    Views:
    143
    Nigel Horne
    Aug 4, 2003
  5. brendan

    Avoiding multiple cgi instances

    brendan, Oct 29, 2003, in forum: Perl Misc
    Replies:
    2
    Views:
    126
    Eric J. Roode
    Oct 29, 2003
Loading...

Share This Page