☰
SMTP流量中的Base64钓鱼邮件:Wireshark解码与攻击链还原
2026/10/22 12:05:25 网站建设 项目流程

每次在BUUCTF上看到“大流量分析”四个字,心里都会先咯噔一下。流量分析题的上限和下限差距非常大,熟练的人几分钟就能从几百MB的pcap里捞出关键报文,不熟悉的翻半天包只能看到满屏的TCP重传和乱七八糟的握手包。这道题为“解码SMTP流量中的Base64钓鱼邮件”,名字其实已经把所有考点写得很透明了:SMTP、Base64、钓鱼邮件,三个关键词串起一条完整的攻击链——攻击者通过SMTP协议投递邮件,邮件正文或附件用Base64编码隐藏真实内容,受害者一旦查看或点击附件就会中招。我做完这道题之后最大的感受是:它考察的不是单个孤立知识点,而是你能不能把邮件协议、MIME结构和编码原理串起来,去还原一次贴近真实的钓鱼攻击。这篇文章就是我的完整解题笔记,从拿到pcap包开始,到最后还原攻击链,每一步都拆开讲清楚。

1. 拿到题先别急着开Wireshark:先搞懂出题人想考什么

1.1 从题目名拆解考点:SMTP、Base64、钓鱼邮件分别意味着什么

“SMTP流量中的Base64钓鱼邮件”这个描述,本质上是三类核心知识的组合。先说SMTP,它是电子邮件传输的标准协议,默认端口25,明文传输。这段流量里必然能看到完整的邮件会话过程——客户端连上服务器后先发EHLO打招呼,再MAIL FROM声明发件人,RCPT TO声明收件人,DATA之后就是邮件正文。由于历史原因,SMTP在设计之初只支持7位ASCII字符,所以二进制内容、中文内容、文件附件都要经过编码后塞进信封里传输。Base64就是最常用的一种编码方式,它能把任意二进制数据映射成64个字符的子集,在邮件协议里扮演“打包工”的角色。

再说“钓鱼邮件”。在CTF题目里,钓鱼邮件通常意味着两个考点:一是邮件内容里有恶意链接或诱导性文案,二是可能带一个伪装成PDF、DOC、压缩包的恶意附件。题目明确说了“Base64钓鱼邮件”,那我基本可以断定,关键数据藏在Base64编码后的MIME部分里,要么是正文中的恶意链接,要么就是一个附件。带着这种预期去看流量包,效率会高很多,而不是盲人摸象式地到处翻包。

1.2 打开流量包的第一步:先看全局信息再做精细定位

拿到pcap包后,我的习惯是先花十几秒看一眼全局信息,而不是上来就敲过滤器。Wireshark的Statistics菜单下有Protocol Hierarchy和Conversations两张表,前者能看出这个包里哪些协议占比最高,后者能看出主机之间有哪些大流量会话。这样做的目的是快速建立对流量包的“体感”。

如果是上百MB的大流量包,光是SMTP流量可能只占很少一部分,剩下全是TCP背景流量、HTTP请求、DNS查询或者SSL握手。先看协议层级能帮我确认:这包里的SMTP是不是真的存在、集中在哪个IP段、大概占了多大比例。顺便说一句,很多新手容易犯的毛病是一上来就Follow TCP Stream,结果跟着跟着就跟到一条无关流量里去了,半天找不到邮件。先看统计信息,再针对IP和端口过滤,才是大流量分析的正常姿势。

这一步还有一个关键操作是快速看一眼Expert Info(在左下角或者通过Analyze菜单打开),里面会标记异常包,比如TCP重传、乱序、异常标志位。大流量包里这些噪音非常多,没必要逐个排查,但如果有明显的邮件错误响应,比如“550 relay denied”,那可能就是出题人埋的干扰项。

2. 在Wireshark里精准定位SMTP会话:从过滤器到TCP流

2.1 用smtp过滤器把邮件会话从几百MB包里捞出来

全局概览做完后,真正的肉搏开始了。Wireshark的显示过滤器支持直接写smtp,它会把所有SMTP协议报文过滤出来。如果包里有端口25的明文流量,这个过滤器通常能一锤定音。

