Perl CGI被黑客入侵?但我正在做一切正确

前端之家收集整理的这篇文章主要介绍了Perl CGI被黑客入侵?但我正在做一切正确前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我刚刚注意到我的一个网页目录中有一些奇怪的PHP文件。他们原来是垃圾邮件发送者的漏洞利用文件

自2006年以来,他们一直在那里,在我使用我的CGI脚本进行高调的捐赠活动的时候。而这些文件被放置在脚本的可写目录中,所以我怀疑我的脚本可能被以某种方式被利用了。

但是我使用Perl“污点检查”,严格等等,我从来没有将查询数据传递给shell(它从不调用shell!)或使用查询数据生成OPEN的文件路径…我只OPEN文件,我直接在脚本中指定。我将查询数据INTO写入文件作为文件内容,但据我所知,这并不危险。

我盯着这些脚本,看不到任何东西,我研究了所有的Perl CGI标准孔。当然,他们可能会以某种方式获得我的主机帐户的密码,但是这些脚本被放置在我的CGI脚本的数据目录中的事实使我怀疑脚本。 (另外,他们得到我的密码“不知何故”是一个非常可怕的解释。)另外,在那段时间,我的日志显示了很多“警告,从非PayPal地址收到的IPN”消息,这些IP来自俄罗斯。所以似乎有人至少TRYING来破解这些脚本。

涉及两个脚本,我将它们粘贴到下面。任何人都可以看到任何可能被利用来写入意外的文件

这是第一个脚本(用于接收PayPal IPN并跟踪捐款,并跟踪哪个站点产生最多捐款):

#!/usr/bin/perl -wT


# Created by Jason Rohrer,December 2005
# Copied basic structure and PayPal protocol code from DonationTracker v0.1


# Script settings



# Basic settings

# email address this script is tracking payments for
my $receiverEmail = "receiver\@yahoo.com"; 

# This script must have write permissions to BOTH of its DataDirectories.
# It must be able to create files in these directories.
# On most web servers,this means the directory must be world-writable.
# (  chmod a+w donationData  )
# These paths are relative to the location of the script.
my $pubDataDirectory =  "../goliath";
my $privDataDirectory = "../../cgi-data/donationNet";

# If this $privDataDirectory setting is changed,you must also change it below
# where the error LOG is opened

# end of Basic settings





# Advanced settings
# Ignore these unless you know what you are doing.



# where the log of incoming donations is stored
my $donationLogFile =   "$privDataDirectory/donationLog.txt";


# location of public data generated by this script
my $overallSumFile =    "$pubDataDirectory/overallSum.html";
my $overallCountFile =  "$pubDataDirectory/donationCount.html";
my $topSiteListFile =       "$pubDataDirectory/topSiteList.html";

# private data tracking which donation total coming from each site
my $siteTrackingFile =  "$privDataDirectory/siteTracking.txt";

# Where non-fatal errors and other information is logged
my $logFile =           "$privDataDirectory/log.txt";



# IP of notify.paypal.com
# used as cheap security to make sure IPN is only coming from PayPal
my $paypalNotifyIP =    "216.113.188.202";



# setup a local error log
use CGI::Carp qw( carpout );
BEGIN {

    # location of the error log
    my $errorLogLocation = "../../cgi-data/donationNet/errors.log";

    use CGI::Carp qw( carpout );
    open( LOG,">>$errorLogLocation" ) or
        die( "Unable to open $errorLogLocation: $!\n" );
    carpout( LOG );
}

# end of Advanced settings


# end of script settings








use strict;
use CGI;                # Object-Oriented CGI library



# setup stuff,make sure our needed files are initialized
if( not doesFileExist( $overallSumFile ) ) {
    writeFile( $overallSumFile,"0" );
}
if( not doesFileExist( $overallCountFile ) ) {
    writeFile( $overallCountFile,"0" );
}
if( not doesFileExist( $topSiteListFile ) ) {
    writeFile( $topSiteListFile,"" );
}
if( not doesFileExist( $siteTrackingFile ) ) {
    writeFile( $siteTrackingFile,"" );
}


# allow group to write to our data files
umask( oct( "02" ) );



