钓鱼攻防这几年已经成了红队演练里的必考科目,但在众多钓鱼方式里,用 Excel 文档做载体一直是我个人觉得最值得深挖的方向。原因很简单:员工可以不上外网点链接,却很难不打开一份带账单、工资条或排班表的 Excel 文件。虽然很多团队把精力花在了 Web 钓鱼和免杀木马上,但 Excel 钓鱼这种"低技术门槛、高渗透率"的攻击方式,往往才是真正撕开内网第一道口子的那根针。
这篇文章我会从攻击者的视角,聊聊免杀 Excel 钓鱼文档的制作思路和关键细节,也会把免杀背后对抗杀软的原理讲透。文中的代码和操作都只用于授权环境下的安全测试,所有效果都是演示性质,不包含真实可利用的恶意载荷。希望通过这篇文章,红队能少踩一些坑,蓝队也能搞清楚到底该在哪个环节布防。
1. Excel文档为什么成了钓鱼邮件的常客
很多人不理解,为什么非得是 Excel?PDF 不行吗?HTML 不行吗?我在实战里感受最深的一点是:信任惯性。办公室员工每天都要处理 Excel 表格,Excel 在办公场景里的出现频率太高了,高到不会产生任何警觉。收到一个"供应商对账单.xlsx"或者"2025年考勤表.xlsm",第一反应就是双击打开,而不是像收到一个 .exe 那样下意识犹豫。
1.1 功能太多,可被利用的点也就多
Excel 不只是一个表格编辑器,它内嵌了几套能执行代码的机制,这才是它真正危险的地方:
- VBA 宏:历史最悠久、使用最广泛的脚本载体,可以调用 Windows API、执行 PowerShell、下载载荷。
- DDE(动态数据交换):Windows 古老的进程间通信机制,Excel 可以直接通过 DDE 命令拉起系统命令行,而且不需要任何宏,安全提示也比宏少得多。
- 外部链接与数据连接:Excel 支持从远程 URL 拉取数据,攻击者可以把恶意代码放在远端,文件本身不携带恶意内容,静态扫描很难查出来。
- Excel 4.0 宏:一种比 VBA 更老的宏格式,定义在宏表里,只靠几个函数就能拼出恶意逻辑,许多杀软对它的解析都不完整。
一个攻击者拿到这些能力后,几乎可以为所欲为。而且 Excel 文档的格式结构本身就复杂,OLE 复合文档里嵌套着各种流和目录,解析器稍微不严谨就会出现检测盲区。相比 PE 文件那种相对统一的结构,Excel 文档对杀软来说是个更难啃的骨头。
1.2 员工每天都要用,视觉疲劳就是最好的掩护
现在社交媒体上的钓鱼模板做得再花哨,也不如一份"平平无奇"的 Excel 表格容易让人卸下防备。你看热搜词里那些"excel表格实践训练题"、"excel使用技巧大全",就能明白 Excel 对一般人来说意味着什么——它就是普通打工人每天接触最多的工具。当攻击者把恶意内容伪装成一份绩效排名表、一份项目排期表,甚至一份防疫统计表的时候,受害者几乎没有概率会产生"这文件不对劲"的想法。
这就是 Excel 钓鱼的核心优势:它不是利用技术漏洞,而是利用人的工作习惯。
2. 一条完整的Excel钓鱼攻击链条拆解
免杀只是 Excel 钓鱼里的一个环节,要把整个攻击链路捋清楚,才知道该在哪些地方做文章。我把一次完整的 Excel 钓鱼攻击拆成了五个阶段。
2.1 阶段一:生成带载荷的Excel文档
攻击者通常不会老老实实地在 Excel 里手写宏,而是通过脚本批量生成不同主题的恶意文档。比如用 Python 操作 Excel,这在安全圈里已经是常规操作了,热度词里那一堆python写入excel、pandas读取excel文件就能看出来,Python 处理 Excel 的能力早就被玩透了。
这里我给一个最基础的思路:先用 Excel 创建一份带 VBA 宏的模板文件(.xlsm),然后使用openpyxl替换文档内容,保留宏模块,最后另存为一伤看起来完全不同的表格。
import openpyxl # load_workbook 带上 keep_vba=True 才能保留宏 wb = openpyxl.load_workbook("template.xlsm", keep_vba=True) ws = wb.active ws["A1"] = "2025年3月员工绩效汇总表" ws["B2"] = "部门" ws["C2"] = "个人得分" # 批量填入伪造数据 for i in range(3, 20): ws.cell(row=i, column=2, value=f"部门{i-2}") ws.cell(row=i, column=3, value=70 + i) wb.save("绩效汇总_202503.xlsm")这段代码的核心是keep_vba=True,如果你不加这个参数,openpyxl保存的时候会把宏丢掉,文件就变成了普通表格。除了openpyxl,也有人用win32com操作本地 Excel 实例直接写出带宏的文档,但那种方式需要安装 Office,不适合大规模批量生产。
2.2 阶段二:投递与诱导
文档生成后,投递方式通常有三种:邮件附件、即时通讯工具、网盘链接。邮件附件是首选,因为邮件可以伪造发件人,还能用"紧急""账单""报销"之类的标题制造压力,让受害者来不及细想就点击附件。
诱导是整个链条里最容易翻车的一环。如果文档依赖宏执行,那受害者打开文档后必须手动点击"启用宏"按钮,这个动作就会劝退一大批人。所以攻击者会绞尽脑汁让用户主动启用宏:要么在文档里用艺术字写"此文档受保护,请点击启用内容",要么仿冒微软的安全提示,甚至直接把文档设置成Protected View页面样式。
2.3 阶段三:宏执行与载荷落地
一旦用户启用了宏,Workbook_Open或者Auto_Open事件就会触发。攻击者通常不会直接在宏里写最终恶意行为,而是先执行一段很小的下载器代码,从远程服务器拉取下一阶段的载荷,再在内存里直接运行。这样做的目的很单纯:减少恶意代码在磁盘上的暴露时间,因为杀软的实时监控对落盘文件查得最狠。
2.4 阶段四:权限维持与横向移动
拿到初始权限后,攻击者就会进入后渗透阶段。这个阶段和 Excel 钓鱼本身已经没有太大关系,但文档里释放的载荷往往是 Cobalt Strike 之类的远控木马,用来建立持久化通道,然后再通过内网渗透拿下更多机器。
2.5 阶段五:收尾与隐藏痕迹
成熟的攻击者会在执行完毕后删除临时文件、清理 Office 最近打开记录、修改宏的编译时间戳,减缓蓝队溯源速度。这一点很多人会忽略,但对实战来说至关重要。
3. 免杀的本质:先搞清楚杀软在审查什么
很多刚接触免杀的人有个误区,以为免杀就是把特征码改一改,换个壳就完事。其实真正的免杀是一场持续的对抗,你得先搞清楚杀软到底站在哪些位置盯着你看。
3.1 静态检测:扫描文件本身
杀软对 Office 文档的静态检测主要是三件事:特征库匹配、机器学习分类、结构分析。
特征库匹配最好理解,恶意文件中只要有一串字节和病毒库里的特征相同,就会被命中。针对 VBA 宏,杀软会提取宏源码,做字符串匹配,常见的关键词比如Shell、PowerShell、DownloadString、CreateObject全是高危特征。
机器学习分类是近几年的主流。杀软会提取宏的结构特征,比如函数调用的顺序、字符串拆分的方式、变量命名的混乱程度,交给模型打分。这也就是为什么很多"只要把恶意函数拆开用变量拼接"的简单混淆,现在不再那么管用——模型的鲁棒性比单纯的特征匹配强太多。
结构分析则是检查 OLE 文件本身。比如vbaProject.bin里的压缩数据是否异常、宏是否在不可见的工作表里、文档里是否存在可疑的外部连接,都可以作为研判依据。
3.2 动态检测:沙箱运行看行为
静态扫描看不出的问题,就交给沙箱。杀软会把 Office 文档放到虚拟环境里执行,观察一段时间内的行为:
- 有没有尝试访问敏感 API(如
VirtualAlloc、CreateProcess) - 有没有创建子进程(比如 Excel 去调用 PowerShell)
- 有没有连接外部网络地址
- 有没有在短时间内连续写入多个文件
所以你在设计免杀方案时,不仅要躲静态,还要躲沙箱。简单来说,就是让文档看起来"像一个无辜的表格",而不是"一个拼命干坏事的脚本"。
3.3 信誉查询:云端情报比对
现在的杀软基本都带着云查杀。文件被执行时,杀软会计算文件的哈希,和云端数据库比对。如果样本从未出现,会被判定为可疑;如果样本已经在其他机器上被确认为恶意,那主机这边的拦截概率会大很多。
这条对攻击者来说是最被动的——你精心做的免杀文档,放出去一次被抓,下次同款样本基本就废了。所以做免杀文档不能一个样本反复用,得考虑每轮测试更新一次哈希和混淆策略。
3.4 免杀的本质是"干扰评审"
理解了上面这些,你就能明白免杀的本质不是"让文件无毒",而是让杀软无法可靠地判断文件有毒。干扰静态特征、干扰沙箱体验、打乱信誉比对,这三件事同时做,才能撑过最容易被抓的那几分钟。
4. 亲自做一份免杀Excel文档:思路与代码示例
下面我演示一份简单但完整的 Excel 钓鱼文档制作过程。再次强调,只用于授权环境的安全测试,这里的载荷我用calc.exe做演示,不会造成任何实际破坏。
4.1 第一步:把恶意字符串切碎
最基础的宏免杀手段是字符串拆分和编码混淆。杀软扫描宏的时候,如果整段源码里出现cmd.exe /c calc这种连续字符串,大概率直接命中。但如果你在宏里把字符串拼出来,很多旧引擎就傻了。
我写了一个 Python 脚本,输入要执行的命令,自动生成经过拆分的 VBA 代码:
def gen_vba(cmd): # 把命令切成每段4个字符 chunks = [cmd[i:i+4] for i in range(0, len(cmd), 4)] lines = [] lines.append("Sub Workbook_Open()") lines.append(" Dim payload As String") lines.append(" Dim chunk As String") for ch in chunks: lines.append(f" payload = payload & Chr(34) & \"{ch}\" & Chr(34)") lines.append(" Shell payload") lines.append("End Sub") return "\n".join(lines) vba_code = gen_vba("calc.exe") print(vba_code)生成出来的 VBA 代码大致长这样:
Sub Workbook_Open() Dim payload As String Dim chunk As String payload = payload & Chr(34) & "calc" & Chr(34) payload = payload & Chr(34) & ".exe" & Chr(34) Shell payload End Sub这种水平只是能骗过老式特征码,遇到机器学习百分百被教育。所以更进阶一点,要用上Chr()函数动态拼出敏感字符串,配合数组拼接和变量名混淆,给杀软的静态分析制造障碍。
4.2 第二步:用 DDE 绕过宏启用限制
DDE 的最大优势是不需要启用宏。攻击者在 Excel 单元格里写入一条类似下面的公式,当文档被打开时,Excel 会尝试解析并执行命令:
=cmd|'/c calc.exe'!A1只要用户看到对话框点了"是",系统就会拉起命令行并执行calc.exe。这比宏更可怕,因为用户根本不知道"允许 DDE 连接"按钮是危险操作。
防御 DDE 的办法也很简单,我建议所有企业直接关闭 Office 的 DDE 功能。注册表位置如下:
HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Excel\Options DWORD 值: DDEAllowed 设置为: 0如果你的环境是 Word,路径里的Excel换成Word即可。
4.3 第三步:利用 Excel 4.0 宏和隐藏工作表
如果你的目标是绕过一些对 VBA 查得特别严的终端,Excel 4.0 宏是一个值得研究的冷门方向。Excel 4.0 宏不写在 VBA 模块里,而是直接写在工作表中。具体做法是在工作簿里插入"MS Excel 4.0 宏表",然后在宏表单元格里填入类似下面的内容:
=EXEC("calc.exe") =RETURN()这种宏格式的历史包袱很重,不少杀软的解析器没想到还有人在用,所以检测覆盖度参差不齐。你还可以把宏表所在的 sheet 设为xlVeryHidden,用户连右键取消隐藏都找不到页面。
结合 Python 生成文档的话,操作就变成:先用openpyxl打开模板,把宏表藏好,然后填充可见 sheet 的内容,确保用户打开后只会看到一份正常的表格。
4.4 第四步:应对沙箱的几种实践
免杀做得好不好,很大程度要看能不能骗过沙箱。这里有几条我实测比较有用的思路:
- 时间延迟:宏执行前先
Sleep几十秒,很多沙箱没耐心等;但注意,用户体验会很差,延迟太明显会被用户关闭文档。 - 环境探测:检测当前进程里有没有
vmware、vbox相关进程,检测 CPU 核数、内存大小、屏幕分辨率,发现是虚拟机就不执行。 - 鼠标轨迹判断:用 API 获取用户鼠标是否在动,沙箱里鼠标往往不动或者呈规则移动。这类判断非常简单却有效。
- 分阶段执行:宏不直接在文档里干活,而是把一段 PowerShell 脚本注入到
regsvr32.exe或者rundll32.exe这样的白名单程序里,用白进程执行黑流量。
分阶段执行有一个额外的好处:沙箱里的杀软看到的只有第一阶段的无害下载代码,第二阶段直接从内存远程加载,磁盘上没有痕迹,静态检测完全失效。
5. 那些实测中容易翻车的细节
我把这几年踩过的坑集中写在这里,给做攻防测试的朋友当个提醒。
5.1 宏安全设置和"启用宏"按钮
如果你用的是Auto_Open这种老名字,Office 现在默认会拦得更厉害。建议使用Workbook_Open事件,它在加载文档时自动触发。另外,宏的安全级别设置为"禁用并通知"时,用户至少还看得到"启用内容"按钮;但如果企业组策略做了"禁用所有宏"并且"不通知",那你的宏根本不会执行,文档直接就废了。做测试之前,先确认目标环境的 Office 宏策略是个铁律。
5.2 杀软对临时文件的查杀
宏在运行过程中只要在磁盘上写了一个 PE 文件,大概率被实时监控立刻杀掉。所以载荷尽量别落地,能用 PowerShell 从远程IEX加载就别自己写文件。如果实在要写文件,建议把 PE 切成碎片,运行的时候再拼回去,缩短文件在磁盘上的存活时间。
5.3 Office 保护视图
如果你把恶意文档放在下载目录、微信接收目录,Office 会以保护视图方式打开,禁用宏和外部内容。这也是为什么钓鱼文档放在邮件附件里比放在网盘里更容易成功——邮件附件的文件来自"互联网区域"同样会触发保护视图,但攻击者通常会在文档里引导用户点"启用编辑",这一步其实就是和用户体验做对抗。
5.4 Excel加载项和启动失败安全模式
某个版本的 Office 在文档异常崩溃后,下次打开会进入"安全模式",默认不加载宏。很多实战术里,宏一旦触发蓝屏或崩溃,后续测试就会全面失败。我在做测试时遇到过excel上次启动失败安全模式的提示,当时还以为是杀软拦截了,排查了很久才发现是宏代码里释放内存的 API 用得不规范,导致 Excel 被搞崩。建议在测试阶段多次打开文档确认稳定性,再投到真实目标上。
5.5 文档元数据也能出卖你
生成的 Excel 文档里带着作者名字、公司名、模板路径这些元数据,蓝队一旦发现恶意文档并提取元数据,可以直接定位到攻击者的身份信息。用openpyxl保存前记得清理属性,还可以手工把作者改成目标公司内部可能常见的名字,混淆溯源。
6. 蓝队视角:怎么把这类攻击拦下来
免杀对抗不是单方向的,上面聊了攻击思路,下面聊聊防守方该做哪些事。
6.1 邮件网关和附件沙箱
第一道防线是邮件网关。很多企业已经在网关侧做了附件类型过滤、压缩包嵌套检测和沙箱运行。不过 Excel 钓鱼的隐蔽性在于,文件本身可能不含任何恶意内容,所有恶意行为都在"外部链接"或"远程模板"上。这时候单靠附件沙箱还不够,沙箱必须模拟网络出站,观察文档是否有可疑的外部请求。
6.2 终端上彻底关掉不需要的功能
对企业来说,办公场景几乎用不到 DDE,也极少需要宏。直接在 Teams/组策略里把 Office 宏全部禁用,同时通过注册表关闭 DDE,就能把绝大多数的 Excel 钓鱼杀掉。这里我给个组策略建议:
- 启用"禁用 VBA 宏"
- 启用"禁用所有宏,且不通知"
- 在"受信任位置"里严格限定,禁止用户自行添加
- 启用"阻止 Office 创建子进程"
最后一条"阻止 Office 创建子进程"特别关键,它能让excel.exe -> cmd.exe -> powershell.exe这种进程链直接被拦截,很多宏攻击都是因为这条策略而失效的。
6.3 行为检测与进程链监控
EDR 的检测重点应该放在进程树上,而不是单纯文件特征。当 Excel 打开后出现以下行为时,可以直接拉高危:
EXCEL.EXE启动cmd.exe或powershell.exeWINWORD.EXE写入%APPDATA%下的可执行文件- Office 进程有异常的网络连接(访问非内网、非微软域名的 URL)
Sysmon 的事件日志里,这些进程链都会被记录。蓝队日常可以搜索EventID 1中父进程为EXCEL.EXE的子进程创建事件,基本一抓一个准。
6.4 员工教育不是口号,要做具体练习
安全意识教育不能光讲"不要打开陌生邮件",得给员工看真样本。我之前在蓝队演练里做过一个实验:给全公司发了一封带模拟恶意 Excel 的钓鱼邮件,大约有 15% 的人打开了附件,其中一半点击了"启用内容"。这个比例在真实攻击里已经足够造成严重突破了。
建议每季度做一次钓鱼演练,对点击附件和启用宏的员工进行定向提醒和二次培训。比任何高深的技术防御都管用。
最后再分享一点体会
做了这么多年攻防测试,我最深的感触是:Excel 钓鱼之所以层出不穷,核心不是杀软不强,而是人对办公文件的惯性信任几乎无解。攻击者只需要把恶意代码伪装成一份正常的表格,剩下的事全是用户自己完成的。免杀文档再怎么花哨,最终拼的还是谁能更好地理解杀软的检测逻辑,以及谁能更细腻地模拟正常用户的操作行为。对于红队,请务必把测试控制在授权范围内,不要越线;对于蓝队,也别只迷信堆设备,把宏禁用、进程链监控和员工演练这三件事做扎实,大部分 Excel 钓鱼就进不了门。