Forum Moderators: coopster

Message Too Old, No Replies

PHP mail form security problem?

My host says that this code is being abused....

         

Drylouvre

2:44 pm on Sep 8, 2006 (gmt 0)

10+ Year Member



Hi,

I've been using this code (or similar) for a while now on several sites. Today, my host switched one site off saying the this code is being exploited. I can see no reference to this in the site logs and have no problems with it on other sites.

Can anybody see anything obviously insecure with this:


<?PHP

session_start();
error_reporting(7);

#----------
# Set Variables

$formfailed = false;
$_SESSION['user'] = $_POST['user'];
$_SESSION['email'] = $_POST['email'];
$_SESSION['telephone'] = $_POST['telephone'];
$_SESSION['address'] = $_POST['address'];
$_SESSION['how'] = $_POST['how'];
$_SESSION['message'] = $_POST['message'];
$_SESSION['submit'] = $_POST['submit'];
$_SESSION['error'] = '';
$_SESSION['formfailed'] = false;

#----------
# Validate: String

function check_string($value, $low, $high, $mode, $optional)
{
if ( (strlen($value) == 0) && ($optional === true) ) {
return true;
} elseif ( (strlen($value) >= $low) && ($mode == 1) ) {
return true;
} elseif ( (strlen($value) <= $high) && ($mode == 2) ) {
return true;
} elseif ( (strlen($value) >= $low) && (strlen($value) <= $high) && ($mode == 3) ) {
return true;
} else {
return false;
}
}
#----------
# Validate: Email

function check_email($email, $optional)
{
if ( (strlen($email) == 0) && ($optional === true) ) {
return true;
} elseif ( eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$", $email) ) {
return true;
} else {
return false;
}
}

#-----------
function spamcheck($teststr) {
if (eregi('mime-version',$teststr) ¦¦ eregi('content-type',$teststr) ¦¦ eregi('bcc:',$teststr)) {

$emailBody = "Name: " . $_POST['user'] . "\n"
. "Email: " . $_POST['email'] . "\n"
. "Phone: " . $_POST['telephone'] . "\n"
. "Address: " . $_POST['address'] . "\n"
. "Wedding: " . $_POST['wedding'] . "\n"
. "Commercial: " . $_POST['commercial'] . "\n"
. "Lifestyle: " . $_POST['lifestyle'] . "\n"
. "Brochure: " . $_POST['brochure'] . "\n"
. "How Did You Hear About Us " . $_POST['how'] . "\n"
. "Message: " . $_POST['message'] . "\n"
. "\n"
. "--\n"
. "" . $_SERVER['HTTP_USER_AGENT'] . "\n"
. "" . $_SERVER['REMOTE_ADDR'] . "\n"
. "" . date('Y-m-d H:i:s') . "\n";

$emailHeader = "From: server@domain.co.uk\n"
. "Reply-To: nobody@nobody.com\n"
. "MIME-Version: 1.0\n"
. "Content-type: text/plain; charset=\"ISO-8859-1\"\n"
. "Content-transfer-encoding: quoted-printable\n";

mail('me@mydomain.co.uk','Attempted Hack', $emailBody, $emailHeader);
header ("Location: http://www.domain.co.uk/index.php");
exit;
}
}

#--------------------------------------------------------

# RegisterGlobals OFF

$FTGuser = $_POST['user'];
$FTGemail = $_POST['email'];
$FTGtelephone = $_POST['telephone'];
$FTGaddress = $_POST['address'];
$FTGwedding = $_POST['wedding'];
$FTGcommercial = $_POST['commercial'];
$FTGlifestyle = $_POST['lifestyle'];
$FTGbrochure = $_POST['brochure'];
$FTGhow = stripslashes($_POST['how']);
$FTGmessage = stripslashes($_POST['message']);

# Check inputs

spamcheck($FTGuser);
spamcheck($FTGemail);
spamcheck($FTGtelephone);
spamcheck($FTGaddress);
spamcheck($FTGwedding);
spamcheck($FTGcommercial);
spamcheck($FTGlifestyle);
spamcheck($FTGbrochure);
spamcheck($FTGhow);
spamcheck(FTGmessage);

# Fields Validations

$validationFailed = false;

if ( (! check_string($FTGuser, 1, 200, 3, false))) {
$validationFailed = true;
$_SESSION['error'] = 'Your name must be between 1 and 200 characters long';
}

else if ( (! check_email($FTGemail, false))) {
$validationFailed = true;
$_SESSION['error'] = 'Your email address is invalid';
}

else if ( (! check_string($FTGtelephone, 1, 200, 3, true))) {
$validationFailed = true;
$_SESSION['error'] = 'Telephone number can not be longer than 20 characters';
}

