0x00:简介
Scapy 不仅可以在二层、三层去发现目标主机,也可以在四层去应用。主要用到的协议是 TCP 和 UDP。
0x01:scapy tcp
首先,构造一个 tcp 包,tcp 单独是不能发送的,所以需要结合一下三层的 ip 协议,scapy 中也就是 ip 函数和 tcp 函数的结合,示例如下:
IP 包头里的 dst 字段,指定目标地址。
TCP 包头里的 flags 字段,用来指定 tcp 包要发送的类型,这里用 A,意思是 TCP 握手包中的 ACK。
TCP 包里的 sport 指源发送的端口,也就是我们用哪个端口去请求,这里是 ftp20。
TCP 包里的 dport 指目标的端口,也就是要请求目标的哪个端口,这里默认是 http80。
dst、flags 我们手动设置,sport、dport 默认即可,最后用 sr1 函数发送即可,示例图如下:
我请求的是网关地址,也就是路由,路由的 80 端口是开放的,看返回结果,主要看 TCP 中的 flags,因为如果目标地址存在,握手包都会返回一个 RST。返回包的 flags 是 R 时,也就代表返回了 RST 包。则证明目标存在。
再看一下目标 ip 不开放时的情况,首先修改 TCP 包的 dport 值,指定一个没有开放的端口,例如 3306,示例图如下:
这时发送此包,然后 display 查看返回结果,如下图:
可见 TCP 中的 flags 值为 R,也就是返回了 RST 包,则目标在线。
注意:以上过程是利用四层 TCP 协议去发现相关的主机,而不是端口探测。综上可知,L 利用 TCP 去进行四层发现时,如果某一个目标在线,不管使用哪一个端口去发现,其都会返回 RST 包,则可根据此标志来判断目标是否在线。
那么当目标在线时,开放的端口和未开放的端口都会返回 RST 包,那该如何判断目标不在线,当目标不在线时,就不会返回 RST 包,而是返回信息告诉你没有找到目标地址,示例如下:
上面地址是没在线的,因为没有在线,所以需要加个 timeout 超时时间,可以看到提示地址 mac 没有找到,这可以判断目标地址此时没有在线。
以上操作也可以放到一行中去执行,例如 a=sr1(IP(dst="20.18.0.254")/TCP(dport=80,flags='A'),timeout=1) ,运行结果如下:
一行形式写 python 脚本,批量去发现,效率更快,示例代码如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
if len(sys.argv)!=2:
print "参数错误,示例:./scapy-tcp.py 192.168.1.0"
sys.exit()
address=str(sys.argv[1])
prefix=address.split('.')[0]+'.'+address.split('.')[1]+'.'+address.split('.')[2]+'.'
for addr in range(1,254):
response=sr1(IP(dst=prefix+str(addr))/TCP(dport=2222,flags='A'),timeout=0.1,verbose=0)
try:
if response[TCP].flags)=='R':
print prefix+str(addr)
except:
pass
使用时,直接更 ip 即可,脚本会获取 ip 地址前三段,然后遍历第时段从 1-254,最后使用 TCP 来发送,如果 response 返回的 TCP 中的 flags 是 R,也就是返回了 RST 包,则打印此 ip,执行结果如下:
通过 wireshark 抓包,可以看出协议是 TCP,由 20 端口发像 2222 端口,发的是 ACK,如下图:
0x02:scapy udp
udp 不像 tcp 有多次握手,udp 包发送后是不会有 flags 响应的。判断时需要选择一个端口,如果返回的包提示端口不可达,则说明目标在线。当目标不在线时,会发出提示地址的 mac 没有找到。
同样的,发现目标是否存在,在四层中除了 tcp,也可以使用 udp,,首先在 scapy 中利用 ip 函数和 udp 函数来构造一个请求包,如下图:
然后设置 ip 的 dst 指定目标地址,设置 udp 的 dport 指定目标端口,如下:
如果设置一个不在线的 ip 地址,则执行结果如下:
以上过程也可以通过一行命令来执行,以下是一行命令,目标在线和目标不在线结果对比:
目标在线时会得到一个响应包,目标不在线时会得到零个响应包且会提示 mac 地址没有找到。使用 udp 批量扫描时,python 脚本如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
if len(sys.argv)!=2:
print "参数错误,示例:./scapy-udp.py 192.168.1.0"
sys.exit()
address=str(sys.argv[1])
prefix=address.split('.')[0]+'.'+address.split('.')[1]+'.'+address.split('.')[2]+'.'
for addr in range(1,254):
response=sr1(IP(dst=prefix+str(addr))/UDP(dport=22222),timeout=0.1,verbose=0)
try:
if int(response[IP].proto)==1:
print prefix+str(addr)
except:
pass
脚本和 tcp 一样,原理也相似,最后打印判断时,取的是 ip 包头里的 proto 字段,当 proto 为 1 时,则代表 udp 上一层协议为 icmp 协议,当上一层为 icmp 时,上一层能成功返回则可认为目标是在线的,通过前面的返回 IP 包头也可以看到,proto 的值为 icmp,脚本运行结果如下:
可以抓包看一下 ip 包头的 proto 值,不同的数字代表不同的协议,如下图:
具体数字代表哪些协议可以查看 RFC1700 文档,1 为 icmp 截图如下:
0x03:总结
利用 scapy 的 tcp 和 udp 在四层发现目标是否在线,虽然利用到了端口,但这里检测的是主机是否在线,而不是端口是否开放。四层不能单独的去发包,需要结合其他层来实现,例如返回的包有三层 icmp,发送需要三层的 ip 等。如果目标在线,而 tcp 或 udp 没有检测到时,建议再使用其他层来确认,检测结果只是参考,而不是确认结果。
公众号推荐:aFa攻防实验室
分享关于信息搜集、Web安全、内网安全、代码审计、红蓝对抗、Java、Python等方面的东西。