通过PHP表单进行网络钓鱼攻击


Phishing attack through PHP form

我在一个网站上有两个表单的问题。由于"网络钓鱼攻击",承载表单的页面已经被我的主机删除了好几次。

页面上的表单是使用'PHPFormGenerator'构建的,这是由托管公司(iPage)提供的软件。

我已经确保我按照建议使用最新版本的"PHPFormGenerator",但再次将页面取下。我在表单上安装了"验证码",但页面又被删除了。

我目前已经从页面中删除了表单,并将其替换为电子邮件链接。

预订表单页面代码:

   <div class="formRow">
      <div class="formLeft">Name<span class="red">*</span></div>
      <div class="formRight">
        <input type=text name='Name' size=40>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Telephone<span class="red">*</span></div>
      <div class="formRight">
        <input type=text name='Telephone' size=40>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Email<span class="red">*</span></div>
      <div class="formRight">
        <input type=text name='Email' size=40>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Address1<span class="red">*</span></div>
      <div class="formRight">
        <input type=text name='Address1' size=40>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Address2<span class="red">*</span></div>
      <div class="formRight">
        <input type=text name='Address2' size=40>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Address3</div>
      <div class="formRight">
        <input type=text name='Address3' size=40>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Address4</div>
      <div class="formRight">
        <input type=text name='Address4' size=40>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Zip / Post code<span class="red">*</span></div>
      <div class="formRight">
        <input type=text name='PostCode' size=40>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Arrival<span class="red">*</span></div>
      <div class="formRight">
        <input type=text name='Arrival' size=15 id="datepicker">
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Departure<span class="red">*</span></div>
      <div class="formRight">
        <input type=text name='Departure' size=15 id="datepicker2">
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">GroupNumber<span class="red">*</span></div>
      <div class="formRight">
        <select name='GroupNumber'>
          <option value='01'>01</option>
          <option value='02'>02</option>
          <option value='03'>03</option>
          <option value='04'>04</option>
          <option value='05'>05</option>
          <option value='06'>06</option>
        </select>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Reason</div>
      <div class="formRight">
        <select name='Reason'>
          <option value='Vacation'>Vacation</option>
          <option value='Business'>Business</option>
          <option value='Education'>Education</option>
          <option value='Other'>Other</option>
        </select>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Transfer</div>
      <div class="formRight">
        <input type=checkbox name='Transfer'>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">CarRental</div>
      <div class="formRight">
        <input type=checkbox name='CarRental'>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Taxi</div>
      <div class="formRight">
        <input type=checkbox name='Taxi'>
      </div>
    </div>
    <div class="formRow">
      <div class="formLeft">Additional</div>
      <div class="formRightBox">
        <textarea name='Additional' rows=7 cols=31></textarea>
      </div>
    </div>
    <div class="formRow">
                 <div class="formLeft"> </div>
                    <div class="formRightCaptcha">
                         <?php
                              require_once('recaptchalib.php');
                              $publickey = "6LfgGOASAAAAAOQ-oVZNlo8HmxR4GadyY4h5c0hY"; // you got this from the signup page
                              echo recaptcha_get_html($publickey);
                            ?>
                    </div>
    </div>
    <div class="formRow">
      <div class="formLeft"> </div>
      <div class="formRight">
        <input type=submit value='Submit Form'>
        &nbsp;
        <input type=reset value='Reset Form'>
      </div>
    </div>
  </div>

