SA Bugzilla – Bug 5227
Enhancement: Create a plugin to catch hosts trying to helo as me
Last modified: 2019-07-08 09:14:33 UTC
It would be a nice test if we could specify the names of our servers and have a test that would automatically add points if the sending party helo's with the same name as the receiving party. HostA is outside sender HostB is spamassassin protected mail server. When HostA connects to HostB and gives a helo of HostB or HostB's IP Address, it would be nice to have a rule that could catch this. One problem pointed out is how to determine the hostname or IP address of the outside interface? Possibly with a config item to allow the admin to specify what the outside IP or DNS names are. One issue I can think is when we have multiple servers using the same rule sets. I personally run 3 SA servers with the same set of rules, how would I setup this configuration option to have it work on all 3 servers? And last, we'd need to keep track of each server receiving mail in order to get a proper mass-check of this rule, considering if mail in our corpus comes from all sorts of various servers, how would we accurately measure this rules accuracy through standard mass-checks etc? It's a nice idea, but can it be implemented in a way that works for everyone?
From Matthias Leisi on users list: I have cases where a machine legitimately HELOs as "myself"; in my situation these cases are covered by trusted_networks or internal_networks. Maybe eval:check_for_my_mx() should consider these networks (or skip it's tests altogether if the connection came from one of these networks); it may also need an actual exception list ('allowed_helo_as_myself').
Created attachment 3777 [details] Simple plugin Very simple plugin; not tested against the latest SA version from CVS but against an older release (3.1.3).
This plugin looks promising, a quick test shows it hitting a few spam and no ham yet. After trying to tweak it some, I've run into a road block. How do I define more than one ip address or name to be used? For in my setup, I have 3 IP addresses and 3 dns names that often get spoofed, I also see my domain name spoofed as in: helo mail.i-is.com or helo i-is.com I'm trying to configure helo_my_names with multiple names, it doesn't seem to take more than the last line I give it. Should I use helo_allow_all ip1.x.x.x ip2.x.x.x ip3.x.x.x or should I be using helo_allow_all ip1.x.x.x helo_allow_all ip2.x.x.x etc? Neither method seems to work yet. If we can get this fixed, it'll be a real nice addition!!
> helo_allow_all ip1.x.x.x > helo_allow_all ip2.x.x.x > etc? Neither method seems to work yet. It doesn't work because I haven't figured out yet how to parse and/or read multiple lines of the same config item ;) > If we can get this fixed, it'll be a real nice addition!! I'm pretty busy this week but I'll have some time over the weekend to improve on that (also missing is a check against HELOing with IP addresses which I'd like to add).
Ok, thanks for responding, take a look at whitelist_from, trusted_networks or some other config items that already do this!
Somehow I managed to get this working on my own. Not sure how good this is, I'm not a perl dev. Change: type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING to --> type => $Mail::SpamAssassin::Conf::CONF_TYPE_ADDRLIST add this sub: sub _helo_is_in_list { my ($self, $list, $pms, $helo) = @_; $helo = lc $helo; foreach my $regexp (values %{$list}) { if ($helo =~ qr/$regexp/i) { dbg("HeloAsMyself: helo $helo matches regexp: $regexp"); return 1; } } return 0; } Add this to check_for_helo_as_myself after my $ip = $pms-> return 1 if $self->_helo_is_in_list($pms->{conf}->{helo_my_names}, $pms, $helo); Then a bunch of code can be removed or commented, it seems to be working as expected now.
And to check for helo_is_my_ip I just added more config lines with my IP addresses as in: helo_my_names !66.51.152.240! The ! is needed due to how it's matched against the headers.
Created attachment 3794 [details] Detect HELO-forgery as "myself" * Now tested against 3.1.7 on multiple environments * Multiple "helo_allow_all" config items can be specified * Multiple "helo_my_name" config items can be specified (renamed from "helo_my_names") * Using permessagestatus->{relays_untrusted} to determine the potentially HELO-faking IP address, skipping ->{ip_private} addresses * Now does three checks: exact match, HELO regex on "myself" and vice versa
Created attachment 3796 [details] Detect HELO-forgery as "myself" (revised 1) Change: Line 206ff: If the potentially attacker-specified HELO string contains regex metachars (eg "?"), we would receive errors or open an attack vector. Therefore we skip the test if HELO contains anything but characters legal for a domain (a-z0-9, ., -). Also added a "Security Consideration" section to the POD doc.
if you change: } elsif ($myself =~ /.*$helo$/) { to } elsif ($myself =~ /.*\Q$helo\E$/) { that blocks the danger I think. (\Q, \E inhibit regexp metacharacters.) taint mode is good for finding this stuff...
My own mass-check (see http://masscheck.dnswl.org/full-results.20061221) gives the following result on this: MSECS SPAM% HAM% S/O RANK SCORE NAME 0 6248 339 0.949 0.00 0.00 (all messages) 0.00000 94.8535 5.1465 0.949 0.00 0.00 (all messages as %) [..] 2.687 2.8169 0.2950 0.905 0.00 1.00 HELO_AS_MYSELF The 0.295% ham (10 messages) are my own tests; however the 2.8% spam hits (175 messages) does not look too impressive. Regarding \Q..\E (comment #10): That's what I did first. While testing it became clear that we don't have a valid HELO in cases where \Q..\E is necessary to begin with, and hence we can not expect a match on "myself" anyway. So I decided to skip the test completely. But it may make sense to have an actual rule to penlize obviously illegal HELOs (or is there one already?).
If not already blocking in MTA, one can simply use: header FAKE_MY_HELO X-Spam-Relays-Untrusted =~ / helo=(?:\S+\.)?(?:mydomain\.com|mydomain2\.net) /i header FAKE_MY_HELO_IP X-Spam-Relays-Untrusted =~ / helo=\S*\b12\.23\.34\.45\b/i Closing.