# create object to extract the CGI query elements

my $cgiQuery = CGI->new();




# always at least send an HTTP OK header
print $cgiQuery->header( -type=>'text/html',-expires=>'now',-Cache_control=>'no-cache' );

my $remoteAddress = $cgiQuery->remote_host();



my $action = $cgiQuery->param( "action" ) || '';

# first,check if our count/sum is being queried by another script
if( $action eq "checkResults" ) {
    my $sum = readTrimmedFileValue( $overallSumFile );
    my $count = readTrimmedFileValue( $overallCountFile );

    print "$count \$$sum";
}
elsif( $remoteAddress eq $paypalNotifyIP ) {

    my $donorName;


    # $customField contains URL of site that received donation
    my $customField = $cgiQuery->param( "custom" ) || '';

    # untaint and find whitespace-free string (assume it's a URL)
    ( my $siteURL ) = ( $customField =~ /(\S+)/ );

    my $amount = $cgiQuery->param( "mc_gross" ) || '';

    my $currency = $cgiQuery->param( "mc_currency" ) || '';

    my $fee = $cgiQuery->param( "mc_fee" ) || '0';

    my $date = $cgiQuery->param( "payment_date" ) || '';

    my $transactionID = $cgiQuery->param( "txn_id" ) || '';


    # these are for our private log only,for tech support,etc.
    # this information should not be stored in a web-accessible
    # directory
    my $payerFirstName = $cgiQuery->param( "first_name" ) || '';
    my $payerLastName = $cgiQuery->param( "last_name" ) || '';
    my $payerEmail = $cgiQuery->param( "payer_email" ) || '';


    # only track US Dollars 
    # (can't add apples to oranges to get a final sum)
    if( $currency eq "USD" ) {

    my $status = $cgiQuery->param( "payment_status" ) || '';

    my $completed = $status eq "Completed";
    my $pending = $status eq "Pending";
    my $refunded = $status eq "Refunded";

    if( $completed or $pending or $refunded ) {

        # write all relevant payment info into our private log
        addToFile( $donationLogFile,"$transactionID  $date\n" . 
               "From: $payerFirstName $payerLastName " .
               "($payerEmail)\n" .
               "Amount: \$$amount\n" .
               "Fee: \$$fee\n" .
               "Status: $status\n\n" );                    

        my $netDonation;

        if( $refunded ) {
        # subtract from total sum

        my $oldSum = 
            readTrimmedFileValue( $overallSumFile );

        # both the refund amount and the
        # fee on the refund are now reported as negative
        # this changed as of February 13,2004
        $netDonation = $amount - $fee;
        my $newSum = $oldSum + $netDonation;

        # format to show 2 decimal places
        my $newSumString = sprintf( "%.2f",$newSum );

        writeFile( $overallSumFile,$newSumString );


        my $oldCount = readTrimmedFileValue( $overallCountFile );
        my $newCount = $oldCount - 1;
        writeFile( $overallCountFile,$newCount );

        }

        # This check no longer needed as of February 13,2004
        # since now only one IPN is sent for a refund.
        #  
        # ignore negative completed transactions,since
        # they are reported for each refund (in addition to 
        # the payment with Status: Refunded)
        if( $completed and $amount > 0 ) {
        # fee has not been subtracted yet
        # (fee is not reported for Pending transactions)

        my $oldSum = 
            readTrimmedFileValue( $overallSumFile );
                $netDonation = $amount - $fee;
        my $newSum = $oldSum + $netDonation;

        # format to show 2 decimal places
        my $newSumString = sprintf( "%.2f",$newSumString );

        my $oldCount = readTrimmedFileValue( 
                             $overallCountFile );
        my $newCount = $oldCount + 1;
        writeFile( $overallCountFile,$newCount );
        }

        if( $siteURL =~ /http:\/\/\S+/ ) {
        # a valid URL

        # track the total donations of this site
        my $siteTrackingText = readFileValue( $siteTrackingFile );
        my @siteDataList = split( /\n/,$siteTrackingText );
        my $newSiteData = "";
        my $exists = 0;
        foreach my $siteData ( @siteDataList ) {
            ( my $url,my $siteSum ) = split( /\s+/,$siteData );
            if( $url eq $siteURL ) {
            $exists = 1;
            $siteSum += $netDonation;
            }
            $newSiteData = $newSiteData . "$url $siteSum\n";
        }

        if( not $exists ) {
            $newSiteData = $newSiteData . "$siteURL $netDonation";
        }

        trimWhitespace( $newSiteData );

        writeFile( $siteTrackingFile,$newSiteData );

        # now generate the top site list

        # our comparison routine,descending order
        sub highestTotal {
            ( my $url_a,my $total_a ) = split( /\s+/,$a );
            ( my $url_b,my $total_b ) = split( /\s+/,$b );
            return $total_b <=> $total_a;
        }

        my @newSiteDataList = split( /\n/,$newSiteData );

        my @sortedList = sort highestTotal @newSiteDataList;

        my $listHTML = "<TABLE BORDER=0>\n";
        foreach my $siteData ( @sortedList ) {
            ( my $url,$siteData );

            # format to show 2 decimal places
            my $siteSumString = sprintf( "%.2f",$siteSum );

            $listHTML = $listHTML .
            "<TR><TD><A HREF=\"$url\">$url</A></TD>".
            "<TD ALIGN=RIGHT>\$$siteSumString</TD></TR>\n";
        }

        $listHTML = $listHTML . "</TABLE>";

        writeFile( $topSiteListFile,$listHTML );

        }


    }
    else {
        addToFile( $logFile,"Payment status unexpected\n" );
        addToFile( $logFile,"status = $status\n" );
    }
    }
    else {
    addToFile( $logFile,"Currency not USD\n" );
    addToFile( $logFile,"currency = $currency\n" );
    }
}
else {
    # else not from paypal,so it might be a user accessing the script
    # URL directly for some reason


    my $customField = $cgiQuery->param( "custom" ) || '';
    my $date = $cgiQuery->param( "payment_date" ) || '';
    my $transactionID = $cgiQuery->param( "txn_id" ) || '';
    my $amount = $cgiQuery->param( "mc_gross" ) || '';

    my $payerFirstName = $cgiQuery->param( "first_name" ) || '';
    my $payerLastName = $cgiQuery->param( "last_name" ) || '';
    my $payerEmail = $cgiQuery->param( "payer_email" ) || '';


    my $fee = $cgiQuery->param( "mc_fee" ) || '0';
    my $status = $cgiQuery->param( "payment_status" ) || '';

    # log it
    addToFile( $donationLogFile,"WARNING:  got IPN from unexpected IP address\n" .
           "IP address:  $remoteAddress\n" .
           "$transactionID  $date\n" . 
           "From: $payerFirstName $payerLastName " .
           "($payerEmail)\n" .
           "Amount: \$$amount\n" .
           "Fee: \$$fee\n" .
           "Status: $status\n\n" );

    # print an error page
    print "Request blocked.";
}



