Antispam reputation and composite rules

This commit is contained in:
mdecimus
2023-10-15 16:15:14 +02:00
parent 0b67f31220
commit 29a8ecb29b
13 changed files with 287 additions and 41 deletions

View File

@@ -1,5 +1,5 @@
if eval "!t.SPAM_TRAP && !t.TRUSTED_REPLY" {
let "bayes_result" "bayes_classify('spamdb/bayes-classify', body_and_subject)";
let "bayes_result" "bayes_classify('spamdb/token-lookup', body_and_subject)";
if eval "!is_empty(bayes_result)" {
if eval "bayes_result > 0.7" {
let "t.BAYES_SPAM" "1";

View File

@@ -0,0 +1,75 @@
if eval "t.FORGED_RECIPIENTS && t.MAILLIST" {
let "t.FORGED_RECIPIENTS_MAILLIST" "1";
}
if eval "t.FORGED_SENDER && t.MAILLIST" {
let "t.FORGED_SENDER_MAILLIST" "1";
}
if eval "t.DMARC_POLICY_ALLOW && (t.R_SPF_SOFTFAIL || t.R_SPF_FAIL || t.R_DKIM_REJECT)" {
let "t.DMARC_POLICY_ALLOW_WITH_FAILURES" "1";
}
if eval "t.R_DKIM_NA && t.R_SPF_NA && t.DMARC_NA && t.ARC_NA" {
let "t.AUTH_NA" "1";
}
if eval "!(t.R_DKIM_NA && t.R_SPF_NA && t.DMARC_NA && t.ARC_NA) && (t.R_DKIM_NA || t.R_DKIM_TEMPFAIL || t.R_DKIM_PERMFAIL) && (t.R_SPF_NA || t.R_SPF_DNSFAIL) && t.DMARC_NA && (t.ARC_NA || t.ARC_DNSFAIL)" {
let "t.AUTH_NA_OR_FAIL" "1";
}
if eval "(t.AUTH_NA || t.AUTH_NA_OR_FAIL) && (t.BOUNCE || t.SUBJ_BOUNCE_WORDS)" {
let "t.BOUNCE_NO_AUTH" "1";
}
if eval "(t.HAS_X_POS || t.HAS_PHPMAILER_SIG) && t.HAS_WP_URI && (t.PHISHING || t.CRACKED_SURBL || t.PH_SURBL_MULTI || t.DBL_PHISH || t.DBL_ABUSE_PHISH || t.URIBL_BLACK || t.PHISHED_OPENPHISH || t.PHISHED_PHISHTANK)" {
let "t.HACKED_WP_PHISHING" "1";
}
if eval "(t.HAS_XOIP || t.RCVD_FROM_SMTP_AUTH) && t.DCC_BULK" {
let "t.COMPROMISED_ACCT_BULK" "1";
}
if eval "t.DCC_BULK && (t.MISSING_TO || t.R_UNDISC_RCPT)" {
let "t.UNDISC_RCPTS_BULK" "1";
}
if eval "t.RECEIVED_SPAMHAUS_PBL && !t.RCVD_VIA_SMTP_AUTH" {
let "t.RCVD_UNAUTH_PBL" "1";
}
if eval "(t.R_DKIM_ALLOW || t.ARC_ALLOW) && t.RCVD_IN_DNSWL_MED" {
let "t.RCVD_DKIM_ARC_DNSWL_MED" "1";
}
if eval "(t.R_DKIM_ALLOW || t.ARC_ALLOW) && t.RCVD_IN_DNSWL_HI" {
let "t.RCVD_DKIM_ARC_DNSWL_HI" "1";
}
if eval "(t.HAS_X_POS || t.HAS_PHPMAILER_SIG || t.HAS_X_PHP_SCRIPT) && (t.SUBJECT_ENDS_QUESTION || t.SUBJECT_ENDS_EXCLAIM || t.MANY_INVISIBLE_PARTS)" {
let "t.AUTOGEN_PHP_SPAMMY" "1";
}
if eval "(t.PHISHING || t.DBL_PHISH || t.PHISHED_OPENPHISH || t.PHISHED_PHISHTANK) && (t.SUBJECT_ENDS_QUESTION || t.SUBJECT_ENDS_EXCLAIM)" {
let "t.PHISH_EMOTION" "1";
}
if eval "t.HAS_GUC_PROXY_URI || t.URIBL_RED || t.DBL_ABUSE_REDIR || t.HAS_ONION_URI" {
let "t.HAS_ANON_DOMAIN" "1";
}
if eval "(t.R_SPF_FAIL || t.R_SPF_SOFTFAIL) && (t.RCVD_COUNT_ZERO || t.RCVD_NO_TLS_LAST)" {
let "t.VIOLATED_DIRECT_SPF" "1";
}
if eval "(t.FREEMAIL_FROM || t.FREEMAIL_ENVFROM || t.FREEMAIL_REPLYTO) && (t.TO_DN_RECIPIENTS || t.R_UNDISC_RCPT) && (t.FROM_NAME_HAS_TITLE || t.FREEMAIL_REPLYTO_NEQ_FROM_DOM)" {
let "t.FREEMAIL_AFF" "1";
}
if eval "t.HFILTER_URL_ONLY && t.REDIRECTOR_URL" {
let "t.REDIRECTOR_URL_ONLY" "1";
}
if eval "t.FAKE_REPLY && t.RCVD_VIA_SMTP_AUTH && (!t.RECEIVED_SPAMHAUS_PBL || t.RECEIVED_SPAMHAUS_XBL || t.RECEIVED_SPAMHAUS_SBL)" {
let "t.THREAD_HIJACKING_FROM_INJECTOR" "1";
}

View File

@@ -103,6 +103,9 @@ if eval "header.X-PHP-Script.exists" {
let "t.HIDDEN_SOURCE_OBJ" "1";
}
}
if eval "contains_ignore_case(header.X-Mailer, 'PHPMailer')" {
let "t.HAS_PHPMAILER_SIG" "1";
}
if eval "header.X-Source:X-Source-Args:X-Source-Dir.exists" {
let "t.HAS_X_SOURCE" "1";
if eval "contains(header.X-Source-Args, '../')" {

View File

@@ -6,17 +6,3 @@ if eval "env.iprev.result != ''" {
let "t.RDNS_NONE" "1";
}
}
# Lookup ASN
let "asn_lookup" "";
if eval "len(env.remote_ip.reverse) <= 15" {
let "asn_lookup" "env.remote_ip.reverse + '.origin.asn.cymru.com'";
} else {
let "asn_lookup" "env.remote_ip.reverse + '.origin.asn6.cymru.com'";
}
let "asn_lookup" "split(dns_query(asn_lookup, 'txt'), '|')";
let "asn" "asn_lookup[0]";
let "country" "asn_lookup[2]";
#eval "print('ASN: ' + asn + ' (' + country + ')')";

View File

@@ -6,6 +6,6 @@ if eval "!is_empty(message_id)" {
eval "lookup('spamdb/id-insert', message_id)";
if eval "lookup('spam/options', 'AUTOLEARN_REPLIES')" {
eval "bayes_train('spamdb/bayes-train', thread_name(header.subject) + ' ' + body.to_text, false)";
eval "bayes_train('spamdb/token-insert', thread_name(header.subject) + ' ' + body.to_text, false)";
}
}

View File

@@ -0,0 +1,73 @@
# Obtain sender address and domain
let "rep_from" "envelope.from";
let "rep_from_domain" "envfrom_domain_sld";
if eval "is_empty(rep_from)" {
let "rep_from" "from_addr";
let "rep_from_domain" "from_domain_sld";
}
if eval "env.dmarc.result != 'pass'" {
# Do not penalize forged domains
let "rep_from" "'_' + rep_from";
let "rep_from_domain" "'_' + rep_from_domain";
}
# Lookup ASN
let "asn" "";
if eval "len(env.remote_ip.reverse) <= 15" {
let "asn" "env.remote_ip.reverse + '.origin.asn.cymru.com'";
} else {
let "asn" "env.remote_ip.reverse + '.origin.asn6.cymru.com'";
}
let "asn" "split(dns_query(asn, 'txt'), '|')[0]";
# Generate reputation tokens
let "token_ids" "";
if eval "asn > 0" {
let "token_ids" "['i:' + env.remote_ip, 'f:' + rep_from, 'd:' + rep_from_domain, 'a:' + asn ]";
} else {
let "token_ids" "['i:' + env.remote_ip, 'f:' + rep_from, 'd:' + rep_from_domain ]";
}
# Lookup reputation
let "i" "len(token_ids)";
let "reputation" "0.0";
while "i > 0" {
let "i" "i - 1";
let "token_id" "token_ids[i]";
# Lookup reputation
let "token_rep" "lookup_map('spamdb/reputation-lookup', token_id)";
# Update reputation
eval "lookup_map('spamdb/reputation-insert', [token_id, score])";
if eval "is_empty(token_rep)" {
continue;
}
# Assign weight
let "weight" "";
if eval "starts_with(token_id, 'f:')" {
# Sender address has 50% weight
let "weight" "0.5";
} elsif eval "starts_with(token_id, 'd:')" {
# Sender domain has 20% weight
let "weight" "0.2";
} elsif eval "starts_with(token_id, 'i:')" {
# IP has 20% weight
let "weight" "0.2";
} elsif eval "starts_with(token_id, 'a:')" {
# ASN has 10% weight
let "weight" "0.1";
} else {
continue;
}
let "reputation" "reputation + (token_rep[0] / token_rep[1] * weight)";
}
# Adjust score using a 0.5 factor
if eval "reputation > 0" {
let "score" "score + (reputation - score) * 0.5";
}

View File

@@ -1,6 +1,6 @@
# Check if the message was sent to a spam trap address
if eval "lookup('spam/trap-address', envelope.to)" {
eval "bayes_train('spamdb/bayes-train', body_and_subject, true)";
eval "bayes_train('spamdb/token-insert', body_and_subject, true)";
let "t.SPAM_TRAP" "1";
}