摘要:邮件头注入(Mail Header Injection)又称为CRLF注入(SMTP场景),属于经典的输入安全漏洞。当Web程序直接将用户可控输入拼接至SMTP邮件头部,且未过滤换行符\r\n/\n时,攻击者可注入自定义邮件头(Bcc、Cc、Reply-To),实现批量钓鱼邮件、伪造发件人等攻击。本文以bWAPP靶场Mail Header Injection (SMTP)关卡为载体,由浅入深讲解漏洞历史背景、底层原理、危害;分Low/Medium/High三个安全等级完成源码审计、实战复现;针对公网靶机无法向外发送邮件痛点,提供openEuler(欧拉系统)+小皮面板+Postfix纯内网无公网复现方案,同时梳理完整防御手段。
一、前言
很多新手学习Web漏洞时,最先接触SQL注入、XSS,但经常忽略一类和邮件系统相关的漏洞:邮件头注入。日常网站的「联系我们」「留言反馈」「找回密码邮件」功能,底层大多调用邮件接口发送通知。如果开发者忽略输入过滤,看似普通的邮箱输入框,就能成为黑客发起钓鱼攻击的入口。
bWAPP作为经典漏洞教学靶场,内置了Mail Header Injection关卡,通过三个安全等级展示不同防护力度下漏洞表现。 网上绝大多数教程直接使用公网mailinator临时邮箱复现,但是存在两个巨大痛点:
云服务器25端口常被运营商封禁;
虚拟机内网环境无法访问外网邮箱,邮件投递直接被拒收。
本文配套实战环境:openEuler(欧拉Linux虚拟机)+小皮面板(phpStudy)+bWAPP+Postfix本地邮件服务,全程内网闭环,不依赖任何外网资源。
二、漏洞基础知识科普
2.1 什么是SMTP协议、邮件头部结构
SMTP(简单邮件传输协议)是互联网发送电子邮件标准协议。一封标准电子邮件分为邮件头部(Header)和邮件正文(Body),头部由多行键值对组成,换行符CRLF(\r\n)是区分不同头部字段的分隔标志。
标准邮件头部示例:
From: service@demo.com To: user@test.com Subject: 网站留言通知 这里是邮件正文内容From:发件人To:收件人Subject:邮件标题Cc:抄送Bcc:密送
当程序允许攻击者插入换行,就能跳出原有的From字段,新增任意头部。
2.2 CRLF与URL编码常识
\r= 回车(CR,URL编码%0D)\n= 换行(LF,URL编码%0A) HTTP传输时,浏览器不会自动解析明文换行,必须通过URL编码传递;PHP接收参数后自动解码,还原为换行符号。
重点:很多新手踩坑!直接在表单填写
%0A只会被当成普通字符串,必须抓包构造请求。
2.3 邮件头注入(Mail Header Injection)定义
Web应用获取用户输入,直接拼接进mail()函数邮件头部;攻击者通过插入换行字符,截断原有头部字段,新增自定义邮件头,操控邮件投递行为。 该漏洞本质属于CRLF注入在SMTP邮件场景下的分支。
2.4 漏洞历史背景与真实经典案例
邮件头注入漏洞最早在2000年前后被安全研究员披露,伴随着PHPmail()函数大规模普及而大量出现。在2005~2015年,大量论坛、企业官网留言系统存在该漏洞。
真实历史案例1:大量企业官网留言系统批量漏洞
2012年国内大量中小企业建站模板(织梦、PHPCMS早期版本)的「在线留言通知管理员」模块存在邮件头注入。黑客批量扫描站点,利用漏洞向企业内部员工群发仿冒官方钓鱼邮件,窃取员工账号。
真实历史案例2:开源商城找回密码模块漏洞
某开源电商系统早期版本,找回密码页面接收用户邮箱,未过滤换行。攻击者注入Bcc头部,批量向平台注册用户投递伪造「账号异常」钓鱼邮件,诱导用户输入账号密码。
真实攻击场景推演(最常见利用方式)
某企业官网联系表单,留言会发送邮件给管理员admin@company.com。 黑客构造Payload,新增Bcc密送所有员工邮箱:
contact@hacker.com%0ABcc:staff1@company.com,staff2@company.com网站发送邮件时,不仅通知管理员,同时秘密抄送给所有员工。黑客在留言正文嵌入钓鱼链接,实现批量钓鱼。
2.5 漏洞会造成哪些危害?
批量钓鱼攻击:新增
Bcc密送大量目标邮箱,批量投递钓鱼邮件;伪造发件人身份:覆盖From头部,伪装成企业管理员、官方客服发送虚假通知;
邮件炸弹:构造大量收件人,消耗邮件服务器资源;
泄露业务信息:结合其他漏洞,配合邮件向外转发内部信息。
注意:邮件头注入不能直接执行系统命令,主要风险集中在钓鱼、邮件滥用,属于业务逻辑类安全漏洞。
三、实验环境搭建
3.1 环境清单
虚拟机系统:openEuler 24.03
Web环境:小皮面板(phpStudy for Linux),PHP7.x
靶场程序:bWAPP
邮件服务:Postfix(替代bee-box内置sendmail)
访问方式:物理机浏览器访问虚拟机地址
http://10.0.0.149:4096环境特点:纯内网,不访问外网邮件服务器,避免mailinator滥用拦截、25端口封禁问题
3.2 Postfix部署与配置步骤
# 1. 安装服务 dnf install postfix mailx -y # 2. 设置开机自启、启动 systemctl enable postfix systemctl start postfix编辑配置文件/etc/postfix/main.cf
myhostname = localhost.localdomain mydomain = localdomain myorigin = localhost inet_interfaces = all mydestination = $myhostname, localhost.$mydomain, localhost smtputf8_enable = no重启服务:
systemctl restart postfix3.3 PHP适配
打开对应PHP版本php.ini,找到sendmail_path
sendmail_path = "/usr/sbin/sendmail -t -i"去掉注释,保存,重启PHP服务。
3.4 创建本地内网收件用户
useradd mailrecv passwd mailrecv本地有效邮箱地址:mailrecv@localhost邮件最终保存在/var/spool/mail/mailrecv,直接读取文件查看原始邮件头部。
3.5 解决bWAPP默认收件人外网问题
靶场源码默认收件地址:$recipient = $smtp_recipient;,配置文件内为bwapp@mailinator.com,会尝试向外网投递导致退信。 两种方案:
直接修改源码,将
$recipient = "mailrecv@localhost";,邮件全程内网投递;不修改源码,利用退信文件
/var/spool/mail/root查看原始注入报文。
3.6 测试PHP mail()可用性
新建mailtest.php放入网站根目录:
<?php $to = "mailrecv@localhost"; $subject = "本地内网测试"; $msg = "内网邮件测试内容"; mail($to,$subject,$msg); echo "发送完成"; ?>访问页面后执行命令查看邮件:
cat /var/spool/mail/mailrecv出现完整邮件报文 = 邮件环境搭建成功。
四、源码完整审计
4.1 核心代码片段提取
// 接收POST提交的邮箱 $email = $_POST["email"]; // 拼接邮件头部 $status = @mail($recipient, $subject, $content, "From: $email");mail()第四个参数为自定义邮件头部,直接拼接用户可控变量$email,无过滤时存在注入风险。
安全等级分支逻辑:
if($_COOKIE["security_level"] == "2") { // HIGH级别调用maili_check_2过滤 $email = maili_check_2($_POST["email"]); } elseif($_COOKIE["security_level"] == "1") { // MEDIUM:提交时使用email_check_2校验邮箱格式 } else { // LOW级别:无任何过滤,直接使用输入 $email = $_POST["email"]; }4.2 过滤函数源码解析
// Medium级别邮箱格式校验 function email_check_2($data) { return filter_var($data, FILTER_VALIDATE_EMAIL); } // High级别过滤函数 function maili_check_2($data) { $input = urldecode($data); $input = filter_var($input, FILTER_SANITIZE_EMAIL); return $input; }FILTER_VALIDATE_EMAIL:严格校验标准邮箱格式,输入包含换行直接判定邮箱非法;FILTER_SANITIZE_EMAIL:自动移除换行、空格等非法字符,彻底阻断CRLF注入。
4.3 页面表单细节差异
Low级别表单E-mail输入框为<textarea>文本域;Medium/High为单行input,仅仅是前端限制,前端限制可被Burp抓包绕过,真正防护依靠后端过滤。
五、Low安全等级:漏洞完全开放
5.1 环境准备
bWAPP登录账号:
bee/bug安全等级设置:Low
访问地址:
http://10.0.0.149:4096/maili.php
5.2 攻击原理推演
原始拼接代码:
"From: " . $_POST["email"]传入Payload:test@localhost%0D%0ACc:mailrecv@localhost拼接后邮件头部变为:
From: test@localhost Cc:mailrecv@localhost%0D%0A就是\r\n,解码为回车换行,成功新增独立Cc抄送头部。
5.3 实战通关步骤
重要说明:必须Burp抓包,禁止浏览器直接提交。由于SMTP协议特性,Bcc密送头部会被Postfix等MTA自动清理,无法在邮件原始报文中直观观察。 实验优先选用自定义扩展头部X-Hacked作为验证载荷,无协议干扰,结果一目了然;Cc抄送载荷可作为拓展攻击演示。
推荐基础验证Payload(首选,用于判断漏洞是否生效)
name=test&email=test@localhost%0D%0AX-Hacked:Injection_Success&remarks=test&form=submit拓展载荷1:实现抄送Cc(头部可在报文内直接查看)
name=test&email=test@localhost%0D%0ACc:mailrecv@localhost&remarks=test&form=submit拓展载荷2:实现密送Bcc(仅原理演示,报文无法直接看到Bcc字段)
name=test&email=test@localhost%0D%0ABcc:mailrecv@localhost&remarks=test&form=submit操作流程:
1. 页面随意填写内容,点击Send,Burp Suite拦截POST请求;
2. 将数据包内email参数替换为上方任意载荷;
3. Forward放行请求,页面提示Your message has been sent to our master bee!
5.4 结果验证
cat /var/spool/mail/mailrecv使用X-Hacked载荷:邮件头部(Header 区域,头部与正文空分界线之上)独立出现一行X-Hacked:Injection_Success;
使用Cc载荷:邮件头部独立出现一行Cc:mailrecv@localhost。
实验高频迷惑现象(Bcc 载荷特殊说明)
使用 Bcc 载荷进行测试时,邮件原始报文内找不到Bcc:mailrecv@localhost,不代表注入失败!原理:根据 SMTP 协议设计规范,MTA(Postfix 邮件服务器)收到包含 Bcc 密送头部的邮件后,会正常向密送地址投递邮件,但自动删除邮件报文内部的 Bcc 头部,防止收件人查看完整密送名单。 因此教学验证漏洞是否存在,不推荐直接使用 Bcc 载荷,优先选择X-Hacked自定义头部消除协议干扰。
5.5 新手高频踩坑说明
很多同学直接在表单输入%0A,注入失败! 原因:浏览器传递原始字符串%0A,PHP不会自动转义换行;只有HTTP传输URL编码,服务端自动解码,才能生成\n换行。
错误示例(注入失败):
From: "test@localhost%0ABcc:mailrecv"@localhost载荷推荐使用标准 CRLF%0D%0A,单一%0A(仅 LF 换行)在部分 Postfix 环境存在不稳定现象。
六、Medium安全等级:基础防护
6.1 防护逻辑
Medium级别在表单提交时执行email_check_2(),调用filter_var(FILTER_VALIDATE_EMAIL)校验邮箱格式。 如果email参数包含换行字符,直接判定邮箱非法,页面提示:Enter a valid e-mail address!。
6.2 能否绕过
标准情况下无法绕过。校验发生在后端,任何包含换行的输入都会被拦截。 部分老旧PHP版本存在过滤器边界漏洞,但当前稳定PHP版本不存在可行绕过Payload。
6.3 安全总结
仅校验邮箱格式,属于基础防护;如果开发者代码书写不规范、条件判断出错,依然存在风险,不能作为最终防御方案。
七、High安全等级:完整输入净化
7.1 防护逻辑
High级别启用maili_check_2(),执行FILTER_SANITIZE_EMAIL过滤器,自动删除所有换行、特殊控制字符。 无论传入%0A、%0D%0A,所有换行符号都会被直接清除。且同样如果email参数包含换行字符,直接判定邮箱非法,页面提示:Enter a valid e-mail address!。
7.2 对比思考
Medium是「校验合法性,非法直接拒绝」; High是「主动净化输入,移除危险字符」;净化类防御稳定性高于格式校验。
八、邮件头注入通用防御方案
结合bWAPP三个等级的防护效果,整理生产环境可用多层防御策略。
首选方案:禁止用户可控变量直接拼接邮件头部
最优方案:不要将用户输入直接拼入邮件Header。 区分两种场景:
仅需要收集用户邮箱展示在正文:邮箱只放在邮件Body,不参与头部拼接;
需要回复功能:预定义固定头部,用户输入仅填充正文。
输入过滤与净化(代码层面)
强制移除所有换行字符
\r、\n;使用成熟过滤函数,不要自己手写正则(正则极易出现绕过漏洞); PHP示例:
$email = str_replace(array("\r","\n"),"",$_POST['email']);使用现代邮件类库替代原生mail()函数
PHP原生mail()非常容易触发邮件头注入,推荐使用PHPMailer、SwiftMailer。 主流邮件类库内置邮件头部净化逻辑,自动拦截非法换行,从底层规避漏洞。
WAF层面防护
WAF规则匹配请求参数中%0A、%0D换行编码字符,拦截恶意Payload。
最小权限原则
邮件服务器限制向外批量发送邮件,配置发送频率限制,降低漏洞被滥用后的危害。
九、总结
本文从邮件头注入漏洞基础知识入手,结合 bWAPP 靶场分等级完成原理剖析与实战复现。该漏洞本质是用户可控输入未过滤换行字符,直接拼接至 SMTP 邮件头部,攻击者利用 CRLF 换行实现邮件头部篡改。在 bWAPP 三种安全等级下防护效果差异明显:Low 等级不存在任何过滤,漏洞可直接利用;Medium 等级采用邮箱格式校验拦截标准载荷,但防护手段单一;High 等级主动净化输入、清除换行危险字符,能够有效抵御攻击。本次搭建的 openEuler+Postfix 内网闭环实验环境,避开了公网 25 端口封禁、外网邮箱拦截等常见学习阻碍,非常适合虚拟机线下练习。同时实验也给出重要开发安全启示:永远不能信任用户输入,尽量避免使用 PHP 原生mail()函数,切勿将可控参数直接拼接邮件头部、SQL 语句、系统命令等底层指令。最后厘清漏洞攻击边界:邮件头注入主要用于批量钓鱼、邮件服务器滥用,无法直接获取服务器权限,属于典型业务安全漏洞,但是在真实互联网业务场景中威胁极高,需要开发者重点防范。
重要声明:本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。