

邮件钓鱼攻防工程·3:贝叶斯与 NLP 内容对抗
拆 SEG 贝叶斯与语义检测层,结合 SpamAssassin 源码边界讲内容绕过与评分对抗思路。
拆 SEG 贝叶斯与语义检测层,结合 SpamAssassin 源码边界讲内容绕过与评分对抗思路。
字数 6265,阅读大约需 32 分钟
本文是「邮件钓鱼攻防工程」系列的第三篇。前两篇解决了发信通道和反沙箱。但发信只是一部分,正文被网关过滤,前面的工作全白做。
邮件能不能进网关,内容进网关之后能不能不进垃圾箱,这是两个完全不同的对抗维度。本篇拆邮件安全网关(SEG)最核心的内容检测逻辑:贝叶斯分类器和 NLP 语义引擎,以及工程上的规避思路。

首发至公众号:Yofune安全研究 欢迎关注。
0x01 对手 模型 :SEG 内容层的双层检测架构#
国内和境外的环境差异直接影响内容对抗的策略选择。境外企业网络的主力 SEG 是 Microsoft Defender for Office 365 和 Proofpoint;国内环境更复杂,大型央企和金融机构普遍部署了国产邮件网关,安全厂商这类产品百花齐放,不一一例举。中小企业大量使用腾讯企业邮、 阿里云 企业邮,后端反垃圾接的是云端能力。
这些产品的内容检测架构大体一致,底层两层叠加:第一层是贝叶斯概率层,基于词袋模型,SpamAssassin 和 Rspamd 是最典型的开源实现,国产厂商基本在此之上做了中文语料的二次训练;第二层是深度语义层,国内有不少基于 BERT-base 的轻量检测模型,不详细展开,感兴趣的可以搜索关键字。两层检测不是串联阻断,而是评分加权合并。一封邮件贝叶斯层得分很低,但只要深度语义层捕捉到了钓鱼邮件特征,照样被拦。目标是让最终得分低于阈值,需要两层一起对抗。

SpamAssassin 的 Bayes 插件(lib/Mail/SpamAssassin/Plugin/Bayes.pm)#
Token 化流程(源码 tokenize 函数位于第 1134-1217 行,_tokenize_line 位于第 1219-1380 行):
原始文本 → 正则提取 → 最少3字符过滤 → SHA1哈希截取40位 → 停用词过滤bashToken 提取正则(_tokenize_line 函数内,行 1235-1240):