##
# Reads file as a string.
#
# @param0 the name of the file.
#
# @return the file contents as a string.
#
# Example:
# my $value = readFileValue( "myFile.txt" );
##
sub readFileValue {
    my $fileName = $_[0];
    open( FILE,"$fileName" ) 
        or die( "Failed to open file $fileName: $!\n" );
    flock( FILE,1 ) 
        or die( "Failed to lock file $fileName: $!\n" );

    my @lineList = <FILE>;

    my $value = join( "",@lineList );

    close FILE;

    return $value;
}



##
# Reads file as a string,trimming leading and trailing whitespace off.
#
# @param0 the name of the file.
#
# @return the trimmed file contents as a string.
#
# Example:
# my $value = readFileValue( "myFile.txt" );
##
sub readTrimmedFileValue {
    my $returnString = readFileValue( $_[0] );
    trimWhitespace( $returnString );

    return $returnString;
}



##
# Writes a string to a file.
#
# @param0 the name of the file.
# @param1 the string to print.
#
# Example:
# writeFile( "myFile.txt","the new contents of this file" );
##
sub writeFile {
    my $fileName = $_[0];
    my $stringToPrint = $_[1];

    open( FILE,">$fileName" ) 
        or die( "Failed to open file $fileName: $!\n" );
    flock( FILE,2 ) 
        or die( "Failed to lock file $fileName: $!\n" );

    print FILE $stringToPrint;

    close FILE;
}