Verify.php

    <?php
  require_once('recaptchalib.php');
  $privatekey = "***PRIVATEKEYREMOVED***";
  $resp = recaptcha_check_answer ($privatekey,
                                $_SERVER["REMOTE_ADDR"],
                                $_POST["recaptcha_challenge_field"],
                                $_POST["recaptcha_response_field"]);
  if (!$resp->is_valid) {
    // What happens when the CAPTCHA was entered incorrectly
    die ("The reCAPTCHA wasn't entered correctly. Go back and try it again." .
         "(reCAPTCHA said: " . $resp->error . ")");
  } else {
    // Your code here to handle a successful verification
    include("global.inc.php");
$errors=0;
$error="The following errors occured while processing your form input.<ul>";
pt_register('POST','Name');
pt_register('POST','Telephone');
pt_register('POST','Email');
pt_register('POST','Address1');
pt_register('POST','Address2');
pt_register('POST','Address3');
pt_register('POST','Address4');
pt_register('POST','PostCode');
pt_register('POST','Arrival');
pt_register('POST','Departure');
pt_register('POST','GroupNumber');
pt_register('POST','Reason');
pt_register('POST','Transfer');
pt_register('POST','CarRental');
pt_register('POST','Taxi');
pt_register('POST','Additional');
$Additional=preg_replace("/('015'012)|('015)|('012)/","&nbsp;<br />", $Additional);if($Name=="" || $Telephone=="" || $Email=="" || $Address1=="" || $Address2=="" || $PostCode=="" || $Arrival=="" || $Departure=="" || $GroupNumber=="" ){
$errors=1;
$error.="<li>You did not enter one or more of the required fields. Please go back and try again.";
}
if(!eregi("^[a-z0-9]+([_''.-][a-z0-9]+)*" ."@"."([a-z0-9]+(['.-][a-z0-9]+)*)+"."''.[a-z]{2,}"."$",$Email)){
$error.="<li>Invalid email address entered";
$errors=1;
}
if($errors==1) echo $error;
else{
$where_form_is="http".($HTTP_SERVER_VARS["HTTPS"]=="on"?"s":"")."://".$SERVER_NAME.strrev(strstr(strrev($PHP_SELF),"/"));
$message="A booking enquiry has been made by ".$Name."
Telephone: ".$Telephone."
Email: ".$Email."
Address1: ".$Address1."
Address2: ".$Address2."
Address3: ".$Address3."
Address4: ".$Address4."
PostCode: ".$PostCode."
Arrival: ".$Arrival."
Departure: ".$Departure."
GroupNumber: ".$GroupNumber."
Reason: ".$Reason."
Transfer: ".$Transfer."
CarRental: ".$CarRental."
Taxi: ".$Taxi."
Additional: ".$Additional."
";
$message = stripslashes($message);
$subject = 'Louis Villa booking enquiry';
$headers = 'From:' .$Name. ' made a booking enquiry' . "'r'n" . // <- change your email here
        'Reply-To:' . $Email . "'r'n";
mail("info@louisvilla.com",$subject,$message,$headers);
header("Location:http://louisvilla.com/thankyou.html");
exit();
}
  }
?>

Global.inc.php

<?php
function pt_register()
{
  $num_args = func_num_args();
   $vars = array();
   if ($num_args >= 2) {
       $method = strtoupper(func_get_arg(0));
       if (($method != 'SESSION') && ($method != 'GET') && ($method != 'POST') && ($method != 'SERVER') && ($method != 'COOKIE') && ($method != 'ENV')) {
           die('The first argument of pt_register must be one of the following: GET, POST, SESSION, SERVER, COOKIE, or ENV');
     }
       $varname = "HTTP_{$method}_VARS";
      global ${$varname};
       for ($i = 1; $i < $num_args; $i++) {
           $parameter = func_get_arg($i);
           if (isset(${$varname}[$parameter])) {
               global $$parameter;
               $$parameter = ${$varname}[$parameter];
          }
       }
   } else {
       die('You must specify at least two arguments');
   }
}
?>

recaptchalib.php

<?php
define("RECAPTCHA_API_SERVER", "http://www.google.com/recaptcha/api");
define("RECAPTCHA_API_SECURE_SERVER", "https://www.google.com/recaptcha/api");
define("RECAPTCHA_VERIFY_SERVER", "www.google.com");
/**
 * Encodes the given data into a query string format
 * @param $data - array of string elements to be encoded
 * @return string - encoded request
 */
function _recaptcha_qsencode ($data) {
        $req = "";
        foreach ( $data as $key => $value )
                $req .= $key . '=' . urlencode( stripslashes($value) ) . '&';
        // Cut the last '&'
        $req=substr($req,0,strlen($req)-1);
        return $req;
}

/**
 * Submits an HTTP POST to a reCAPTCHA server
 * @param string $host
 * @param string $path
 * @param array $data
 * @param int port
 * @return array response
 */
function _recaptcha_http_post($host, $path, $data, $port = 80) {
        $req = _recaptcha_qsencode ($data);
        $http_request  = "POST $path HTTP/1.0'r'n";
        $http_request .= "Host: $host'r'n";
        $http_request .= "Content-Type: application/x-www-form-urlencoded;'r'n";
        $http_request .= "Content-Length: " . strlen($req) . "'r'n";
        $http_request .= "User-Agent: reCAPTCHA/PHP'r'n";
        $http_request .= "'r'n";
        $http_request .= $req;
        $response = '';
        if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) {
                die ('Could not open socket');
        }
        fwrite($fs, $http_request);
        while ( !feof($fs) )
                $response .= fgets($fs, 1160); // One TCP-IP packet
        fclose($fs);
        $response = explode("'r'n'r'n", $response, 2);
        return $response;
}