# 使用正则表达式提取Token:匹配ASCII单词、英文字符,以及多字节的UTF-8编码字符(用于支持中文等非ASCII文本)
s{ ( [A-Za-z0-9,@*!_'"\$. -]+ | # 匹配常规ASCII词元
[\xC0-\xDF][\x80-\xBF] | # 匹配UTF-8双字节字符
[\xE0-\xEF][\x80-\xBF]{2} | # 匹配UTF-8三字节字符(包含常见中文字符)
[\xF0-\xF4][\x80-\xBF]{3} | # 匹配UTF-8四字节字符
[\xA1-\xFF] ) | . } # 匹配ISO-8859等单字符或多字节边缘字符,其余未匹配的单个字符替换为空格
{ defined $1 ? $1 : ' ' }xsge;bashSHA1 哈希压缩为 40 位 Key(tokenize 函数内,行 1212):
# 对提取出的Token计算SHA1值,并截取其哈希结果的最后5个字节(40位),作为统计数据库中存储的唯一键值
$tokens{substr(sha1($token), -5)} = $token if $token ne '';bashSpamAssassin 实现里有三个硬性设计边界值得利用。
Token 计数上限(行 250):

# 声明在对数似然比排序中参与评分合并计算的最大有效Token数量为150。超过该限制的弱特征Token将被舍弃。
use constant N_SIGNIFICANT_TOKENS => 150;bashSpamAssassin 在计算贝叶斯分值时,会把分词拿到的所有 Token 按概率偏差(即该词有多偏向垃圾邮件或正常邮件)进行降序排列,最多只取前 150 个最明显的 Token 参与最终计算,超出 150 个的词直接丢弃。这个 150 的限制是代码里写死的硬性规定。
只要在正文里混入足够多(超过 150 个)的强 Ham 词(即正常业务往来中的高频词),就能把真正敏感的恶意词挤出这 150 个计分窗口,使其不参与最终评分。

Robinson 平滑算法(行 1644-1653,常量在 CombineChi.pm 第 47-50 行定义,FW_S_CONSTANT = 0.030,FW_S_DOT_X = 0.538 * 0.030 = 0.01614):
# 1. 计算原始贝叶斯分类概率
my $prob = ($s * $nn) / ($n * $ns + $s * $nn);
# 2. 如果启用了 Robinson 平滑算法(针对低频出现的 Token)
if (USE_ROBINSON_FX_EQUATION_FOR_LOW_FREQS) {
my $robn = $s + $n; # 该 Token 在所有样本中出现的文档总数
# 使用 Gary Robinson 的 f(w) 公式计算平滑后的概率:
# P_smooth = (s * x + n * P_raw) / (s + n)
$prob = ($Mail::SpamAssassin::Bayes::Combine::FW_S_DOT_X + ($robn * $prob))
/ ($Mail::SpamAssassin::Bayes::Combine::FW_S_CONSTANT + $robn);
}bashrobn 是词库里记录的该词总频次。塞入拼写奇怪的罕见词或生僻词,平滑公式会直接把它的概率拉回 0.5(代表无特征)。概率为 0.5 的词在排序时垫底,进不了 150 个评分窗口。所以,做正文稀释时,必须使用网关已经见过多次的常规商务词汇,不能胡乱生造冷门词。
得分合并的数学核心,在 CombineChi.pm(行 60-104):

# 1. 累加计算双假设 Fisher 对数概率
foreach my $prob (@$sortedref) {
$S *= 1.0 - $prob; # 假设邮件为 Spam 的概率累乘因子 (1 - P(Spam|Token))
$H *= $prob; # 假设邮件为 Ham 的概率累乘因子 (P(Spam|Token))
# 浮点防下溢处理省略(使用 frexp 分解科学计数法)
}
$S = log($S) + $Sexp * LN2; # 计算假设 Spam 的对数似然
$H = log($H) + $Hexp * LN2; # 计算假设 Ham 的对数似然
# 2. 通过卡方逆累积分布函数 (chi2q) 转换为 p 值评分。
# 传入 $wc (即 $token_count) 代表自由度相关的半值参数,省略了重复的乘除2操作。
$S = 1.0 - chi2q(-2.0 * $S, $wc);
$H = 1.0 - chi2q(-2.0 * $H, $wc);
# 3. 将得分归一化到 [0, 1] 区间,作为最终贝叶斯概率分值
return (($S - $H) + 1.0) / 2.0;bashFisher 算法在数学上默认所有词相互独立。但实际邮件中,词语之间关联性非常强。塞入一整段连贯的商务文本,这些关联词会直接扭曲卡方统计的自由度计算,降低分类器判定的把握,反而容易触发误判放行。
Rspamd 的 Bayes 分类器(src/libstat/classifiers/bayes.c)#
和 SpamAssassin 不同,Rspamd 使用的是 OSB(Optimal Score Based)算法。
它的一个核心特点是把单个词(Unigram)的权重设成了 0(行 215)。不管单个词有多敏感,它都不会直接影响最终分值,必须和相邻词语结合组成 Bigram(双词组)或 Trigram(三词组)窗口才会计分。
光堆砌单个单词没用,必须注入连贯的词组或完整的商务短语。在中文场景下, 分词器 如果把词切得太细,容易破坏 OSB 窗口的统计结构。
Rspamd 里的 Robinson 平滑通过 PROB_COMBINE 宏实现(行 217):
// prob: 原始概率, cnt: Token出现总数, weight: Robinson平滑权重, assumed: 默认假设概率(通常为0.5)
#define PROB_COMBINE(prob, cnt, weight, assumed) (((weight) * (assumed) + (cnt) * (prob)) / ((weight) + (cnt)))bash在 bayes_classify_token 函数(行 227-324)中被调用:
// 计算 w
w = (fw * total_count) / (1.0 + fw * total_count);
// 平滑合并概率
bayes_spam_prob = PROB_COMBINE(spam_prob, total_count, w, 0.5);bashRspamd 统计 Token 标志位定义(src/libserver/word.h 与 src/libstat/tokenizers/tokenizers.c)#
分词阶段,Rspamd 会在 src/libserver/word.h(行 34-47)中为 Token 标记不同的 Flag,这些属性采用 bitmask 形式定义:
| 标志名称 | 位偏移量 | 掩码实际值 | 安全对抗及分析含义 |
|---|---|---|---|
RSPAMD_STAT_TOKEN_FLAG_TEXT | 1u << 0 | 0x01 | 纯文本内容提取的常规 Token |
RSPAMD_STAT_TOKEN_FLAG_META | 1u << 1 | 0x02 | 元数据或指令生成的 Token |
RSPAMD_STAT_TOKEN_FLAG_LUA_META | 1u << 2 | 0x04 | 由 Lua 规则接口动态产生的 Token |
RSPAMD_STAT_TOKEN_FLAG_EXCEPTION | 1u << 3 | 0x08 | 异常 Token(例如 URL 提取出的特征) |
RSPAMD_STAT_TOKEN_FLAG_HEADER | 1u << 4 | 0x10 | 邮件 Header 头部字段提取的 Token |
RSPAMD_STAT_TOKEN_FLAG_UNIGRAM | 1u << 5 | 0x20 | 单字/单词一元 Token 标志 |
RSPAMD_STAT_TOKEN_FLAG_UTF | 1u << 6 | 0x40 | UTF-8/Unicode 多字节文本 Token |
RSPAMD_STAT_TOKEN_FLAG_NORMALISED | 1u << 7 | 0x80 | ICU 库成功进行规范化转换的 Token |
RSPAMD_STAT_TOKEN_FLAG_STEMMED | 1u << 8 | 0x100 | 已成功执行词干提取转换的 Token |
RSPAMD_STAT_TOKEN_FLAG_BROKEN_UNICODE | 1u << 9 | 0x200 | Unicode 序列规范化失败或格式损坏;会标记 RSPAMD_TASK_FLAG_BAD_UNICODE,但不对 Bayes 权重做 50% 降权 |
RSPAMD_STAT_TOKEN_FLAG_STOP_WORD | 1u << 10 | 0x400 | 识别为高频中性的停用词 |
RSPAMD_STAT_TOKEN_FLAG_SKIPPED | 1u << 11 | 0x800 | 属于被衰减或跳过的 Token,不参与评分计算 |
RSPAMD_STAT_TOKEN_FLAG_INVISIBLE_SPACES | 1u << 12 | 0x1000 | 包含不可见字符的 Token |
RSPAMD_STAT_TOKEN_FLAG_EMOJI | 1u << 13 | 0x2000 | 包含 Emoji 表情符号的 Token |
BROKEN_UNICODE 和 INVISIBLE_SPACES 是判断是否存在异常绕过行为的重要技术线索。若邮件中包含大量非规范 Unicode 或隐藏格式,容易触发网关的异常拦截逻辑。
词干提取与自定义分词(tokenizers.c 约第 600-650 行)#
词干提取(Stemming)会消除单词变形的影响,例如 "approved" 与 "approves" 会归并为相同的词干 "approv"。这虽然限制了通过同词变形刷计数的做法,但也意味着如果注入大量 "unanimously",等于在全局给 "unanim" 这一族词干刷了强烈的 Ham 信誉。在中文或日文的实际部署中,如果网关引入了自定义的中文分词器,需要摸清其词典边界,OOV的打散退化往往会减弱稀释效果。
所有 Token 在生成时都会计算 Mum-hash 用于 Redis 端的去重。即使使用等价 Unicode 字符替换,哈希值的改变也会使其被当作新词统计。
自动学习决策(lualib/lua_bayes_learn.lua,行 184、368)#
在 can_learn() 逻辑中,Rspamd 设置了自适应防过拟合的保护机制。默认的置信度阈值为 min_prob = 0.95:
-- 判断当前邮件的贝叶斯分类置信度是否已经达到阈值(默认为0.95)
in_class = prob >= (probability_opts.min_prob or probability_opts.spam_min or 0.95)
if in_class then
-- 若系统已经足够确信,则拒绝进一步将其加入训练库中,以防止模型过拟合
return false, reason, ctx.result
endbash如果稀释文本成功将评分卡在拦截线以下,且置信度未达到 0.95,则分类器既不会拦截该邮件,也无法从中学习到最新的特征漂移,从而提供了一个稳定的绕过时间窗口。

Rspamd 的神经网络插件(src/plugins/lua/neural.lua)#
除了贝叶斯层,Rspamd 默认启用了 ANN(神经网络)分类插件。
其后置过滤器回调 ann_scores_filter(源码约在 121 行)在贝叶斯分类器之后执行。这意味着神经网络看到的特征已经经过了贝叶斯层的预处理。
特征提取依赖以下五个 Provider 接口(源码第 33-37 行):
-- 动态加载这五个特征提供者模块
pcall(require, "plugins/neural/providers/llm") -- 外部LLM嵌入特征
pcall(require, "plugins/neural/providers/symbols") -- 规则得分缓存收集
pcall(require, "plugins/neural/providers/text_hash") -- 局部特征哈希
pcall(require, "plugins/neural/providers/fasttext_embed") -- FastText 词嵌入
pcall(require, "plugins/neural/providers/static_embed") -- 静态词嵌入bash在生产环境中,通常仅启用 symbols 和 text_hash。
在 Hybrid特征提取模式下,神经网络把贝叶斯的打分直接当做特征输入的一部分。贝叶斯分数被稀释词干扰降下来后,这个偏低的分数被神经网络直接吃进去。神经网络虽然学到了”低贝叶斯分 = Ham”,但它无法区分这封信是因为干净还是被掺了沙子。
网关串行审计架构在这里存在盲区。
网关开启了 PCA 特征降维时,多封邮件里注入的相似良性文本被当作主成分保留,而原本维度较低的恶意敏感词特征反而容易在降维过程中被过滤掉,导致神经网络失效。
权重继承机制(行 1452)通过 Redis 的 zset 查找 providers_digest 匹配的旧 profile 并复制权重,避免在配置更新时重新训练。
五个 Provider 绝大部分仍然依赖对 Token 流的切分。一旦贝叶斯层削弱了 Token 的区分力,神经网络所接收的输入特征也会随之失效。
Rspamd LLM Provider 的底层细节(llm.lua)#
在 LLM Provider 的工程设计中,有两个机制值得关注:
拼装输入文本:collect_async 中,输入文本强制按主题优先(Subject first)原则拼接:
"Subject: " + 邮件主题 + "\n" + 正文内容bash确保在嵌入向量的位置编码中,高信息价值的主题行始终被置于最前,避免因长文本 截断 而丢失关键信息。
路由与缓存:该插件支持根据邮件检测语言路由到不同的第三方模型,且使用了 Redis 进行本地缓存(TTL 默认 24 小时):
Redis 缓存机制引入了新的规避面。已知目标网关开启了该缓存时,可以提前发送一封内容正常的良性邮件(使用特定主题词),让网关缓存下该特征。后续发送带有相同主题词的邮件即可命中该干净缓存,绕过实时审计。
SpamAssassin 的独立神经网络插件(NeuralNetwork.pm)#
SpamAssassin 的 NeuralNetwork 插件(v0.11.2)走另一条路:不依赖贝叶斯分,而是维护自己独立的词汇表与特征流水线。
隐藏层结构(源码第 767-770 行、第 1327-1330 行):
# 隐藏层1节点数计算:≈ √输入维度 × 0.25 (向上取整)
my $num_hidden1 = int(sqrt($num_input) * 0.25 + 0.5);
$num_hidden1 = 4 if $num_hidden1 < 4; # 最少4个节点
# 隐藏层2节点数计算:为隐藏层1节点数的一半
my $num_hidden2 = int($num_hidden1 / 2);
$num_hidden2 = 2 if $num_hidden2 < 2; # 最少2个节点bash输入层映射至该双隐层结构,并在单输出神经元输出结果。
词表修剪卡方计算(_chi2_score,行 1065):
# 计算特定词项(term)的卡方得分,用于过滤全局区分度弱的特征词
sub _chi2_score {
my ($spam, $ham, $total_spam, $total_ham) = @_;
my $total = $total_spam + $total_ham;
return 0 unless $total > 0;
my $total_spam_noterm = $total_spam - $spam; # 未包含该词的Spam文档数
my $total_ham_noterm = $total_ham - $ham; # 未包含该词的Ham文档数
# 分母为各维频数的乘积
my $denom = ($spam+$ham) * ($total-$spam-$ham) * $total_spam * $total_ham;
return 0 unless $denom > 0;
# 卡方检验矩阵交叉相乘平方差计算,提取鉴别度分值
return ($total * ($spam*$total_ham_noterm - $ham*$total_spam_noterm)**2) / $denom;
}bash在词汇表达到 10,000 的上限进行清理时,其卡方算法偏向于删除低频但有高特异性的词,而保留高频的中性词。使用高信誉的常规商务词汇进行稀释,它们不容易在词汇表清理中被剔除。
类别加权训练(行 817-825):
# 根据训练样本中Spam与Ham的文档比例,为少数类乘以一个反比权重,最大加权值为4.0倍
if ($isspam) {
$class_weight = ($spam_docs > 0) ? $ham_docs / $spam_docs : 1.0;
} else {
$class_weight = ($ham_docs > 0) ? $spam_docs / $ham_docs : 1.0;
}
$class_weight = 1.0 if $class_weight < 1.0;
$class_weight = 4.0 if $class_weight > 4.0;
my $weighted_epochs = int($train_epochs * $class_weight) || 1;bash大量发送被标记为 Ham 的稀释邮件,会稀释掉 ham_docs/spam_docs 的权重比例,直接把整个模型的权重偏向有利于 Ham 判定的方向。
在重训计算”类原型向量”时,一段周期内持续发送含有良性术语的稀释信,这些词录入词表后直接毒化类原型向量,在重训后整体带偏神经网络的判断基准。
SpamAssassin 神经网络插件有四道硬性检测门槛:邮件长度小于 256 字符,或命中词汇表的有效词少于 10 个时,系统直接跳过神经网络检测,退回到贝叶斯层。
正文中把可见字符控制在 200~250 字符(大约 3、4 句话),然后将大段的稀释语料通过 CSS 隐藏(不可见语料不计入正文字数),神经网络就会因为有效字数不足而直接放弃检测,退回贝叶斯层,此时隐藏稀释词已经绕过了它。
其他方案对比:无原生 ML 的 SMTP 架构#
Haraka(Node.js 开发)的内容检测插件不含原生 ML/NLP 组件,只做外部 Rspamd/SpamAssassin 接口转发,或者用 karma 等插件按硬编码规则对信誉、SPF 状态做加减分。自建 Node.js 邮件网关在这块很脆弱——绕过外部检测器之后,本地几乎没有防御。yangdongchao/mailscanner 这类 PyTorch 学术分类器就更不用说了,完全没考虑工程对抗。
0x02 维度一:HTML 多态变异#
网关审计 HTML 时,通常会将其解析为 DOM 树并计算指纹(如 Fuzzy Hash)。短时间内发送的多封邮件在 DOM 树特征上相似度过高,就会被判定为已知模板的变种。
对抗方法:生成 HTML 时对每个收件人的邮件单独做变异编译,用多态方式扰乱 DOM 特征。
2.1 结构注入(Structural Injection)#
编译引擎会在 DOM 树结构中动态引入以下变异:
-
随机嵌套:在
<body>路径下随机引入 1~3 层无样式的<div>/<span>嵌套,其 class 类名完全动态随机生成。 -
空节点引入:随机插入多个
display:none占位符。视觉上不可见,但会影响 DOM 解析树的深度和兄弟节点计数,直接改变特征 Hash 值。 -
属性顺序置乱:在生成 HTML 元素时,随机打乱各标签属性的输出顺序(例如将 href, style, class 顺序倒置)。许多 DOM 解析器是顺序敏感的,这会改变网关的 Hash 指纹。
2.2 等效标签置换(Tag Normalization Bypass)#
编译引擎结合转换映射,随机置换等效标签:
| 原始标签 | 变异标签 | NLP 解析差异 |
|---|---|---|
<b> | <strong> | 语义标签 vs 样式标签 |
<i> | <em> | 同上 |
<strike> | <s> / <del> | 新旧语法标准混杂 |
<center> | <div style="text-align:center"> | 标签 vs CSS 样式 |
<font> | <span style="..."> | 传统样式 vs CSS 样式 |
每次生成以 50% 的概率随机置换,破坏基于标签分布的特征词频统计。
2.3 随机 CSS 格式化#
模板里的类名均采用随机字符代替,同时内联 CSS 样式的属性格式也随机选择:
/* 格式一 — 十六进制 */
color: #ff0000; margin: 10px;
/* 格式二 — rgb函数 */
color: rgb(255, 0, 0); margin: 10px 10px 10px 10px;
/* 格式三 — hsl简写 */
color: hsl(0, 100%, 50%); margin: 10px;bash网关提取 CSS 的正则过滤规则因此无法成功匹配。
0x03 维度二:语义稀释——拉低贝叶斯评分#
语义稀释的数学原理是通过在正文引入大量的强 Ham 词,来拉低贝叶斯联合概率分值,让原本超标的分数降到网关拦截线以下。但工程落地中需要考虑这些稀释词的注入方式,以对抗网关的可见性检测。
3.1 数学原理#
贝叶斯的代数加和公式为:
一封包含”立即登录”、”验证账号”等 10~15 个强敏感词的钓鱼邮件,判定值容易突破 +60(网关拦截线通常为 +40)。
不修改原有核心文案,通过在正文中注入大量强 Ham Token(每个词贡献 -2 至 -5 的得分),将联合分数拉低:
当 Misplaced & 时,系统会自动将该邮件归为 Ham 类别。
根据工程实践,稀释注水词量的注入公式为:
TaiGong 设置了三档注水等级:
| 级别 | 注水比例 (DilutionRatio) | 适用场景 |
|---|---|---|
| Light | 2.0 | 对付消费级邮(Gmail / Outlook) |
| Standard | 5.0 | 对付企业 邮服环境 |
| Aggressive | 10.0 | 高防目标(金融、央企) |

3.2 七种文本注入手段#
按检测难度从低到高排列:
[#1](javascript:😉 HTML 注释区
<!-- quarterly budget review meeting scheduled for next Thursday -->bashHTML 注释区注入最基础——直接把词写在 <!-- --> 里。现代网关分词时一般会自动过滤注释,只对极少数老旧系统有效。
[#2](javascript:😉 同色极小字体
<span style="font-size:1px;color:#ffffff">budget allocation approved</span>bash用 CSS 把字体设成 1px、颜色设成与背景相同。简单网关上有效,但带可见字符占比审计的高级网关会触发惩罚。
[#3](javascript:😉 overflow:hidden 与零高度容器
<div style="height:0;overflow:hidden;max-height:0;opacity:0">
team meeting scheduled for Thursday
</div>bashoverflow:hidden 配合零高度容器(height:0; max-height:0; opacity:0),比单一隐藏样式更稳定,不同环境下表现一致。
[#4](javascript:😉 ARIA 语义隐藏
<span aria-hidden="true" role="presentation" style="position:absolute;left:-9999px">
project milestone review
</span>bashARIA 无障碍属性也能用来隐藏:aria-hidden="true" role="presentation" 配合 left:-9999px 绝对定位,读屏器会跳过这段内容,但直接提取文本的网关引擎照读不误。
[#5](javascript:😉 mime multipart/alternative 差异注入复合格式邮件走 multipart/alternative 结构:text/plain 放良性长文本,HTML 放实际内容。网关为了性能往往初筛只扫 plain 部分——第一轮评分就这样拉下来了。
[#6](javascript:😉 CSS 伪元素 content 注入
<style>
.d1::after { content: "budget allocation quarterly review"; }
.d2::before { content: "team meeting scheduled for Thursday afternoon"; }
</style>
<div class="d1"></div>
<div class="d2"></div>bashCSS 伪元素 ::before / ::after 注入则更难检测。稀释内容写入样式表的 content 属性,普通文本提取器只读 DOM textContent,读不到 CSSOM 的计算值。
[#7](javascript:😉 JavaScript 动态延时渲染
<div id="dilution_container" style="display:none">...</div>
<script>
setTimeout(function() {
document.getElementById('dilution_container').style.display = 'block';
}, 5000);
</script>bashJavaScript 延时渲染是检测难度天花板。容器初始状态设为隐藏,JS 延时数秒后再改为显示,网关一般不会等待 JS 执行完毕,扫描时这部分词被忽略,用户端依然可以渲染。
3.3 语料库筛选#
语料库分三层:
-
Layer 1 — 通用商务(基础 500条):全是项目审批、会议通知等纯商业文案(比如“本季度财务预算已经提交审阅”)。
-
Layer 2 — 行业特异(约 200条):根据每个行业所接触的邮件预料不同而塞入不同的词组进行干扰,因为目标行业的网关本就会经常放行这类词,把它们视为极高 Ham 概率特征。
-
Layer 3 — 动态生成:调用 LLM 获取当天或本周的商业、时政热点,组装成和发信场景匹配的模板,也许能绕过网关的缓存名单?
Token 筛选准则:SpamAssassin 分值为负、长度超 5 字符、不带任何 URL 链接,三个条件缺一不可。
0x04 未完成的维度三:HTML5 Smuggling 流量层绕过#
绕过网关内容过滤后,进一步投送可执行程序(Cobalt Strike 载payload)时,还需要解决网络流量和附件特征的检测问题。
HTML5 Smuggling 提供了流量层免杀的思路:让文件在受害者本地的浏览器内存中生成,而不是直接在网络上传输。
暂时没有找到好的思路实现这一目标,目前来说这只是一个设想,因此也不过多套困了,具体的思路可以搜索相关关键词。
4.1 落地页上的对抗#
TaiGong 实战中,目前只是语义稀释配合落地页一起用。
效果不算很好,究极的理想状态就是我说的:受害者点击链接进入落地页后,后台执行 Smuggling ,同时往网关塞入大量良性商务免责声明等 Ham 文本,能够有效阻挡安全策略对 URL 的爬行和标记就如愿了。
0x05 结语#
从开源产品来看,策略应该都是大同小异的,就是不清楚闭源设备有LLM的参与之后,进化会到何种地步。
更根本的防线在终端上。
参考资料:
-
_Proofpoint: Introduction to HTML Smuggling, _https://www.proofpoint.com/us/blog/threat-insight/introduction-html-smuggling ↗
-
_Proofpoint: HTML Smuggling Evolves - Threat Actor Adoption and Detection, _https://www.proofpoint.com/us/blog/threat-insight/html-smuggling-evolves ↗
-
_SpamAssassin Bayes Scoring Documentation, _https://cwiki.apache.org/confluence/display/SPAMASSASSIN/Bayes ↗
-
_Rspamd Documentation: Statistics and Bayes, _https://rspamd.com/doc/configuration/statistic.html ↗
-
_Microsoft 365 Defender Anti-Phishing Policy Reference, _https://learn.microsoft.com/en-us/defender-office-365/anti-phishing-policies-about ↗
-
_IcedID Banking Trojan Leveraging HTML Smuggling (2021), _https://www.mcafee.com/blogs/other-blogs/mcafee-labs/icedid-banking-trojan-uses-html-smuggling-technique/ ↗
-
_SpamAssassin Bayes Plugin source_https://github.com/apache/spamassassin/blob/trunk/lib/Mail/SpamAssassin/Plugin/Bayes.pm ↗
-
_SpamAssassin CombineChi source: _https://github.com/apache/spamassassin/blob/trunk/lib/Mail/SpamAssassin/Bayes/CombineChi.pm ↗
-
_Rspamd Bayes classifier source: _https://github.com/rspamd/rspamd/blob/master/src/libstat/classifiers/bayes.c ↗
-
_Rspamd Neural Network plugin source: _https://github.com/rspamd/rspamd/blob/master/src/plugins/lua/neural.lua ↗
-
_Rspamd Neural Network providers (LLM): _https://github.com/rspamd/rspamd/blob/master/lualib/plugins/neural/providers/llm.lua ↗
-
_Rspamd Tokenizer source: _https://github.com/rspamd/rspamd/blob/master/src/libstat/tokenizers/tokenizers.c ↗
-
_Rspamd Bayes autolearn: _https://github.com/rspamd/rspamd/blob/master/lualib/lua\_bayes\_learn.lua ↗
-
_SpamAssassin NeuralNetwork Plugin source: _https://github.com/apache/spamassassin/blob/trunk/lib/Mail/SpamAssassin/Plugin/NeuralNetwork.pm ↗
-
_Haraka Plugin Ecosystem: _https://github.com/haraka/Haraka ↗
-
_yangdongchao/mailscanner (Python ML): _https://github.com/yangdongchao/mailscanner ↗
系列导航