不过在大流量包场景下,我建议再细化一点。先用smtp && ip.addr == 10.0.0.1之类的条件锁定嫌疑主机,如果不知道嫌疑IP,就先看Conversations表里谁是25端口的通信双方。还有一个小技巧是,smtp.req.command这种字段可以精确匹配命令,比如过滤出所有EHLO、MAIL FROM、DATA请求:

smtp.req.command == "EHLO"

如果你不确定过滤字段是否有效,可以直接敲smtp.req,Wireshark会自动补全字段名。千万别硬背过滤器语法,打开过滤表达式对话框,输入smtp几个字母,候选字段都列出来了,选就是了。我比较喜欢用smtp先粗过滤,然后右键任意一条SMTP报文,选择Follow -> TCP Stream,直接在流视图里翻邮件会话。

下面是一个典型的SMTP会话流(实际解题中看到的报文经过脱敏处理):

220 mail-server ESMTP ready EHLO client 250-smtp-server MAIL FROM:<from@example.com> 250 2.1.0 Ok RCPT TO:<to@target.com> 250 2.1.5 Ok DATA 354 End data with <CR><LF>.<CR><LF> Subject: =?UTF-8?B?6IGU56C7?= MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_12345" ------=_Part_12345 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: base64 5L2g5aW9IHdlbGNvbWUg ------=_Part_12345-- . 250 2.0.0 Ok: queued as 3A2B4C QUIT

看到这串东西,基本就可以确认这封邮件用的是Base64传输编码了。邮件头里Content-Transfer-Encoding: base64是找解码目标的最重要线索,后续所有关键操作都围绕它展开。

2.2 区分同一封邮件的不同流:Message-ID和MIME边界是导航仪

大流量包里经常不止一封邮件,甚至同一个IP段里有多条SMTP会话交杂在一起。如何判断自己看的流是同一封邮件?两个标记很关键:Message-ID和boundary。

Message-ID是每封邮件的唯一标识,出现在邮件头里,形如<a1b2c3d4@mail-server>。同一个Message-ID的所有DATA部分属于同一封邮件。MIME的boundary则是分割邮件各部分的界线,只要看到Content-Type: multipart/mixed; boundary="----=_Part_12345",后面直到结束标记------=_Part_12345--之间的内容都属于同一个MIME实体。

实际操作中,我会先把包含RCPT TO和DATA的流挨个打开,快速浏览邮件头和正文。如果发现有明确乱码或Base64字符块的流,把它记下来,用Message-ID做标记。有些出题人喜欢把“钓鱼”部分放在邮件的HTML正文中,有些则放在附件里。只有理清了boundary的起止位置,才能准确判断这个Base64块对应的是正文还是附件。

这里还有一个容易踩的坑:邮件可能包含多个MIME部分,每个部分是独立的Base64块。如果你只复制了第一个Base64块就去解码,解出来的可能只是祝福语或空白文本,真正藏flag的附件在第二个块。所以解码头两步一定先数清楚:这封邮件里一共有几个boundary,每个boundary间的内容是什么类型。别嫌麻烦,这一步决定了后面能不能一击命中。

3. 解码Base64邮件正文与附件载荷:工具、参数与坑

3.1 手动解码还是脚本解码?我两个都试了

定位到Base64字符块之后,解码方法很简单但门道不少。Base64编码字符集中包括大小写字母、数字、+、/、=(填充),有时出现换行符是正常的,因为MIME规范规定每行不能超过76个字符,编码器会插入CRLF换行。

最直接的方式是复制Base64字符串,用命令行工具解码:

echo "5L2g5aW9IHdlbGNvbWUg" | base64 -d

这个命令在Linux和macOS上都能用。但邮件里的Base64可能会有很长的换行,直接echo整串非常容易出错。我的建议是先把Base64内容保存到文件里再解码:

cat body.b64 | base64 -d > body.decoded file body.decoded

更稳妥的方式是用Python三行代码搞定:

import base64 with open("body.b64", "r") as f: data = f.read().replace("\n", "").strip() decoded = base64.b64decode(data) with open("body.decoded", "wb") as f: f.write(decoded)

注意一点:Python的b64decode在遇到非法字符时会抛异常,如果你发现数据里有奇怪的符号,可以检查一下邮件头里的Content-Transfer-Encoding到底是base64还是quoted-printable,后者用的是=XX格式的十六进制编码,解码方式完全不同。这是我在实际做题中吃过的一次亏,拿到一串“=E6=9C=89=E5=BA=8F...”就当Base64处理,结果怎么解都不对,后来才意识到这是quoted-printable。