/**
 * Gets the challenge HTML (javascript and non-javascript version).
 * This is called from the browser, and the resulting reCAPTCHA HTML widget
 * is embedded within the HTML form it was called from.
 * @param string $pubkey A public key for reCAPTCHA
 * @param string $error The error given by reCAPTCHA (optional, default is null)
 * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false)
 * @return string - The HTML to be embedded in the user's form.
 */
function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false)
{
    if ($pubkey == null || $pubkey == '') {
        die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
    }
    if ($use_ssl) {
                $server = RECAPTCHA_API_SECURE_SERVER;
        } else {
                $server = RECAPTCHA_API_SERVER;
        }
        $errorpart = "";
        if ($error) {
           $errorpart = "&amp;error=" . $error;
        }
        return '<script type="text/javascript" src="'. $server . '/challenge?k=' . $pubkey . $errorpart . '"></script>
    <noscript>
        <iframe src="'. $server . '/noscript?k=' . $pubkey . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
        <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
        <input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
    </noscript>';
}


/**
 * A ReCaptchaResponse is returned from recaptcha_check_answer()
 */
class ReCaptchaResponse {
        var $is_valid;
        var $error;
}

/**
  * Calls an HTTP POST function to verify if the user's guess was correct
  * @param string $privkey
  * @param string $remoteip
  * @param string $challenge
  * @param string $response
  * @param array $extra_params an array of extra variables to post to the server
  * @return ReCaptchaResponse
  */
function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array())
{
    if ($privkey == null || $privkey == '') {
        die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
    }
    if ($remoteip == null || $remoteip == '') {
        die ("For security reasons, you must pass the remote ip to reCAPTCHA");
    }

        //discard spam submissions
        if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) {
                $recaptcha_response = new ReCaptchaResponse();
                $recaptcha_response->is_valid = false;
                $recaptcha_response->error = 'incorrect-captcha-sol';
                return $recaptcha_response;
        }
        $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify",
                                          array (
                                                 'privatekey' => $privkey,
                                                 'remoteip' => $remoteip,
                                                 'challenge' => $challenge,
                                                 'response' => $response
                                                 ) + $extra_params
                                          );
        $answers = explode ("'n", $response [1]);
        $recaptcha_response = new ReCaptchaResponse();
        if (trim ($answers [0]) == 'true') {
                $recaptcha_response->is_valid = true;
        }
        else {
                $recaptcha_response->is_valid = false;
                $recaptcha_response->error = $answers [1];
        }
        return $recaptcha_response;
}
/**
 * gets a URL where the user can sign up for reCAPTCHA. If your application
 * has a configuration page where you enter a key, you should provide a link
 * using this function.
 * @param string $domain The domain where the page is hosted
 * @param string $appname The name of your application
 */
function recaptcha_get_signup_url ($domain = null, $appname = null) {
    return "https://www.google.com/recaptcha/admin/create?" .  _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname));
}
function _recaptcha_aes_pad($val) {
    $block_size = 16;
    $numpad = $block_size - (strlen ($val) % $block_size);
    return str_pad($val, strlen ($val) + $numpad, chr($numpad));
}
/* Mailhide related code */
function _recaptcha_aes_encrypt($val,$ky) {
    if (! function_exists ("mcrypt_encrypt")) {
        die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed.");
    }
    $mode=MCRYPT_MODE_CBC;   
    $enc=MCRYPT_RIJNDAEL_128;
    $val=_recaptcha_aes_pad($val);
    return mcrypt_encrypt($enc, $ky, $val, $mode, "'0'0'0'0'0'0'0'0'0'0'0'0'0'0'0'0");
}

function _recaptcha_mailhide_urlbase64 ($x) {
    return strtr(base64_encode ($x), '+/', '-_');
}
/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */
function recaptcha_mailhide_url($pubkey, $privkey, $email) {
    if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) {
        die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " .
             "you can do so at <a href='http://www.google.com/recaptcha/mailhide/apikey'>http://www.google.com/recaptcha/mailhide/apikey</a>");
    }

    $ky = pack('H*', $privkey);
    $cryptmail = _recaptcha_aes_encrypt ($email, $ky);
    return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail);
}
/**
 * gets the parts of the email to expose to the user.
 * eg, given johndoe@example,com return ["john", "example.com"].
 * the email is then displayed as john...@example.com
 */
function _recaptcha_mailhide_email_parts ($email) {
    $arr = preg_split("/@/", $email );
    if (strlen ($arr[0]) <= 4) {
        $arr[0] = substr ($arr[0], 0, 1);
    } else if (strlen ($arr[0]) <= 6) {
        $arr[0] = substr ($arr[0], 0, 3);
    } else {
        $arr[0] = substr ($arr[0], 0, 4);
    }
    return $arr;
}
/**
 * Gets html to display an email address given a public an private key.
 * to get a key, go to:
 *
 * http://www.google.com/recaptcha/mailhide/apikey
 */
