信息搜集分为两种:被动扫描和主动扫描
被动扫描主要是指在目标无法察觉的情况下进行信息搜集,相比于被动扫描,主动扫描的范围要小的多,主动扫描一般针对目标发送特制的数据包,然后根据目标的反应来获得一些信息。这种扫描方法的技术性比较强,通常可以使用专业的扫描工具来对目标进行扫描。扫描之后获得的信息包括网络的结构,目标网络所使用设备的类型,操作系统,开放的端口,提供的服务等等。
用Python实现设备状态扫描
基于ARP的活跃设备发现技术
ARP协议的中文名字是“地址解析协议”,在前面讲过,在局域网中通信是需要用它将IP地址转换为mac地址才能与交换机进行通信的,下面我们来简单回顾一下它的工作原理,当我们所在设备只知道目标设备IP地址却不知道目标设备的mac地址时,我们所在设备就需要发送一个广播给局域网的每一台设备,当网络中的主机收到这个广播时与请求包中的目标IP比对,如果匹配不上则不响应,如果匹配则返回相应包,然后目标机器则更新自己的ARP表。以后再想与这个主机通信,查询ARP表就可以了。
所以当目标设备与我们主机处于同一局域网中时,利用ARP对其进行扫描是一个较好的选择,因为这种扫描方式最快,也最为精准,没有任何安全机制会阻止这种扫描方式。接下来我们来说一下这个扫描方式的工作流程:
(1)首先向目标机器发送一个ARP请求
(2)接下来,如果目标设备处于活跃状态,他一定会给出一个ARP reply
(3)如果目标设备处于不活跃状态,它不会给出任何回应
接下来我们编写一个利用ARP是实现的活跃设备扫描程序,这个程序有很多种方式可以实现,我们先来借助Scapy库实现,核心思想就是要产生一个ARP请求数据包,数据包的结构如下
可以看到这里面大多数参数都有默认值,其中hwsrc和psrc分别时源mac和源Ip地址,这个不用设置,发送的时候会自动填写本机的地址。唯一需要设置的是目的IP地址pdst。由于我们发送的是一个广播请求包,所以目标mac地址还需要设置为ff:ff:ff:ff:ff:ff,下面我们来构造一个请求包。
ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.1"),timeout=1)
然后我们需要对这个数据包进行监听,如果得到回应,那么证明目标设备在线,并输出目标设备的mac地址。
from scapy.all import ARP,ls,Ether,srp
ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.1"),timeout=1)
for s,r in ans:#s是发送的数据包,r是接收的数据包
print(r.sprintf("%Ether.src% %ARP.psrc%"))#打印接收的数据包的源IP和源mac地址
基于ARP的扫描是一种高效的方式,但是它的局限性也很明显,只能扫描同意局域网中的设备,如果想扫描其他网段的设备,这个方法就不适用了。
基于ICMP的活跃设备发现技术
互联网控制报文协议(ICMP)也位于TCP/IP族中的网络层,它的目的是用于在设备,路由器之间传递控制消息。ICMP中提供了多种报文,这些报文又分为两大类:差错报文和查询报文,与ARP扫描不同的地方在于ICMP查询报文有四种,分别是响应请求或应答,时间戳请求或应答,地址掩码请求或应答,路由器询问或应答,但是在实际应用中,后面三种报文成功率很低,所以主要讲解第一种ICMP查询报文。
ping命令就是响应请求或应答的一种应用,我们经常会使用这个命令来测试本地设备与目标设备之间的连通性,我们接下来介绍一下ICMP的工作流程:
(1)首先向目标发送一个ICMP请求数据包
(2)接下来,如果目标设备处于活跃状态,他就会回应一个ICMP应答数据包,需要注意的是,现在很多网络安全设备或者机制都会屏蔽ICMP,如果发生这种情况,那么即使目标设备处于活跃状态我们也收不到任何回应。
(3)如果目标处于非活跃状态,他不会给出任何回应
也就是说,只要我们收到了ICMP应答数据包,就可以判断该目标设备处于活跃状态,下面我们来构造一个ICMP请求数据包,这里我们只需要设置目标IP地址就可以了,因为本机IP地址会在发送的时候自己填入。
from scapy.all import IP,ICMP,sr
ans,unans = sr(IP(dst="192.168.1.2")/ICMP())
按照之前的思路,我们对这个数据包进行监控就可以了,如果收到ICMP应答数据包,就证明目标设备在线,并输出目标设备的IP地址。
from scapy.all import IP,ICMP,sr
ans,unans = sr(IP(dst="192.168.1.2")/ICMP())
for s,r in ans:
print("目标机器存在")
print(r.sprintf("%IP.src%"))
基于TCP的活跃设备发现技术
传输控制协议(TCP)是一个位于传输层的协议,他是一个面向连接的,可靠的,基于字节流的传输层通信协议,由互联网工程任务组的RFC793定义。TCP的特点是使用3次握手协议建立连接,当客户端发送SYN数据包后,就等待服务端回应SYN+ACK数据包,并最终对服务器端的SYN+ACK数据包执行ACK确认,这种建立连接的方法可以防止产生错误的连接,TCP的3次握手的过程如下。
(1)客户端发送SYN数据包给服务器端,进入SYN_SEND状态
(2)服务器端收到SYN数据包,回应一个SYN+ACK数据包,进入SYN+RECV状态
(3)客户端收到服务器端的SYN+ACK数据包,回应一个ACK数据包,进入ESTABLISHED状态,当3次握手完成后,客户端和服务器端就成功的建立连接,就可以传输数据了。
TCP和ARP还有ICMP都不在同一层,而是位于上一层传输层,在这一层出现了端口的概念,如果我们检测到目标机器的某个端口有回应,也一样可以判断这台设备是活跃设备,需要注意的是,如果一台设备处于活跃状态,那么它的端口即使是关闭的,也会响应,不过不是返回SYN+ACJ,而是返回一个拒绝连接的RST数据包
这样我们在检测目标设备是否活跃的时候,就可以向目标设备的80端口发送一个SYN数据包,之后的情形可能有如下三种。
(1)我们设备发送的SYN数据包到达了目标设备的80端口处,但是目标端口关闭了,所以目标设备会发回一个RST数据包
(2)我们设备发送的SYN数据包到达了目标设备会发回一个SYN+ACK数据包
(3)我们设备发送的SYN数据包到达不了目标端口,这时就不会收到任何回应
也就是说如果我们收到回应,那么就可以判断该设备为活跃主机
现在来编写一个利用TCP实现的活跃设备扫描程序,这个程序有很多种方式可以实现,首先检测TCP数据包结构
这里大多数参数都是不需要设置的,需要考虑的是sport,dport和flags这里的sport是源端口,dport是目标端口,flags是标志位,可能的值包括SYN(建立连接),FIN(关闭连接),ACK(响应),PSH(有DATA数据传输),RST(连接重置).这里我们将flags设置为S,也就是SYN,另外,TCP没有目标地址和源地址,所以需要在IP层进行设置。所以然后我们来构造一个发往192.168.1.1的80端口的SYN数据包并将其发送。
from scapy.all import TCP,sr,IP
ans,unans = sr(IP(dst="192.168.1.1")/TCP(dport = 80,flags ="S"))
按照之前的思路,我们对该数据包的相应包进行监控,如果得到回应,就证明该目标设备在线,并输出目标设备的地址。
from scapy.all import TCP,sr,IP
ans,unans = sr(IP(dst="192.168.1.1")/TCP(dport = 80,flags ="S"))
for s,r in ans:
print(r.sprintf("%IP.src%"))
for s in unans:
print("the target is not alive")
基于UDP的活跃设备发现技术
UDP全称是用户数据报协议,在网络中它与TCP一样用于处理数据包,是一种无连接协议,也是位于传输层,UDP与TCP不同,它没有三次握手,所以也不会返回任何UDP 数据包,但是我们目标设备处于活跃状态,但是目标端口是关闭的时候,会返回一个ICMP数据包,这个数据包会显示unreadchable(不可达)
如果目标设备处于非活跃状态,我们是收不到任何回应的
下面我们构造一个发往192.168.1.1的6677端口的UDP数据包发送
from scapy.all import UDP,IP,sr
ans,unans = sr(IP(dst="192.168.1.1")/UDP(dport=6677))
然后进行监听
from scapy.all import UDP,IP,sr
ans,unans = sr(IP(dst="192.168.1.1")/UDP(dport=6677))
for s,r in ans:
print(r.sprintf("%IP.src%"))
使用Python实现端口扫描
在正常情况下,端口只有开放(Open)和关闭(Close)两种状况,但是有时出于安全考虑,会屏蔽对端口的扫描,因此端口状态可能会出现无法判断的情况,所以我们在扫描的时候要为端口加上一个filtered状态,表示无法获取端口的真正形态。
基于TCP全开的端口扫描技术
首先我们先来介绍一下这种扫描技术,这种扫描技术的思想很简单,如果目标端口开放,那么在收到设备端口发出的SYN数据包之后,就会返回一个SYN+ACK数据包,表示愿意接受这次连接的请求,然后设备端口回应一个ACK数据包,这样就成功的和目标端口建立一个TCP连接
如果目标端口是关闭的,那么在收到设备端口发出的SYN数据包之后,就会返回一个RST数据包,表示不接受这次连接的请求,这样就终端了这次TCP连接。
但是目标端口关闭还有另一种情况,就是当设备端口发出了SYN数据包之后,没有收到任何回应,多种原因都有可能造成这种情况,例如目标设备处于非活跃状态,这时当然无法回应,不过也可以认为目标端口是关闭的,另外,一些网络安全设备也会屏蔽对某些端口的SYN数据包,这时也会出现无法回应的情况。
下面我们构造数据包,需要注意的是TCP数据包的flags参数设置为S,表示SYN
from scapy.all import UDP,IP,sr,TCP
pkt =IP(dst="192.168.1.1")/TCP(sport=6677,dport = 7788,flags = "S")
然后我们将这个数据包发送出去
resp = sr1(pkt,timeout = 1)
接下来我们要根据收到的对应应答数据包来判断目标端口的状态,这时会有3个步骤
(1)如果resp为空,就表示我们没有收到来自目标端口的回应,在程序种,我们可以根据str(type(resp))来判断resp是否为空,如果是<type;NoneType>时表示为空,直接判断端口是关闭状态,如果不是为空,我们则执行下面步骤
(2)当收到应答数据包之后,我们需要判断这个数据包时SYN+ACK类型还是RST类型,可以使用haslayer()函数来判断合格数据包是否使用某一个协议,例如判断是否使用TCP协议,就使用haslayer(TCP),也可以使用getlayer(TCP)来读取其中某个字段的内容
resp.getlayer(TCP).flags ==0x12
#0x12就是SYN+ACK类型
如果以上语句为真,就表示目标端口接收了我们TCP连接请求,我们需要发送一个ACK数据包,完成三次握手
IP(dst="192.168.1.1")/TCP(sport=1111,dport=2222,flags=0x10)
#0x10就是ACK
如果resp.getlayer(TCP).flags的结果不是0x12,而是0x14(表示RST),那么表明目标端口是关闭的,我们使用如下语句来判断这个数据包是不是RST类型
resp.getlayer(TCP).flags == 0x14#0x12就是SYN+ACK
以上面的思路,我们书写一个完整的TCP全开的扫描程序
from scapy.all import UDP,IP,sr1,TCP,send,RandShort
dst_ip = "192.168.1.1"
src_port = RandShort()
dst_port = 80
pkt = IP(dst = dst_ip)/TCP(sport = src_port,dport = dst_port,flags = "S")
resp = sr1(pkt,timeout =1)
if (str(type(resp))=="<class 'NoneType'>"):
print("这个端口%s是关闭的"%dst_port)
elif(resp.haslayer(TCP)):
if(resp.getlayer(TCP).flags == 0x12):
seq1 = resp.ack
acl1 = resp.seq+1
pkt_rst = IP(dst=dst_ip)/TCP(sport= src_port,dport = dst_port,seq =seq1,ack=acl1,flags = 0x10)
send(pkt_rst)
print ("这个端口%s是开着的"%(dst_port))
elif(resp.getlayer(TCP).flags == 0x14):
print("这个端口%s是关闭的"%(dst_port))
今天的学习分享就到这里了,如果有什么不对的地方,欢迎大家的指正,谢谢大家的观看。