3.2 一封真实钓鱼邮件的完整解码过程

我在这个包里实际看到的邮件,正文部分解码后长这样(经过脱敏处理):

Hello user, Your order has been shipped. Please click the link below to track your package: http://tracking-logistics.update-package-status.com/track?id=MjA4OTk= Sincerely, Logistics Team

看到这种文案,第一反应就对了:“包裹已发货,请点击链接查询物流”——这是邮件钓鱼最经典的诱导模板之一。钓鱼者会伪造一个看似官方的通知,诱使受害者点击背后的恶意链接。链接带上?id=MjA4OTk=这样的参数,后面的Base64字符串又是一个隐藏信息:MjA4OTk=解码出来是20899。这个编号很可能是受害者的“订单号”或者“用户编号”,钓鱼者用它在后台关联攻击数据。

再看另一段Base64附件。这个包的MIME部分里有一个名为Invoice-2025.doc的附件,它的Base64块非常大,解码后用file命令识别:

file decoded_attachment

如果输出是Composite Document File V2,那说明这是一个老式OLE格式文档,可以进一步用工具检查宏代码;如果输出是PDF document,那就得去提取其中的JavaScript或恶意跳转。我在这个包里实际遇到的附件解码后是PDF,里面藏了一个/URI对象,指向的是一段跳转链接。这种附件型钓鱼比纯正文链接更隐蔽,因为它不只是“点个链接”,而是诱导受害者下载打开“发票/订单/合同”文档,触发文档内嵌的恶意代码。

结合实际经验,把Base64解码后的邮件结构整理一下:

数据块解码结果线索价值
邮件正文钓鱼话术英文模板,带URL链接锁定恶意域名与URL参数
附件Invoice-2025.docOLE格式文档需进一步检查宏或漏洞利用载荷
恶意链接参数MjA4OTk=字符串20899可作为IDS规则特征或溯源线索
邮件头X-MailerMicrosoft Outlook 16.0识别伪造发件端

3.3 一定要留意的嵌套编码:Base64套Base64,甚至Base64套Hex

CTF出题人特别喜欢玩“套娃”。第一次做这类题时,我解码Base64后看到一长串十六进制字符,以为是文件内容,结果又是ASCII字符串,再经过一次Base64解码才看到明文。后来我总结了一个经验:解码后先别急着下结论,先把输出结果的前几十个字符列出来,判断它们是不是可打印ASCII、十六进制字符或者某种文件头。

十六进制字符的特征是只有0-9a-f,ASCII明文会有大量可打印字母,压缩包文件头是50 4B 03 04,PNG图片文件头是89 50 4E 47。如果解码结果是十六进制字符串,可以尝试反转fromhex后再看一次;如果解码结果本身是可打印字符但毫无语义,可以再跑一遍Base64解码。这个过程可以用一个简单脚本连续剥壳:

import base64, binascii s = open("body.b64", "r").read().replace("\n", "") for i in range(5): try: decoded = base64.b64decode(s) print(f"Round {i}: {len(decoded)} bytes") s = decoded except Exception: break print(s[:500])

每次打印解码后的字符片段,快速判断是否还有下一层。这种做法在流量分析题里很常用,因为攻击者为了规避关键字检测,往往会把payload编码很多层。

4. 还原完整攻击链:从钓鱼邮件一路追到流量中的恶意行为

4.1 追踪点击后的流量:从SMTP延伸到HTTP和其他协议

只解出邮件正文里的钓鱼链接,得分还不够。我继续在流量包里过滤该恶意域名或恶意IP的通信,顺着攻击链往后走。先在Wireshark里用dns.qry.name contains "tracking-logistics"过滤DNS请求,看看受害者主机是否真的查询过这个域名;再过滤HTTP请求:

http.host contains "tracking-logistics"

如果受害者没有解析这个域名,那说明攻击链路在“邮件查看”这一步就断了——但也可能是邮件客户端自动预取了链接(Outlook和部分Web邮件客户端会做链接预览)。如果受害者真的点开了链接,DNS和HTTP流里都会出现对应记录。