else if( (! check_string($FTGaddress, 1, 200, 3, true))) {
$validationFailed = true;
$_SESSION['error'] = 'Address can not be longer than 200 characters';
}

else if( (! check_string($FTGhow, 1, 200, 3, true))) {
$validationFailed = true;
$_SESSION['error'] = 'The How Did You.. field can not be longer than 200 characters';
}

else if ( (! check_string($FTGmessage, 1, 1000, 3, true))) {
$validationFailed = true;
$_SESSION['error'] = 'Your message but can not be longer than 1000 characters';
}

# Redirect user to the error page

if ($validationFailed == true) {

$_SESSION['formfailed'] = true;
header("Location: ../contact.php");
exit;

}

# Email to Form Owner

$emailTo = '"Owner Name" <owner@domain.co.uk>';

$emailSubject = "Contact Form Submission";

$emailBody = "A form has just been submitted:\n"
. "\n"
. "Name: $FTGuser\n"
. "Email: $FTGemail\n"
. "Telephone: $FTGtelephone\n"
. "Address: $FTGaddress\n"
. "Wedding: $FTGwedding\n"
. "Commercial: $FTGcommercial\n"
. "Lifestyle: $FTGlifestyle\n"
. "Brochure: $FTGbrochure\n"
. "How Did You Hear About Us?: $FTGhow\n"
. "Message: $FTGmessage\n"
. "\n"
. "--\n"
. "This is for information only:\n"
. "" . date('Y-m-d H:i:s') . "\n"
. "" . $_SERVER['REMOTE_ADDR'] . "\n"
. "" . $_SERVER['HTTP_USER_AGENT'] . "\n"
. "\n"
. "";

$emailHeader = "From: owner@domain.co.uk\n"
. "Reply-To: owner@domain.co.uk\n"
. "MIME-Version: 1.0\n"
. "Content-type: text/plain; charset=\"ISO-8859-1\"\n"
. "Content-transfer-encoding: quoted-printable\n";

mail($emailTo, $emailSubject, $emailBody, $emailHeader);

# Confirmation Email to User

$confEmailTo = $FTGemail;

$confEmailSubject = "Thank You";

$confEmailBody = "Dear $FTGuser\n"
. "\n"
. "Thank you for contacting Owner Website. I'll get back to you as soon as possible.\n"
. "\n"
. "Regards\n"
. "\n"
. "Owner";

$confEmailHeader = "From: owner@domain.co.uk\n"
. "Reply-To: owner@domain.co.uk\n"
. "MIME-Version: 1.0\n"
. "Content-type: text/plain; charset=\"ISO-8859-1\"\n"
. "Content-transfer-encoding: quoted-printable\n";

mail($confEmailTo, $confEmailSubject, $confEmailBody, $confEmailHeader);

# Redirect user to success page

header("Location: ../contact.php");
exit;

# End of PHP script
?>

Your help would be much appreciated.

Cheers

whoisgregg

4:36 pm on Sep 8, 2006 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Just off the top of my head, I'd say this line should include at least one more header:

if (eregi('mime-version',$teststr) ¦¦ eregi('content-type',$teststr) ¦¦ eregi('bcc:',$teststr) [b]¦¦ eregi('cc:',$teststr)[/b]) {

By emailing the "injection attempt" to yourself, you are still allowing the injection attack to go through. It should really be logged to a DB or txt file if you want to see the attack, then an email sent to you that includes no user input with a generic "Caught another one! Go check the blah, blah place."

That's an awful lot of code to chew through though, so I didn't do a comprehensive analysis. Hope this helps... :)

Drylouvre

6:11 pm on Sep 8, 2006 (gmt 0)

10+ Year Member



Hi,

Yeah, it does. Thanks. I will add that other CC check to the list. How does emailing the attempted attack to me actually make the code less secure though? I don't understand that bit.

Cheers

whoisgregg

7:32 pm on Sep 8, 2006 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



First, I assume the reason for which the script was disabled was because it was being used as a spam relay. A contact form can be turned into a spam relay through header injection.

PHP header injection attacks work when the user provides headers which are passed, unfiltered, to the mail function.

So, whether you address the mail function to send the "payload" of the attack to yourself or to someone else, the attack still occurs.

A recent discussion (and a thread from the library [webmasterworld.com]) I highly recommend for anyone experiencing the problem:

Combatting Webform hijack [webmasterworld.com]
"My webform has been hijacked. Any suggestions how to make this form secure?"

Drylouvre

4:57 pm on Sep 10, 2006 (gmt 0)

10+ Year Member



Thanks for your help.