Postfix 2.9 or sendmail, outgoing spam prevention (check if sender exists)

8,099

Solution 1

Finally I found a solution to filter outgoing spam with header_checks, though I do not know how effective and efficient it can be.

Warning:

  • this mitigation technique solution does not clean the infection.
  • it is only useful when the attack sends spam by using the real domain name and faked user names or email accounts as senders (From:).
  • when the infection uses other domain names as a sender (From:), this regex expression cannot be effective.

Given that /usr/sbin/sendmail is used by the infected script to spam, whenever it uses random-inexistent mail accounts (i.e. [email protected]), rather than real accounts as senders (i.e. [email protected]), mail headers can be checked by using the postfix built-in content inspection header_checks as follows:

#/etc/postfix/main.cf
# add it for example before TLS paramaters
# Anti-SPAM options
header_checks = pcre:/etc/postfix/header_checks

The regular expression in the header_checks file should exclude any real user for the domain (i.e. user1, user2,...):

#/etc/postfix/header_checks
/^From:((?![^@]*?user1|[^@]*?user2|[^@]*?user3|[^@]*?webmaster)[^@]*?)@my-domain\.tld/ REJECT invalid sender

Explanation of the regular expression (for any change it can be used this regex tester):

  • [^@] any character except @
  • [^@]*? any character, except @, zero or more times, lazy non-greedy (*?).
  • (?!user1|user2|etc) negative lookahead with alternatives: discarding real users of the domain.
  • (?![^@]*?user1|[^@]*?user2|etc) allowing characters before each users' email address.

Before any change it can be tested by (first one passes, second rejected):

$ postmap -q "From: [email protected]" pcre:/etc/postfix/header_checks
$ postmap -q "From: [email protected]" pcre:/etc/postfix/header_checks
REJECT invalid sender
$

Or even it can be tested by using a file with message headers captured during the infection (notice: the dash is important here):

$ postmap -q - pcre:/etc/postfix/check_headers < captured_headers.txt
From: "[email protected]      REJECT invalid sender
From: "[email protected]      REJECT invalid sender
$

Once tested, if it meets the desired result, just restart Postfix:

# service postfix restart
 * Stopping Postfix Mail Transport Agent postfix                [ OK ]
 * Starting Postfix Mail Transport Agent postfix                [ OK ]

To check how it is going on, one possible command:

# tail -n 10000 /var/log/mail.log | grep reject
or
# tail -n 10000 /var/log/mail.log | grep 'invalid sender'

Hope this will help anyone else.

EDIT

Some corrections of the regular expression:

#/etc/postfix/header_checks
/^From:(?![^@]*?user1@|[^@]*?user2@|[^@]*?user3@|[^@]*?webmaster@)([^@]*?@my-domain\.tld)/
REJECT invalid sender $1

The postmap check works well, but postfix is not filtering forged email users for the domain (last infection it did not filter). I do not know why.

Solution 2

Look to this diagram, there are three independent entrances where the email could enter postfix, sendmail (via pickup), smtpd and qmqpd. The last entrance was rarely used here.

When email send through smtpd, then the email source likely from outside (the exception when connection from 127.0.0.1). Outside email was considered untrusted obivously. So, postfix have standard feature to block it such as, RBL checker, Open Relay Checker, blaclisting/whitelisting and others. See Postfix SMTP relay and access control.

By default, IP address of server and 127.0.0.1 was considered trusted because whoever can connect from this host than they may cause further destruction beside spam. For example: if a script has been uploaded to webserver, then they can cause may act to deleting web root, planting backdoor and others.

Another trusted mail entrance was via sendmail/mail program. Basically when php call mail function or you invoke mail from terminal, you instruct sendmail to put email on postfix queue. Sendmail can't be invoked unless some people has some access to your box such as uploaded script.


Now, we are ready to answer to your main question: how can I check the sender against the database?

For smtpd connection, you can use postfix feature Postfix SMTP relay and access control check_sender_access to check againts your user database. I can't give you details as I never used Plesk. For sendmail/mail command then you need postfix content inspection (milter or after queue content filter) since above filters doesn't affected by source of the email.

Share:
8,099

Related videos on Youtube

rellampec
Author by

rellampec

Implementing tools to support Agile project management.

Updated on September 18, 2022