实际在这个包里,我发现受害者确实请求了/track?id=MjA4OTk=这个URL,服务器返回了一个HTML落地页。继续追踪TCP流,落地页里有一个外链脚本,脚本地址指向一个用于分发恶意文件的云存储域名。再往下跟进,受害者随后发起了一个针对这个云存储域名的GET请求,下载了名为order-details.zip的压缩包。

这时攻击链就非常完整了:伪造物流通知邮件 -> 诱导点击链接 -> 落地页加载恶意脚本 -> 下载ZIP压缩包 -> 受害者解压执行 -> 载荷落地。出题人把这条链路分散排布在SMTP、DNS、HTTP、TCP四个层面里,题目叫“大流量分析”一点不虚,它考察的其实是跨协议追踪的能力。

4.2 做题避坑与抓流量分析的几条实战经验

第一,一定养成“先过滤后跟踪流”的习惯。直接Follow TCP Stream容易陷入海量背景流量,尤其当包里有多条SMTP会话并交织在多个TCP连接中时。先用smtp过滤,再锁定tcp.stream序号,把邮件会话完整导出,思路就会非常清晰。

第二,留意邮件头伪造。SMTP流量里的MAIL FROM、RCPT TO是信封信息,邮件头里的From:、To:才是显示信息。两者可以不一致。在这个包里,发件地址看似来自某公司客服邮箱,但起头EHLO的客户端IP段和该公司的真实邮件服务器毫无关联。流量分析里,用IP反查和证书信息来交叉验证发件人是否真实,是一个很重要的取证意识。

第三,把Base64解码后的数据当“第一线索”而不是“最终答案”。Base64只是编码不是加密,解码之后的内容往往只是下一步的入口。真正的恶意行为,通常埋在解码后的附件或链接指向的后续流量里。如果你解完码拿到了一串域名,记得马上去过滤DNS和HTTP流量,这就是破局点。

第四,善用Wireshark的导出对象功能。对HTTP下载的ZIP文件,不需要手动复制TCP流字节。File -> Export Objects -> HTTP可以直接把下载文件导出来,保持原始文件完整性。对SMTP里的附件,则需要在Follow TCP Stream里显示原始数据模式,再另存为文件。很多人解完Base64后手里只是一串字符,却没有还原成原始文件,导致后续文件分析无从下手。

关于流量分析类题目的工具选择,我个人的习惯是Wireshark为主,tshark做命令行辅助。当pcap文件很大时,Wireshark界面操作会卡顿,但用tshark快速提取字段就很舒服:

tshark -r 大流量.pcap -Y "smtp" -T fields -e smtp.req.command -e smtp.req.parameter

这个命令可以把所有SMTP命令和参数一次性列出来,比在GUI里翻包快得多。再配合-e frame.time_relative看时间轴,攻击链的先后顺序一目了然。

4.3 基于这道题的一点延伸思考:真实世界里的邮件攻防

做完这道题,我最大的触动是它和真实企业安全场景高度重合。邮件钓鱼至今仍然是最有效的初始入侵途径之一,攻击者投递钓鱼邮件后,技术细节和这道题几乎一模一样:Base64编码的HTML正文、伪造的物流通知、指向恶意域的URL、附带恶意宏的文档。区别只是真实攻击的恶意样本会不断变种,域名和IP会频繁更换。

在企业防守侧,对SMTP流量的检测重点应该放在邮件附件内容和URL信誉上。对Base64编码的邮件正文,简单的字符串匹配很难发现恶意特征,但通过对解码后的内容做深度分析,比如识别语义模板、提取URL并查询威胁情报、检查附件的OLE对象和宏代码,就能在邮件到达用户收件箱之前拦下一批攻击。单纯靠垃圾邮件规则过滤,很容易被编码和混淆绕过去。

所以这类流量分析题目虽然看起来是在“考古”pcap,但实际上是在帮我们建立一种协议层面的攻击感知能力。协议字段里的每一个异常都可能是一次攻击的入口,解码和还原是把异常翻译成攻击故事的关键。以后不管是在CTF比赛里遇到变形考法,还是在真实工作中排查APT事件,这套“从全流量出发 -> 定位协议会话 -> 解码数据单元 -> 跨协议追踪指令”的思路都能直接用上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询