function recaptcha_mailhide_html($pubkey, $privkey, $email) {
    $emailparts = _recaptcha_mailhide_email_parts ($email);
    $url = recaptcha_mailhide_url ($pubkey, $privkey, $email);
    return htmlentities($emailparts[0]) . "<a href='" . htmlentities ($url) .
        "' onclick='"window.open('" . htmlentities ($url) . "', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;'" title='"Reveal this e-mail address'">...</a>@" . htmlentities ($emailparts [1]);
}

?>

任何帮助都会很感激。我一直在联系我的托管公司寻求支持,因为表格是用他们推荐的软件构建的,他们提供的唯一帮助是去一个在线论坛寻求帮助。

我以前创建过许多"联系表单"等,从来没有遇到过这个问题。

提前感谢您的帮助。

问题是你发布的代码很容易通过邮件头被黑客攻击。

此代码的意图是它将始终向您发送电子邮件,并且仅向您发送电子邮件,但是您的电子邮件标题可能会被操纵,从而使电子邮件被重写为意想不到的内容并发送给任意收件人。

换句话说,对于知道如何攻击它的人来说,你已经有了一个开放的电子邮件系统,可以完全匿名地向任何人发送任何东西。

问题的核心在这里:

$headers = 'From:' .$Name. ' made a booking enquiry' . "'r'n" .
        'Reply-To:' . $Email . "'r'n";

$Name$Email都可以通过注入额外的'r'n序列和其他头来攻击系统。

您需要对这些字段和您可能使用的任何其他标头进行消毒,以防止此类攻击。

这种事情暴露了PHP内置mail()函数的弱点。使用此函数编写易受攻击的邮件程序非常容易。

我的建议是使用一个更好的邮件发送器类,如phpMailer,而不是PHP内置的mail()函数。

phpMailer为您完成了所有艰苦的工作,包括过滤输入并使攻击者难以入侵您的系统。是的,您可以自己修改代码来处理所有这些问题,但这需要大量的工作,而且您永远无法完全确定您已经涵盖了所有内容。所以使用phpMailer这样的类,因为它们已经为你完成了所有的工作。

基本上,一旦你安装了phpMailer,你会替换这段代码:
$subject = 'Louis Villa booking enquiry';
$headers = 'From:' .$Name. ' made a booking enquiry' . "'r'n" .
        'Reply-To:' . $Email . "'r'n";
mail("info@louisvilla.com",$subject,$message,$headers);

…用这个:

require 'class.phpmailer.php';
$mail = new PHPMailer;
$mail->From = $Email;
$mail->FromName = $Name;
$mail->AddAddress('info@louisvilla.com');
$mail->Subject = $subject;
$mail->Body    = $message;
if(!$mail->Send()) {
   echo 'Message could not be sent.';
   echo 'Mailer Error: ' . $mail->ErrorInfo;
   exit;
}

代码更整洁,更安全。一个全方位的胜利。

希望对你有帮助。

如果一家主机公司因为"网络钓鱼攻击"(声称你很容易受到攻击)而删除内容,你可以抛弃他们,或者起诉他们是不负责任的主机。网络钓鱼是任何网站都容易受到攻击的东西;这是对人类的攻击,而不是一个网站。

与社会工程类似,你唯一能做的就是教育用户。(注意:你也可以使用带有正确DKIM/SPF记录的签名电子邮件,Gmail也不会轻易将你标记为垃圾邮件,但这是你可以去的极限)

另外(虽然这部分应该在代码审查的理想情况下)

  • 您正在混合$_SERVER(这是正确的)和$HTTP_SERVER_VARS(已被弃用多年并将E_DEPRECATED抛出错误日志)。
  • 你似乎已经全球化了HTTP_*_VARS;这是不必要的,因为$_POST/GET/SESSION/...都是超全局变量。
  • 您的电子邮件正则表达式无效;regex不能验证电子邮件(电子邮件语法不是正则的,因此正则表达式不能100%工作)。
  • 你没有防御CSRF。请看Jeff的博客文章。

我能想到的唯一会导致你的主人合法地删除内容的事情是,如果他们认为你是一个网络钓鱼,但是考虑到他们在论坛上寻求帮助的反应,我想说他们只是非常不专业。

编辑:在我因为没有看到@Spudley看到的东西而打了自己一巴掌之后,似乎人们一直在使用你的脚本向人们发送网络钓鱼攻击。您的托管公司知道您的脚本正在发送它们,但只是删除了它们,并且没有告诉您它正在被利用(并且可能没有假设善意)