Comments

  • rellampec
    rellampec almost 2 years
    • Operating System: Linux Ubuntu 12.04 TLS
    • Context: Plesk 11.5
    • Utility: Postfix 2.9.6 (updates restricted by Plesk 11.5 distribution)

    I would like Postfix to check the 'sender' field of every outgoing smtp mail (before sending), so that the sender matches any of the valid created accounts (mail users) of any of the domains hosted in the server (as a condition to approve the sending).

    The reason of this is because whenever one domain becomes infected by spam-sender script(s), it usually uses a pattern like 'random-name'@infected-domain.tld , so by applying a sender-user-exist filter, it could be reduced the impact on the server's reputation, and so preventing being blacklisted as quickly as it currently does (whenever one of the hosted domains is infected by malicious scripts).

    So here, I am not asking for specific domain restrictions, but for specific users restrictions for the field "Mail From: " before sending it by Postfix (not when receiving incoming mail).

    Edit 1

    Finally I guessed the problem here is that /usr/sbin/sendmail can send, using Postfix, without authentication, isn't it? Or, is it configured somewhere else without me knowing that?

    Please, at least, could you help me with some useful and understandable documentation for sendmail? I could not find anything really useful to understand how it works, and less for a multiuser environment such as Plesk 11.5.

    That is why I would like to filter directly from Postfix, because is the central point to rely or send emails.

    Edit 2

    Searching finally I have discarded the directive for Postfix 2.4+ authorized_submit_users => http://www.postfix.org/postconf.5.html#authorized_submit_users

    It only checks the UID of the /usr/sbin/sendmail process, which is the user of the infected domain.

    Adding:

    authorized_submit_users = !unknown, static:all
    

    does not solve the problem.

    Edit 3

    Working with

    #/etc/postfix/main.cf
    header_checks = regexp:/etc/postfix/header_checks
    

    And the regex filtering options, using negative lookahead:

    #/etc/postfix/header_checks
    /^From: ".*(?!user1|user2|user3).*@infected-domain.tld/ REJECT
    

    Any idea? May I should open another thread?

    Solved!

    I have provided a customized solution for this specific case by answering it myself below. Thank you everyone for your information, collaboration and support!

    • HopelessN00b
      HopelessN00b over 9 years
      mainly those framed within Plesk Parallels context. Administration panels like Plesk are actually off topic here (and extremely unpopular), so you probably won't find much help as it relates specifically to Plesk. There's a fair deal of Postfix expertise around here, however, so you might find some answers that approach it from that angle.
    • masegaloeh
      masegaloeh over 9 years
      As I've never used plesk, I should ask you how the email from plesk enter postfix? mail command/Sendmail? SMTP via port 25?
    • rellampec
      rellampec over 9 years
      @masegaloeh: in this case, I am concerned with php scripts that use either sendmail command or installed libraries to avoid such function.
    • rellampec
      rellampec over 9 years
      @HpelessN00b: Sorry! I probably do not know enough of Postfix and I did not realized that the answer is somewhere else. Please, could you share one example for user-sender filtering tip, when the email is sent by one php script hosted in the same server as the Mail Server? Thank you!
    • masegaloeh
      masegaloeh about 9 years
      I still don't understand your third edits above? So what's your question actually?
    • rellampec
      rellampec about 9 years
      @masegaloeh: can be the Edit III a temporary or definitive solution to detect infections on the domain.tld and avoid being blacklisted? Is the RegEx expression correct?
  • rellampec
    rellampec over 9 years
    Thank you, but I had already searched on it, and it does not say anything about 'only allow to send from existing email accounts'; it just allow to limit the quota per hour. That is why I would like to use a direct custom configuration on Postfix (by command line).
  • Oleg Neumyvakin
    Oleg Neumyvakin over 9 years
    Do you want to say that your server allows to send mail from localhost without authentification?
  • Oleg Neumyvakin
    Oleg Neumyvakin over 9 years
    Feature I mention above allows to disable using sendmail directly by users and script. Does it fits your needs?
  • rellampec
    rellampec over 9 years
    Yes, I think that is exactly what is happening. Thank you! It is sending mail from localhost without authentication. Using the function sendmail(). NOTE: at the plesk panel the SMTP is required with authentication.
  • rellampec
    rellampec over 9 years
    I would like not upgrading to plesk 12, because I have read some many tips regarding problems of configuration after upgrading. I will probably do it time after.
  • rellampec
    rellampec about 9 years
    I have finally upgrade to Plesk 12 and the 'outgoing message limit' function helps to mitigate the impact and detect the attack.
  • rellampec
    rellampec about 9 years
    Finally I found that it is the best solution. The only problem is that I have no idea how to do it. Please, check Edit III of this question. Thank you!
  • rellampec
    rellampec about 9 years
    Thank you for your reply. I have looked at this other configuration. These options for postfix seem to be an option. However I did not see any clear example explaining every single line. So, at the end, you have to trust whatever they say without knowing how really it works. As an administrator I do have to know every single point of the config file whenever I change such things.
  • ServerDown
    ServerDown almost 9 years
    I had to add two lines to have from with a leading space and not /^From:([^@]sales@spammydomain\.net)/ REJECT invalid sender $1 /^From:(sales@spammydomain\.net)/ REJECT invalid sender $1
  • rellampec
    rellampec almost 9 years
    @ServerDown : [^@]*? already includes the leading space before the valid email address. Notice that you should include it within (?! here), a negative lookahead, so you do not reject the valid email address.
  • rellampec
    rellampec almost 9 years
    @ServerDown : the [^@]*? at ([^@]*?@my-domain\.tld) matches any invalid email address. It is between parenthesis brackets with @my-domain\.tld, so all the expression captures the invalid email address at $1
  • rellampec
    rellampec almost 9 years
    @ServerDown : for your example, considering 'sales' as a valid email address of your domain spammydomain.net , your Perl expression should be something like this: /^From:(?![^@]*?sales@)([^@]*?@spammydomain\.net)/ REJECT invalid sender $1
  • rellampec
    rellampec almost 9 years
    @ServerDown: if you want to force a delimiter before the valid email address then you should add valid delimiters before, like this [ <'"]. In your example: /^From:(?![^@]*?[ <'"]sales@)([^@]*?@spammydomain\.net)/ REJECT invalid sender $1