SpamAssassin rules
# Put this file under /etc/spamassassin/ and run an sa-update or reload amavis etc. | |
# | |
# I used https://github.com/ercpe/ercpe-sa-rules/blob/master/ercpe-rules.cf as an example; | |
#-------------------------------------------------- | |
# top level domain matching | |
#-------------------------------------------------- | |
header SPAMMY_TLD_IN_RCVD Received =~ /(\.net\.ae|\.net\.id|\.ro|\.ru|\.co\.jp|\.co\.ke|\.AC\.ZA|\.co\.in|\.com\.vn|\.vn|\.cc|\.cu\.ua|\.com\.br|\.gr|\.hr|\.dk|\.win|\.bid|\.tw|\.br|\.pk|\.top|\.club|\.date|\.stream|\.xyz)\s/i | |
score SPAMMY_TLD_IN_RCVD 0.5 | |
describe SPAMMY_TLD_IN_RCVD Spammy TLD used in Received line | |
header SPAMMY_TLD_IN_FROM From =~ /(\.net\.ae|\.net\.id|\.ro|\.ru|\.co\.jp|\.co\.ke|\.AC\.ZA|\.co\.in|\.com\.vn|\.vn|\.cc|\.cu\.ua|\.com\.br|\.gr|\.hr|\.dk|\.win|\.bid|\.tw|\.br|\.pk|\.top|\.club|\.date|\.stream|\.xyz)>$/i | |
score SPAMMY_TLD_IN_FROM 0.3 | |
describe SPAMMY_TLD_IN_FROM Spammy TLD used in From line | |
header __HIGH_SPAMMY_TLD_RCVD Received =~ /\.(win|bid|top|club|date|stream|xyz)\/.*/i | |
header __HIGH_SPAMMY_TLD_FROM From =~ /\.(win|bid|top|club|date|stream|xyz)\/.*/i | |
uri __HIGH_SPAMMY_TLD_URI /\.(win|bid|top|club|date|stream|xyz)\/.+/i | |
meta HIGH_SPAMMY_TLD (__HIGH_SPAMMY_TLD_RCVD && __HIGH_SPAMMY_TLD_FROM && __HIGH_SPAMMY_TLD_URI) | |
score HIGH_SPAMMY_TLD 1.25 | |
describe HIGH_SPAMMY_TLD HIGH spammy tld used in Received, From and link | |
#-------------------------------------------------- | |
# Software matching | |
#-------------------------------------------------- | |
header EVALED_PHP X-PHP-Originating-Script =~ /eval\(\)\'d code/i | |
score EVALED_PHP 0.75 | |
describe EVALED_PHP Mail send from evaled PHP code | |
# outdated php version | |
header OUTDATED_PHP X-Mailer =~ /PHP v?5\.[1234].*/i | |
score OUTDATED_PHP 0.1 | |
describe OUTDATED_PHP Mail send from an outdated PHP version | |
header X_MAILER_SENDEMAIL X-Mailer =~ /sendEmail/i | |
score X_MAILER_SENDEMAIL 0.2 | |
header VULN_PHPMAILER X-Mailer =~ /PHPMailer 5\.2\.[0-9] /i | |
score VULN_PHPMAILER 0.75 | |
describe VULN_PHPMAILER Mail was sent from a vulnerable version of PHPMailer | |
# Various meta rules to match wordpress | |
header __WP_X_PHP_ORIG_SCRIPT X-PHP-Originating-Script =~ /(post|gallery|user)\.php/i | |
header __WP_X_PHP_SCRIPT X-PHP-Script =~ /(post|gallery|user)\.php/i | |
header __WP_X_SOURCE X-Source =~ /php-cgi/i | |
header __WP_X_SOURCE_ARGS X-Source-Args =~ /(post|gallery|user)\.php/i | |
header __WP_PATH_X_SOURCE_ARGS X-Source-Args =~ /\/wp\-(content|includes)\//i | |
# Various meta rules to match joomla | |
# e.g. X-Source-Args: /usr/bin/php /home/joventa/public_html/OLD/components/com_contact/helpers/files.php | |
header __JO_COMP_X_SOURCE_ARGS X-Source-Args =~ /components\/com_/i | |
header __JO_X_SOURCE_ARGS X-Source-Args =~ /\/joomla\//i | |
meta CMS_MAIL ( __WP_X_PHP_ORIG_SCRIPT || __WP_X_PHP_SCRIPT || __WP_X_SOURCE || __WP_X_SOURCE_ARGS || __WP_PATH_X_SOURCE_ARGS || __JO_COMP_X_SOURCE_ARGS || __JO_X_SOURCE_ARGS ) | |
score CMS_MAIL 1.25 | |
describe CMS_MAIL Mail sent from a probably hacked CMS (like Wordpress or Joomla) | |
#-------------------------------------------------- | |
# Header matching | |
#-------------------------------------------------- | |
header X_ORG_REAL_CAPITAL X-Organization =~ /RealCapitalMarkets\.com/i | |
score X_ORG_REAL_CAPITAL 3.5 | |
#-------------------------------------------------- | |
# Subject matching | |
#-------------------------------------------------- | |
header __SUBJECT_NEIGHBOR Subject =~ /Neighbou?r/i | |
header __SUBJECT_NEXT_DOOR Subject =~ /next door/i | |
meta SUBJECT_NEIGHBOUR (__SUBJECT_NEIGHBOR || __SUBJECT_NEXT_DOOR) | |
score SUBJECT_NEIGHBOUR 0.5 | |
header __SUBJECT_VIAGRA Subject =~ /viagra/i | |
header __SUBJECT_PILLS Subject =~ /pills/i | |
header __SUBJECT_HEALTH_SECRET Subject =~ /health secret/i | |
meta SUBJECT_HEALTH (__SUBJECT_VIAGRA || __SUBJECT_PILLS || __SUBJECT_HEALTH_SECRET) | |
score SUBJECT_HEALTH 0.2 | |
describe SUBJECT_HEALTH health-related subject | |
header SUBJECT_DARLEHEN Subject =~ /Darlehen Angebot jetzt bewerben/i | |
score SUBJECT_DARLEHEN 1.0 | |
header __FROM_DAVIS_WRIGHT From =~ /Davis Wright/i | |
header __SUBJECT_LEG_DARLEHEN Subject =~ /Legitimes Darlehen Angebot/i | |
meta DAVIS_WRIGHT_DARLEHEN (__FROM_DAVIS_WRIGHT && __SUBJECT_LEG_DARLEHEN) | |
score DAVIS_WRIGHT_DARLEHEN 1.5 | |
header __FROM_KEVIN_PAGE From =~ /Kevin Page/i | |
meta KEVIN_PAGE_DARLEHEN (__FROM_KEVIN_PAGE && SUBJECT_DARLEHEN) | |
score KEVIN_PAGE_DARLEHEN 1.2 | |
# "Domain Expiration SEO" spam | |
header __SUBJECT_EXP_SEO Subject =~ /Expiration SEO/i | |
header __FROM_EXP_SEO From =~ /(Domain Expiration SEO|Final Reminder)/i | |
meta EXPIRATION_SEO (__SUBJECT_EXP_SEO && __FROM_EXP_SEO) | |
score EXPIRATION_SEO 0.6 | |
describe EXPIRATION_SEO Variation of Domain SEO spam | |
# "SEO issue" spam | |
header __SUBJECT_SEO_ISSUE Subject =~ /SEO Issue/i | |
body __BODY_SEO_ISSUE /\s+(Search Specialist|Hello Team)/i | |
meta SEO_ISSUE (__SUBJECT_SEO_ISSUE && __BODY_SEO_ISSUE) | |
score SEO_ISSUE 1.0 | |
describe SEO_ISSUE Variation of Domain SEO spam | |
# Domain Alert | |
header __SUBJECT_DOMAIN_ALERT Subject =~ /This is your Final (Reminder|Notice)/i | |
header __FROM_DOMAIN_ALERT From =~ /(Internet Services|Domain Notice)/i | |
body __BODY_DOMAIN_ALERT /\s+search engine registration/i | |
meta DOMAIN_ALERT (__SUBJECT_DOMAIN_ALERT && __FROM_DOMAIN_ALERT && __BODY_DOMAIN_ALERT) | |
score DOMAIN_ALERT 0.9 | |
describe DOMAIN_ALERT Variations of search engine registration spam | |
header __FROM_BITCOIN_CODE From =~ /bestofmesh.com/i | |
uri __BITCOIN_CODE_LINK /track.bestofmesh.com.*/i | |
meta BITCOIN_CODE (__FROM_BITCOIN_CODE && __FROM_BITCOIN_CODE) | |
score BITCOIN_CODE 0.6 | |
#-------------------------------------------------- | |
# uri matching | |
#-------------------------------------------------- | |
# Something like .... /plugin.php?t=147&SeBJYnc8AzD8YLd4kvf4uNR=Fqz&12i=Cwb&4f=cL4g | |
# the common parts are: | |
# - the first parameter name is one char long | |
# - at least two more parameter follow | |
uri SPAM_LINK_1 /\/[a-z]+\.php\?\w=[a-zA-Z0-9]+(&[\w\d]+=[a-zA-Z0-9]+){2,}/i | |
score SPAM_LINK_1 0.4 | |
describe SPAM_LINK_1 Spam link | |
# same as above but focused on the link title | |
rawbody SPAM_LINK_2 /\>.*(profile is here|new photos|photos are here).*\<\/a\>/i | |
score SPAM_LINK_2 0.4 | |
describe SPAM_LINK_2 Spam link title | |
# Something like ..../l/lt2K2240EH14R/1014LP2140G4657WU60A33287012SM1334722588 | |
# Common parts: | |
# - first part is always one character | |
# - three parts in total | |
uri SPAM_LINK_3 /\/\w\/\w{10,}\/\w{10,}/i | |
score SPAM_LINK_3 0.4 | |
describe SPAM_LINK_3 Spam link | |
# /pass.php?utm_source=6900l3njtv&utm_medium=nc6600mc98&utm_campaign=a1q4sxq0wo&utm_term=tvec4xo652&utm_content=403g22e07g | |
# Common parts | |
# - always a .php file in the root of the domain | |
# - only GA tracking parameters | |
# - values for utm_source, utm_medium and utm_campaign are always the same (at least between 2017-07 and 2017-10), | |
# utm_term varies slightly and utm_content is random | |
# - all tracking parameters have 10 chars | |
uri SPAM_LINK_4 /\/[a-z]+\.php\?utm_source=[a-zA-Z0-9]{10}&utm_medium=[a-zA-Z0-9]{10}&utm_campaign=[a-zA-Z0-9]{10}&utm_term=[a-zA-Z0-9]{10}&utm_content=[a-zA-Z0-9]{10}/i | |
score SPAM_LINK_4 0.4 | |
describe SPAM_LINK_4 Spam link | |
uri SPAM_LINK_4_EXTRA /\/[a-z]+\.php\?utm_source=6900l3njtv&utm_medium=nc6600mc98&utm_campaign=a1q4sxq0wo&utm_term=[a-zA-Z0-9]{10}&utm_content=[a-zA-Z0-9]{10}/i | |
score SPAM_LINK_4_EXTRA 0.4 | |
describe SPAM_LINK_4_EXTRA Spam link (extra score) | |
#-------------------------------------------------- | |
# Mime matching | |
#-------------------------------------------------- | |
mimeheader DOC_ATTACHED Content-Disposition =~ /filename\=.*\.docx?/i | |
score DOC_ATTACHED 1.0 | |
describe DOC_ATTACHED Contains .doc or .docx attachment | |
mimeheader XLS_ATTACHED Content-Disposition =~ /filename\=.*\.xlsx?/i | |
score XLS_ATTACHED 1.0 | |
describe XLS_ATTACHED Contains .xls or .xlsx attachment | |
mimeheader __ZIP_ATTACHED Content-Disposition =~ /filename\=.*\.zip/i | |
describe __ZIP_ATTACHED Contains .zip attachment | |
#-------------------------------------------------- | |
# Text matching (lowered impact to avoid false pos) | |
#-------------------------------------------------- | |
header __SUBJECT_FUCK Subject =~ /f[ua5\&\%]ck/i | |
body __BODY_FUCK /\s+(fuck|sex|masturbate|anal|fist)\s+/i | |
meta FUCKED_MAIL (__SUBJECT_FUCK || __BODY_FUCK ) | |
score FUCKED_MAIL 0.3 | |
describe FUCKED_MAIL Contains variations of 'fuck' in the subject and/or body | |
header __SUBJECT_FAX_RECEIVED Subject =~ /You have 1 new fax, document/i | |
header __SUBJECT_FAX_RECEIVED2 Subject =~ /You have received a new fax, document /i | |
meta FAX_RECEIVED ((__SUBJECT_FAX_RECEIVED || __SUBJECT_FAX_RECEIVED2) && __ZIP_ATTACHED) | |
score FAX_RECEIVED 1.1 | |
body __BODY_VIAGRA /viagra/i | |
body __BODY_PILLS /pills/i | |
body __BODY_HEALTH_SECRET /health secret/i | |
meta BODY_HEALTH (__BODY_VIAGRA || __BODY_PILLS || __BODY_HEALTH_SECRET) | |
score BODY_HEALTH 0.2 | |
describe BODY_HEALTH health-related body text | |
#-------------------------------------------------- | |
# RBL using softened mailspike scores | |
#-------------------------------------------------- | |
# Obvious spam sources | |
header __RCVD_IN_MSPIKE eval:check_rbl('mspike-lastexternal','bl.mailspike.net') | |
tflags __RCVD_IN_MSPIKE net noautolearn | |
# Bad senders | |
header __RCVD_IN_MSPIKE_Z eval:check_rbl_sub('mspike-lastexternal', '^127\.0\.0\.2$') | |
describe __RCVD_IN_MSPIKE_Z Spam wave participant | |
tflags __RCVD_IN_MSPIKE_Z net noautolearn | |
header RCVD_IN_MSPIKE_L5 eval:check_rbl_sub('mspike-lastexternal', '^127\.0\.0\.10$') | |
describe RCVD_IN_MSPIKE_L5 Very bad reputation (-5) | |
tflags RCVD_IN_MSPIKE_L5 net | |
header RCVD_IN_MSPIKE_L4 eval:check_rbl_sub('mspike-lastexternal', '^127\.0\.0\.11$') | |
describe RCVD_IN_MSPIKE_L4 Bad reputation (-4) | |
tflags RCVD_IN_MSPIKE_L4 net | |
header RCVD_IN_MSPIKE_L3 eval:check_rbl_sub('mspike-lastexternal', '^127\.0\.0\.12$') | |
describe RCVD_IN_MSPIKE_L3 Low reputation (-3) | |
tflags RCVD_IN_MSPIKE_L3 net noautolearn | |
header RCVD_IN_MSPIKE_L2 eval:check_rbl_sub('mspike-lastexternal', '^127\.0\.0\.13$') | |
describe RCVD_IN_MSPIKE_L2 Suspicious reputation (-2) | |
tflags RCVD_IN_MSPIKE_L2 net noautolearn | |
# Good senders | |
header RCVD_IN_MSPIKE_H5 eval:check_rbl_sub('mspikeg-firsttrusted', '^127\.0\.0\.20$') | |
describe RCVD_IN_MSPIKE_H5 Excellent reputation (+5) | |
tflags RCVD_IN_MSPIKE_H5 nice net | |
header RCVD_IN_MSPIKE_H4 eval:check_rbl_sub('mspikeg-firsttrusted', '^127\.0\.0\.19$') | |
describe RCVD_IN_MSPIKE_H4 Very Good reputation (+4) | |
tflags RCVD_IN_MSPIKE_H4 nice net | |
header RCVD_IN_MSPIKE_H3 eval:check_rbl_sub('mspikeg-firsttrusted', '^127\.0\.0\.18$') | |
describe RCVD_IN_MSPIKE_H3 Good reputation (+3) | |
tflags RCVD_IN_MSPIKE_H3 nice net | |
header RCVD_IN_MSPIKE_H2 eval:check_rbl_sub('mspikeg-firsttrusted', '^127\.0\.0\.17$') | |
describe RCVD_IN_MSPIKE_H2 Average reputation (+2) | |
tflags RCVD_IN_MSPIKE_H2 nice net noautolearn | |
# *_L and *_Z may overlap, so account for that | |
meta __RCVD_IN_MSPIKE_LOW RCVD_IN_MSPIKE_L5 || RCVD_IN_MSPIKE_L4 || RCVD_IN_MSPIKE_L3 || RCVD_IN_MSPIKE_L2 | |
meta RCVD_IN_MSPIKE_ZBI __RCVD_IN_MSPIKE_Z && !__RCVD_IN_MSPIKE_LOW | |
# Scores (lowered impact to avoid false positives) | |
score RCVD_IN_MSPIKE_ZBI 4.1 | |
score RCVD_IN_MSPIKE_L5 4.8 | |
score RCVD_IN_MSPIKE_L4 3.9 | |
score RCVD_IN_MSPIKE_L3 3.6 | |
score RCVD_IN_MSPIKE_L2 0.6 | |
score RCVD_IN_MSPIKE_H2 -0.3 | |
score RCVD_IN_MSPIKE_H3 -1.3 | |
score RCVD_IN_MSPIKE_H4 -2.3 | |
score RCVD_IN_MSPIKE_H5 -3.3 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment