commit 20c3b7e: [Fix] Improve base64 detection

Vsevolod Stakhov vsevolod at highsecure.ru
Mon Mar 16 13:35:06 UTC 2020


Author: Vsevolod Stakhov
Date: 2020-03-16 13:31:01 +0000
URL: https://github.com/rspamd/rspamd/commit/20c3b7eac45b6d946eb5082b47368b54d7571079 (HEAD -> master)

[Fix] Improve base64 detection

---
 src/libmime/mime_parser.c | 81 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 76 insertions(+), 5 deletions(-)

diff --git a/src/libmime/mime_parser.c b/src/libmime/mime_parser.c
index 91df7e684..e15f8974e 100644
--- a/src/libmime/mime_parser.c
+++ b/src/libmime/mime_parser.c
@@ -199,7 +199,8 @@ rspamd_mime_part_get_cte_heuristic (struct rspamd_task *task,
 		struct rspamd_mime_part *part)
 {
 	const guint check_len = 128;
-	guint real_len, nspaces = 0, neqsign = 0, n8bit = 0, nqpencoded = 0;
+	guint real_len, nspaces = 0, neqsign = 0, n8bit = 0, nqpencoded = 0,
+		padeqsign = 0, nupper = 0, nlower = 0;
 	gboolean b64_chars = TRUE;
 	const guchar *p, *end;
 	enum rspamd_cte ret = RSPAMD_CTE_UNKNOWN;
@@ -239,18 +240,24 @@ rspamd_mime_part_get_cte_heuristic (struct rspamd_task *task,
 		}
 	}
 
+	/* Skip trailing spaces */
+	while (end > p && g_ascii_isspace (*(end - 1))) {
+		end --;
+	}
+
 	if (end > p + 2) {
 		if (*(end - 1) == '=') {
-			neqsign ++;
+			padeqsign ++;
 			end --;
 		}
 
 		if (*(end - 1) == '=') {
-			neqsign ++;
+			padeqsign ++;
 			end --;
 		}
 	}
 
+	/* Adjust end to analyse only first characters */
 	if (end - p > real_len) {
 		end = p + real_len;
 	}
@@ -260,6 +267,7 @@ rspamd_mime_part_get_cte_heuristic (struct rspamd_task *task,
 			nspaces ++;
 		}
 		else if (*p == '=') {
+			b64_chars = FALSE; /* Eqsign must not be inside base64 */
 			neqsign ++;
 			p ++;
 
@@ -277,12 +285,74 @@ rspamd_mime_part_get_cte_heuristic (struct rspamd_task *task,
 		else if (!(g_ascii_isalnum (*p) || *p == '/' || *p == '+')) {
 			b64_chars = FALSE;
 		}
+		else if (g_ascii_isupper (*p)) {
+			nupper ++;
+		}
+		else if (g_ascii_islower (*p)) {
+			nlower ++;
+		}
 
 		p ++;
 	}
 
-	if (b64_chars && neqsign < 2 && nspaces == 0) {
-		ret = RSPAMD_CTE_B64;
+	if (b64_chars && neqsign <= 2 && nspaces == 0) {
+		/* Need more thinking */
+
+		if (part->raw_data.len > 80) {
+			if (padeqsign > 0) {
+				ret = RSPAMD_CTE_B64;
+			}
+			else {
+				/* We have a large piece of data with no spaces and base64
+				 * symbols only, no padding is detected as well...
+				 *
+				 * There is a small chance that our first 128 characters
+				 * are either some garbage or it is a base64 with no padding
+				 * (e.g. when it is not needed)
+				 */
+				if (nupper > 1 && nlower > 1) {
+					/*
+					 * We have both uppercase and lowercase letters, so it can be
+					 * base64
+					 */
+					ret = RSPAMD_CTE_B64;
+				}
+				else {
+					ret = RSPAMD_CTE_7BIT;
+				}
+			}
+		}
+		else {
+
+			if (((end - (const guchar *)part->raw_data.begin) + padeqsign) % 4 == 0) {
+				if (padeqsign == 0) {
+					/*
+					 * It can be either base64 or plain text, hard to say
+					 * Let's assume that if we have > 1 uppercase it is
+					 * likely base64
+					 */
+					if (nupper > 1 && nlower > 1) {
+						ret = RSPAMD_CTE_B64;
+					}
+					else {
+						ret = RSPAMD_CTE_7BIT;
+					}
+
+				}
+				else {
+					ret = RSPAMD_CTE_B64;
+				}
+			}
+			else {
+				/* No way */
+				if (padeqsign == 1 || padeqsign == 2) {
+					ret = RSPAMD_CTE_B64;
+				}
+				else {
+					ret = RSPAMD_CTE_7BIT;
+				}
+			}
+		}
 	}
 	else if (n8bit == 0) {
 		if (neqsign > 2 && nqpencoded > 2) {
@@ -297,6 +367,7 @@ rspamd_mime_part_get_cte_heuristic (struct rspamd_task *task,
 	}
 
 	msg_debug_mime ("detected cte: %s", rspamd_cte_to_string (ret));
+
 	return ret;
 }
 


More information about the Commits mailing list