vexim / vexim2

Virtual Exim 2
Other
71 stars 47 forks source link

Unauthenticated email accepted #286

Open rimas-kudelis opened 5 months ago

rimas-kudelis commented 5 months ago

Back in October, I received a funny email in which some great hacker alleged that they had taken over my home network, my mailbox, my everything else, and even caught me on webcam jerking of.

What I found really interesting about that email is that is had my own email address in the From field. I really doubt that the great hacker actually had my credentials, so I suspect that there's a bug somewhere which lets unauthenticated email through if it targets a local mailbox. Would be nice to investigate.

Sadly, I haven't preserved logs from back then. But it should be easy enough to replicate using Telnet.

ezbik commented 5 months ago

Solution would be:

If sender domain is a local domain and email was received without smtp authentication - it is subject to antispam (spamassasin).

runout-at commented 5 months ago

In _/acl/30_exim4-config_checkmail I did add an ACL which checks for the sender domains (except if mail comes from my local IP ranges). Not sure if this targets the same szenario you have.

drop
  message = The sending mailserver was classified as backscatterer. \
              The sender domain $sender_address_domain is the same as a local domain.
  !authenticated = *
  !senders = :
  sender_domains = : +local_domains : +relay_to_domains
  !hosts = <; 10.0.0.0/16 ; localhost ; 127.0.0.1 ; 2001:db8::/32
  logwrite = :main,reject: REJECTED - sender domain $sender_address is the same as a local domain. RCPT: $local_part_data@$domain_data

I you use this, make sure to have proper IP ranges in the !hosts line.

rimas-kudelis commented 5 months ago

drop message = The sending mailserver was classified as backscatterer. \ The sender domain $sender_address_domain is the same as a local domain. !authenticated = * !senders = : sender_domains = : +local_domains : +relay_to_domains !hosts = <; 10.0.0.0/16 ; localhost ; 127.0.0.1 ; 2001:db8::/32 logwrite = :main,reject: REJECTED - sender domain $sender_address is the same as a local domain. RCPT: $local_part_data@$domain_data

I get why you would want and even need an exception for local IPs, but why the exception for local networks though?

I'd probably just use !host @[] instead.

Also that logwrite line seems like a copy-paste error.

runout-at commented 5 months ago

You are right. !hosts line could be omitted completely. This comes from a time when I had clients (monitoring) on the internal networks which had problems with authentication and I didn't want to exclude each IP separately.

Why do you think the logwrite line is an error?

rimas-kudelis commented 5 months ago

You are right. !hosts line could be omitted completely. This comes from a time when I had clients (monitoring) on the internal networks which had problems with authentication and I didn't want to exclude each IP separately.

I guess it could be omitted, assuming emails from localhost are accepted beforehand. Otherwise wouldn't they be rejected by this ACL?

EDIT: nope! Commenting it out causes messages to be rejected even when they originate from localhost.

Why do you think the logwrite line is an error?

Now that I read it again, I take these words back. However, I'd probably write these messages differently.

The message mentions backscattering, but, based on the article in Wikipedia, I'd say backscatter is a different issue than this. I think I'd rather reject the message with the standard non-customized message instead (550 relay not permitted), or use something like "local users must always authenticate before sending mail".

And what the logwrite line says is not an invalid state by itself. It's invalid because that local user has not authenticated. I guess I would just let Exim reuse the message here.

BTW, I enabled SPF checking on my server (apparently it was off until now, along with some other checks), and that was enough for my case, because I use strict SPF rules on my domains. Still I think it would make sense for us to ship this ACL too. And it's tempting for me to go set it up for myself too. Gonna do it right now. :)

rimas-kudelis commented 5 months ago

So, I set it up and it works, and looks like this:

acl_check_mail:

  deny
    message = local users must authenticate before sending mail
    !authenticated = *
    !senders = :
    sender_domains = : +local_domains : +relay_to_domains
    # The !hosts line below allows sending emails from local domains
    # on local interfaces without authenticating first.
    # Comment it out if that is not desired.
    !hosts = @[]

  accept

But now it got me thinking whether this is actually the right thing to do... If my SPF policy prohibits anyone but my own server to send emails from me, this already works. On the other hand, if that policy allows anyone to send emails on my behalf, shouldn't my own server accept them? :thinking:

runout-at commented 5 months ago

Ok. thx for the feedback! And yes, at least !hosts will need something.

I believe in "SPF is broken by design" (but please, no discussion about that religion). I just configure it because otherwise I would have a lot of problems with other providers.

rimas-kudelis commented 5 months ago

Hmm, weird, I thought I'd copy (and later move) the stock SPF check from 30_exim4-config_check_rcpt to 30_exim4-config_check_mail to reject emails that fail SPF right after MAIL FROM instead of waiting for RCPT TO.

But then that check rejects my own emails even if I authenticate first. Which kinda makes sense since there are no exceptions for authenticated users in that check.

However, if it stays in check_rcpt then it doesn't reject my emails after authentication. I wonder why.

runout-at commented 5 months ago

AFAIR there has been a change in the Debian config in the last years and Debian-Exim uses libspf2. It should be sufficient to enable it in the config and as I don't use/trust SPF I didn't mess around with it:

#CHECK_RCPT_SPF = true
#_HAVE_SPF = true

I don't know why there are two settings for that.

I think I use the original file from the Debian bookworm and SPF check is in

  .ifdef CHECK_RCPT_SPF
  .ifdef _HAVE_SPF
  deny
    !acl = acl_local_deny_exceptions
    spf = fail
    message = [SPF] $sender_host_address is not allowed to send mail from \
              ${if def:sender_address_domain {$sender_address_domain}{$sender_helo_name}}.
    log_message = SPF check failed.

  defer
    !acl = acl_local_deny_exceptions
    spf = temperror
    message = Temporary DNS error while checking SPF record.  Try again later.

  warn
    spf = pass:softfail:neutral:permerror
    add_header = :at_start:$spf_received
  .endif
  .endif
rimas-kudelis commented 5 months ago

AFAIR there has been a change in the Debian config in the last years and Debian-Exim uses libspf2. It should be sufficient to enable it in the config and as I don't use/trust SPF I didn't mess around with it:

* main/00_local_listmacrosdefs
#CHECK_RCPT_SPF = true
#_HAVE_SPF = true

I don't know why there are two settings for that.

I think I use the original file from the Debian bookworm and SPF check is in

* acl/30_exim4-config_check_rcpt
  .ifdef CHECK_RCPT_SPF
  .ifdef _HAVE_SPF
  deny
    !acl = acl_local_deny_exceptions
    spf = fail
    message = [SPF] $sender_host_address is not allowed to send mail from \
              ${if def:sender_address_domain {$sender_address_domain}{$sender_helo_name}}.
    log_message = SPF check failed.

  defer
    !acl = acl_local_deny_exceptions
    spf = temperror
    message = Temporary DNS error while checking SPF record.  Try again later.

  warn
    spf = pass:softfail:neutral:permerror
    add_header = :at_start:$spf_received
  .endif
  .endif

That's the method I was talking about. I'm somewhat surprised that this check is placed in acl/30_exim4-config_check_rcpt and not in acl/30_exim4-config_check_mail though, because it checks sender, not recipient.