As the author of the feedback module, I am planning to phase it out for 4.7 in favor of the contact module which is part of core.
Since I had to fix a nasty spam exploit vulnerability in feedback, as detailed here http://drupal.org/node/29927, I am porting the fix to contact as well.
As I noted in the above issue, the fix is a bit over aggressive, but guaranteed to work. The error message is intentionally ambiguous so as not to give any clue to attackers.
I also added some minor modifications, tidying up the code a bit, and giving the user a status message if they Send a Copy to themselves.
Comments welcome, and I hope to see this in core.
Comments
Comment #1
kbahey commentedThis version adds a watchdog message detailing the relay attempt.
Comment #2
morbus iffLooks fine to me - my only concern was case sensitivity ("To:" vs. "to:"), but that's handled nicely by the stristr call.
Comment #3
saerdna commentedhmm why cant we just use the captcha module?
Comment #4
kbahey commentedThe topic of captcha has been discussed several times.
Here is the short version:
1. Captchas are not fool proof
2. Captchas are a problem for people with disabilities
So, the general feeling is not to use them.
Whatever the case may be, we need a fix against this exploit, and mine is proven to work (a feedback form that uses the same technique was probed twice and failed).
Comment #5
killes@www.drop.org commentedPatch still applies, I think it is needed. it looks ok, but I did not test it.
Comment #6
killes@www.drop.org commentedmarked http://drupal.org/node/32107 a duplicate of this. Settign to critical.
Comment #7
dries commentedCode does not look good, IMO. Please review more carefully.
Code does not escape data properly (vulnerable to XSS attacks): use theme('placeholder') to escape data shown to the user. This also makes error messages themable and thus more consistent.
The form's error handling should use form_set_error() rather than drupal_set_message() + drupal_goto().
The contact_check_exploit() function should not take an array; it should check individual fields so it is easy to reuse that function. It makes too many assumptions now.
The contact_check_exploit() function needs PHPdoc to describe the exploit. The function itself could do with a more descriptive name. 'check exploit' is like 'handle data'; non-descriptive.
Comment #8
Steve Dondley commentedHmm, I'm no hacker but I tried putting something like "blah%0a%0dBcc: myemail@address.com" into the subject field and was not able to do the e-mail injection. Perhaps something in the Forms API automtically converts these characters over? Or maybe I'm not doing email injection properly?
Comment #9
chx commentedFirst, this patch now needs a reroll .
Second. If the problem is with line feeds, then instead of the complicated check, blow away \r and \n in all header fields and be done.
str_replace(array('\n', '\r'), array(), $header)Comment #10
Steve Dondley commentedIn an effort to learn more about this attack, I wrote a Perl script to inject a \nBcc: into the subject. It worked. However, the mime_header_encode function effectively blocked this exploit on my server by converting the subject line to UTF-8. See the user_mail function.
But other drupal installations that have the smtp_library installed will bypass the mime_header_encode function. However, I can't find anywhere where the smtp_library variable is set. Is the code at the top of user_mail used?
Comment #11
Steve Dondley commentedOK, after looking at this problem, here's what I've concluded. Please let me know if you disagree:
1) Malicious user can only inject malicious code through the subject field in the contact form.
2) Any new lines in the subject field will get stripped out via the mime_header_encode function for many Drupal users.
3) Other Drupal users who have their own smtp_library are still vulnerable.
4) To solve the problem for folks with their own smtp_library, a simple one liner to strip the subject of newlines will do. Maybe a watchdog entry would be desirable, also.
Comment #12
chx commentedGreat work! Steve, congratulations for the deep analysis. Could you maybe provide a patch? :)
Comment #13
Steve Dondley commentedOK, patch attached.
Comment #14
tangent commentedMy (custom) contact form just got hit by a spammer using this exploit yesterday. I applied Steve's changes on my site but I'm not certain they did the trick. After I reenabled the module I got another email from the spammer. I'm now using a rewriterule to block him so I can't really test further.
In any case, let's make sure this fix lands by 4.7.
Comment #15
Steve Dondley commentedtangent,
The patch doesn't stop spam to you. It stops your site from spammers who will try to spam others through your site.
Comment #16
tangent commentedI knew that but I guess I didn't think it through. Sorry for the false alarm.
Comment #17
dries commentedIs that rewrite rule worth adding to our .htaccess file?
Comment #18
tangent commentedThat is probably debatable. I have a rule with a bunch of conditions that block certain user-agent strings.
This spammer was using a blank user-agent string so I had to add the first condition. This rule is likely one that all sites need but it could cause problems for some if enabled by default.
Comment #19
morbus iffDries - no, it wouldn't be. For one, those sorts of lists need constant maintenance, and are out of date quite frequently. For another, it gives the illusion that Drupal is "complete" in its bot blockage, when that will never be the case: many many bots fake the referrers of legitimate UAs or, for that matter, don't send a referer at all.
Comment #20
chx commentedPatch applies, needed and solves the problem. See #9-#13 for a good review of the problem and the solution.
Comment #21
dries commentedOh, I expected a one or two line patch against .htaccess. Clearly this is not an option.
As for the patch, should we only check the subject? How does this compare with known injection preventions mechanisms?
Comment #22
tangent commentedApologies to Steve and everyone for dragging this out.
After taking another look at this I decided that it would be far more elegant to use the new formsapi to validate the input instead of handling this in the submit. That way, users who somehow add a linebreak in the subject (possible with copy/paste in some user agents) would get a nice warning. Even better though, any offending messages would be completely blocked instead of sent through to the contact with a watchdog warning.
I've created a new patch following adrian's recent commit.
I moved the 'mail' check outside of the category check in contact_mail_page_validate() because it seemed an unnecessary dependency but I might have been in error. I've tested this patch with both /contact and /user/1/contact though and it seems fine. Please test this at your earliest convenience.
On a sidenote, I also discovered that the email validation regexpr accepts addresses of the form x@y which seems like a bad address to me (unless you are only using drupal on an intranet or single system perhaps).
Comment #23
tangent commentedIf anyone has the ability to test this form by posting actual offending values that is what is really needed. I don't have the capability to do so very easily.
Comment #24
tangent commentedOk. I did a real test of an injection attack against the form with my latest patch and it was successful.
@Dries: Here is a decent explanation of the issue and how to work around it. http://securephp.damonkohler.com/index.php/Email_Injection. The synopsis is that detecting and preventing the exploit involves removing linebreak characters ("\r", "\n") from any input fields that could be placed before the message body.
I tested the form using wget by emulating the same headers sent by Firefox and sending an appropriate POST. Here is what I used (censored) if anyone wants to confirm.
post.txt contains
You would need to change all the cookie header, urls and emails (obviously) but also the form_token value I suspect.
Attempting to insert linebreak characters into the browser form was unsuccessful because Firefox and IE encode them and they are sent through in the message subject.
Finally, I have changed the test in the validation functions to use regexpr instead of string manipulation because it should be better.
This should be my final input on the issue. This patch final patch works exactly as I would like. Please review and set back to ready to commit asap.
Comment #25
chx commentedIf you use regexps, then at least please use this. even one ereg is slower than preg and two eregs are definitely slower than one preg :)
Comment #26
dries commentedThis has been fixed in CVS head.
Comment #27
fagoi can't find any checks for newlines in the subject in current head, so i suppose users with a custom smtp_library are still be vulnerable -> setting back to active.
Comment #28
dries commentedOops, this patch didn't make it in. It needs to be re-rolled though. Should be easy.
Comment #29
Steven commentedContact.module uses user_mail() to send the email, which passes the subject through mime_header_encode(). That function should use "=?UTF-8.." style escapes for anything which contains non-printable ascii characters:
So where is the problem? It's in fact rather nasty. That regular expression in mime_header_encode() gets confused around linebreaks, because of the ^ and $ operators, which also match at the beginning and end of a newline.
Patch attached which fixes this. Newlines are no longer a problem, they simply get encoded along with any other non-printables.
I'd much rather prefer this patch.
Comment #30
Steven commentedComment #31
Zen commentedIn addition to this, chx has provided a patch that strips
\rand\ninstances from textfields.-K
Comment #32
dries commentedThis looks like the right fix. Great job, Steven. I patched drupal.org with this patch so we can some testing. Ideally, we'd also have a test script that attempts to inject linebreaks.
Comment #33
killes@www.drop.org commentedboth recommended patches were committed.
Comment #34
(not verified) commented