Hi, applefritters. For what it’s worth, I think it’s great what you’re trying to do: reproducing a vulnerability so that after you change your code, you can verify the vulnerability no longer exists. Otherwise, how could you tell whether your implementation is safe or whether you’re just not exploiting the vulnerability correctly.
Here’s a simple PHP mail form that would be vulnerable.
<?php if ($_SERVER['REQUEST_METHOD'] === 'GET'): ?>
<h1>Contact me</h1>
<form action="" method="post">
<label>Your name: <textarea name="name"></textarea></label>
<label>Your email: <textarea name="email"></textarea></label>
<label>Your message: <textarea name="message"></textarea></label>
<input type="submit">
</form>
<?php elseif ($_SERVER['REQUEST_METHOD'] === 'POST'): ?>
<?php
$success = mail(
'you@example.com', // to
"{$_POST['name']} is contacting you", // subject
$_POST['message'], // message
"From: {$_POST['email']}" // additional headers
);
if ($success) {
echo 'Mail sent!';
} else {
echo 'Something went wrong.';
}
?>
<?php endif ?>
There are several ways you could craft your inputs to be malicious. One simple way is in the email field. You could enter a value such as:
me@example.com
To: other@example.com
It starts with the normal email value, which gets used in the From field, then a newline to start a new header. After that newline, you have full control of the email. You can set any header field as well as the body message, which means you can exploit this form to send spam to anyone you want.
The solution is to escape special characters. Every output destination has a different set of special characters. If outputting to HTML, you escape angle brackets and quotes, because those are the characters special to HTML. If outputting to SQL, you escape quotes and backslashes. If shell command or JSON, then you escape the special characters of those respective destinations. And so on.
For MIME headers, the newline itself is a special character, among others.
Your options are 1) read rfc2822 very carefully so you can escape correctly, or 2) use a library. I strongly suggest the latter. There are several to choose from, but my top choice is SwiftMailer.
Here’s the same script from above but rewritten using Swift.
<?php if ($_SERVER['REQUEST_METHOD'] === 'GET'): ?>
<h1>Contact me</h1>
<form action="" method="post">
<label>Your name: <textarea name="name"></textarea></label>
<label>Your email: <textarea name="email"></textarea></label>
<label>Your message: <textarea name="message"></textarea></label>
<input type="submit">
</form>
<?php elseif ($_SERVER['REQUEST_METHOD'] === 'POST'): ?>
<?php
require_once __DIR__.'/swiftmailer/lib/swift_required.php';
$message = Swift_Message::newInstance()
->setSubject("{$_POST['name']} is contacting you")
->setFrom($_POST['email'])
->setTo('you@example.com')
->setBody($_POST['message'])
;
$transport = Swift_SendmailTransport::newInstance(ini_get('sendmail_path'));
$mailer = Swift_Mailer::newInstance($transport);
$nSuccessfulRecipients = $mailer->send($message);
if ($nSuccessfulRecipients) {
echo 'Mail sent!';
} else {
echo 'Something went wrong.';
}
?>
<?php endif ?>
Even though we’re still using user inputs directly, Swift keeps you safe from injection.