##
# Checks if a file exists in the filesystem.
#
# @param0 the name of the file.
#
# @return 1 if it exists,and 0 otherwise.
#
# Example:
# $exists = doesFileExist( "myFile.txt" );
##
sub doesFileExist {
    my $fileName = $_[0];
    if( -e $fileName ) {
        return 1;
    }
    else {
        return 0;
    }
}



##
# Trims any whitespace from the beginning and end of a string.
#
# @param0 the string to trim.
##
sub trimWhitespace {   

    # trim from front of string
    $_[0] =~ s/^\s+//;

    # trim from end of string
    $_[0] =~ s/\s+$//;
}



##
# Appends a string to a file.
#
# @param0 the name of the file.
# @param1 the string to append.
#
# Example:
# addToFile( "myFile.txt","the new contents of this file" );
##
sub addToFile {
    my $fileName = $_[0];
    my $stringToPrint = $_[1];

    open( FILE,">>$fileName" ) 
        or die( "Failed to open file $fileName: $!\n" );
    flock( FILE,2 ) 
        or die( "Failed to lock file $fileName: $!\n" );

    print FILE $stringToPrint;

    close FILE;
}



##
# Makes a directory file.
#
# @param0 the name of the directory.
# @param1 the octal permission mask.
#
# Example:
# makeDirectory( "myDir",oct( "0777" ) );
##
sub makeDirectory {
    my $fileName = $_[0];
    my $permissionMask = $_[1];

    mkdir( $fileName,$permissionMask );
}

而且,这里有一些冗余(对于…完整性呢?),但这是第二个脚本(用于生成网站HTML按钮,人们可以添加到自己的网站):

#!/usr/bin/perl -wT


# Created by Jason Rohrer,December 2005


# Script settings



# Basic settings

my $templateFile = "buttonTemplate.html";

# end of Basic settings





# Advanced settings
# Ignore these unless you know what you are doing.

# setup a local error log
use CGI::Carp qw( carpout );
BEGIN {

    # location of the error log
    my $errorLogLocation = "../../cgi-data/donationNet/errors.log";

    use CGI::Carp qw( carpout );
    open( LOG,">>$errorLogLocation" ) or
        die( "Unable to open $errorLogLocation: $!\n" );
    carpout( LOG );
}

# end of Advanced settings


# end of script settings








use strict;
use CGI;                # Object-Oriented CGI library


# create object to extract the CGI query elements

my $cgiQuery = CGI->new();




# always at least send an HTTP OK header
print $cgiQuery->header( -type=>'text/html',-Cache_control=>'no-cache' );


my $siteURL = $cgiQuery->param( "site_url" ) || '';

print "Paste this HTML into your website:<BR>\n";

print "<FORM><TEXTAREA COLS=40 ROWS=10>\n";

my $buttonTemplate = readFileValue( $templateFile );

$buttonTemplate =~ s/SITE_URL/$siteURL/g;

# escape all tags
$buttonTemplate =~ s/&/&amp;/g;
$buttonTemplate =~ s/</&lt;/g;
$buttonTemplate =~ s/>/&gt;/g;


print $buttonTemplate;

print "\n</TEXTAREA></FORM>";




##
# Reads file as a string.
#
# @param0 the name of the file.
#
# @return the file contents as a string.
#
# Example:
# my $value = readFileValue( "myFile.txt" );
##
sub readFileValue {
    my $fileName = $_[0];
    open( FILE,$permissionMask );
}

解决方法

我以前看过类似的东西。在我们的例子中,我很确定黑客在库中使用了一个尚未更新的缓冲区溢出。他们然后可以使用PHP shell来写入服务器文件

很可能问题不在你的代码中。更经常地更新您的软件会使攻击不太可能,但不幸的是,无法完全防范黑客攻击。有可能他们正在扫描旧版软件中的常见漏洞。

猜你在找的Perl相关文章