赞
踩
目录
ARP:地址解析协议,ARP(Address Resolution Protocol)
根据IP地址获取物理地址的一个TCP/IP协议,环境所在的设备如果只知道目的设备的IP地址却不知道其MAC地址的时候,可以通过这种协议,使用以太 广播包给网络上的每一台设备发送ARP数据包,获取其中符合条件的MAC地址,以保证通信的顺利进行
ARP的工作过程
假设主机A和B在同一个网段,主机A只知道主机B的IP地址,却不知道主机B的MAC地址的时候,这时候主机A首先会查看自己的ARP表,如果ARP表中含有主机B的IP相对应的MAC地址,只需要按照这个地址发送出去即可。如果主机A在ARP表中找不到对应的MAC地址,这时需要使用以太广播包给网络上的每一台设备发送ARP请求包,首先我们构造一个ARP请求,然后使用wireshark抓取这个数据包进行分析,如下:
- from scapy.all import *
- from scapy.layers.l2 import ARP, Ether
-
- ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst="192.168.200.134"), timeout=1)
以下,我们发送的数据包大小为42个字节
Destination:括号内的内容如果为(ff:ff:ff:ff:ff:ff)则是广播地址,当前数据包会被广播到当前网段的所有设备上
以下为请求信息
Hardware type: Ethernet (1)硬件的类型,硬件类型值为1, 表示是以太网地址
Protocol type: IPv4 (0x0800)协议的类型
Hardware size: 6硬件地址的长度
Protocol size: 4协议的长度
Opcode: request (1):操作码,1表示是一个arp的请求数据包
最后就是发送方的MAC地址和IP地址以及接收方IP地址,接收方MAC地址为00:00:00_00:00:00
当网络上的其他设备在接收到这个ARP请求后,会将自己的IP地址与ARP请求数据包中头部的目标设备IP地址进行比较,如果不匹配,则丢弃这个数据包。如果匹配成功,通过ARP请求数据包内源IP地址和源MAC地址,给发送方回复一个应答数据包,该数据包格式如下(注意:如果想要有应答数据包,那么这个IP地址需要真实存活):
Opcode:reply表示这是一个操作码为2的应答数据包,然后把自己的硬件地址填入,根据请求包的源IP和MAC打包发送回去
当收到这个ARP请求数据包或应答数据包时,双方就会把结果存在各自的ARP缓存表中,如下:
以后再需要和双方进行通讯,只需查询这个ARP表即可。所以,如果双方处于同一局域网时,利用ARP扫描是一个比较好的选择,不但快,而且准确且没有任何安全机制会阻止这种扫描方式
ARP实现活跃设备的扫描
首先查看一下scapy库中ARP类需要的参数,ls()查看该函数需要的具体参数
hwsrc和hwdst分别是源IP地址和源MAC地址,这两个参数不用设置,在发送的时候会自动填写进去,不过pdst需要设备为目的地址,由于是发送的广播数据包,我们需要在Ether层进行设置
- from scapy.all import *
- from scapy.layers.l2 import ARP, Ether
-
- ganyu = '192.168.200.134'
- pkt = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ganyu)
- ans, unans = srp(pkt, timeout=1)
- for s, r in ans:
- print('success')
- print(r.sprintf('%Ether.src% - %ARP.psrc%'))
dst="ff:ff:ff:ff:ff:ff"即为广播数据包发送到各个网络设备上,ans中的s和r分别表示发出的ARP请求数据包和收到的应答数据包
ICMP:Internet控制报文协议(Internet Control Message Protocol)
在日常生活中,我们经常会使用某个命令去检测本地设备与目标设备之间的连通性,比如我们所在的设备地址为192.168.1.1,目的地址为192.168.1.2,通常使用ping这个命令去判断目标是否存活,而ping的过程就是向特定的目的主机发送ICMP请求报文的过程
ICMP提供了多种报文,可以分成差错报文和查询报文,后者由一个请求数据包和查询数据包构成,与ARP扫描一样,如果发送请求数据包后接收到应答数据包,则可以判断目标主机为存活状态
同一网段/不同网段内内的ping操作
假设主机A和B在同一个网段,需要通过ping判断主机B是否存活,首先会判断主机B是否在同一网段内,若IP层协议通过主机B的IP地址和自己的子网掩码,发现跟自己属于同一网络,那么主机A会查询本地的MAC地址表,如果没有发现主机B的地址,发送ARP请求广播请求得到回应主机B的MAC地址,接下来就是进行ICMP通讯了,过程就是先利用ARP获取到主机B的地址,然后再利用MAC地址发送ICMP报文。当进行跨网段ping时,可以理解为:两个同网段的过程,通过路由器连接起来
我们通过scapy库中的ICMP函数来实现扫描,使用ls()查看函数使用时具体需要的参数,其实ICMP()内大多数参数都不需要设置,直接调用该类即可,type为8代表请求,默认为8,如果为0则表示应答
- from scapy.all import *
- from scapy.layers.inet import ICMP, IP
-
- ganyu = '192.168.200.134'
- pkt = IP(dst=ganyu) / ICMP()
- ans, unans = sr(pkt, timeout=1)
- for s, r in ans:
- print(r.sprintf('%IP.src% is alive'))
这种扫描方式相比ARP只能在特定的局域网环境下扫描来说,应用范围就会宽阔很多,无论是在互联网还是以太网都可以使用这种扫描技术,不过缺点也很明显,大部分网络设备会对ICMP进行屏蔽,所以常常会发现有一个真实存活的网络设备,但是没有扫描出来
TCP:传输控制协议(Transmission Control Protocol)
一种面向连接的、可靠的、基于字节流的位于传输层通信协议,特点是使用3次握手协议建立连接,当客户端发送SYN数据包后,就等待服务器回应SYN+ACK数据包,并最终对服务器发送ACK数据包进行确认,至此通讯双方建立连接
TCP3次握手协议过程:
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认
第二次握手:服务器收到syn包,必须确认客户的syn(ack=j+1),同时自己也发送一个SYN+ACK包,此时服务器进入SYN_RECV状态
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
完成三次握手,客户端与服务器传输数据
在TCP处于的传输层出现了端口的概念,可以理解为是设备与外界通讯交流的出口,可以分为物理端口和虚拟端口,通过虚拟端口如80,端口、3306端口、6379端口分别被不同的服务所占用来进行通讯,这些服务都是通过IP+端口号来区分的
如果我们想检测目标主机是否存活,可以从端口入手,当检测到一台设备的摸个端口有回应时(SYN+ACK数据包),可以判断该主机为存活状态,当然端口即使是关闭的,也会返回一个RST(连接重置)数据包,如下
当源地址对目的地址发送一个SYN数据包后,如果目的地址80端口关闭了,所以会返回一个RST(连接重置)数据包;
当源地址对目的地址发送一个SYN数据包后,如果目的地址80端口开启了,会返回一个SYN-ACK数据包;
当源地址对目的地址发送一个SYN数据包后,如果目的地址不存在,没有任何数据包返回;
我们通过scapy库中的TCP函数来实现扫描,使用ls()查看函数使用时具体需要的参数,这里的大多数参数都不需要设置,需要考虑的是sport、dport和flags。其中sport是源端口,dport是目标端口,flags是标志位,可能的值包括SYN(建立连接)、FIN(关闭连接)、ACK(响应)、PSH(有DATA数据传输)和RST(连接重置)
- from scapy.all import *
- from scapy.layers.inet import ICMP, IP, TCP
-
- ls(TCP())
构造一个发往192.168.3.206的3306端口的SYN数据包
- from scapy.all import *
- from scapy.layers.inet import ICMP, IP, TCP
-
- ganyu = '192.168.3.206'
- ganyuport = 3306
- pkt = IP(dst=ganyu) / TCP(dport=ganyuport, flags='S')
- ans, unans = sr(pkt, timeout=1)
- for s, r in ans: print(r.sprintf('%IP.src% is alive'))
- for s in unans: print('you ip is not alive')
UDP:用户数据报协议(User Datagram Protocol)
与TCP不同的是,UDP没有三次握手协议,当我们像目的地址发送UDP数据包后,目标不会返回任何UDP数据包,如果主机处于存活状态时,目标端口是关闭的,就会返回一个ICMP数据包,这个数据包的内容为"unreachable"(不可达)
- from scapy.layers.inet import IP, UDP
- from scapy.sendrecv import sr1
-
- ganyu = '192.168.3.206'
- ganyuport = 3306
- packet = IP(dst=ganyu)/UDP(dport=ganyuport)
- ans = sr1(packet, timeout=1.0)
- if ans:
- if int(ans[IP].proto) == 1:
- print(ganyu + ' ' + 'is alive')
- else:
- print(ganyu + ' ' + 'is alive')
- else:
- print(ganyu + ' ' + 'is alive')
如果把服务器比作房子,那么端口就是通往不同房间的大门,如果需要去了解对方服务器,那么这栋房子里都有什么样子的门,门里面有什么,是我们首要需要去了解的
如果目标端口开放,那么在收到设备的端口发出的SYN数据包后,就会返回一个SYN+ACK的数据包,表示接收这次连接请求,如果目标端口是关闭的,则收到SYN包后会返回一个RST数据包
假设我们向目标主机发送一个数据包,flags参数设置为S,代表这是一个SYN数据包
pkt = IP(dst = dstip)/TCP(sport=sport,dport=dport,flags='S')
然后通过sr1将这个数据包发送出去
ans = sr1(pkt,timeout=1)
如果ans返回为空,表示我们没有收到来自目标端口的回应,直接判断端口关闭,如果接收到了回应,首先会判断这个数据包的类型是SYYN+ACK还是RET的,可以使用haslayer()来判断,比如判断某个数据包使用的是TCP协议吗,就可以使用haslayer(TCP),可以使用getlayer(TCP)来读取其中某个字段的内容,如下
ans.getlayer(TCP).flags == 'SA' # SA代表SYN+ACK
通过ans.getlayer(TCP).flags结果,对端口是否存活进行判断.SA代表SYYN+ACK,A代表ACK,R代表RST
- from scapy.layers.inet import *
- from scapy.sendrecv import *
-
- ganyu = '192.168.2.46'
- ganyuport = 3306
- ganyusport = RandShort() # 由于在和目标端口建立连接的时候,自己也需要使用一个源端口,RandShort用于随机产生一个端口号
- pkt = IP(dst=ganyu) / TCP(sport=ganyusport, dport=ganyuport, flags='S')
- ans = sr1(pkt, timeout=1) # 指定等待应答数据包的时间,不使用的话可能需要要等待很久
- if str(type(ans)) == "<class 'NoneType'>":
- print('%s' % ganyu, ':%s is closed' % ganyuport)
- elif ans.haslayer(TCP):
- if ans.getlayer(TCP).flags == 'SA': # SYN+ACK
- seq1 = ans.ack
- ack1 = ans.seq + 1
- pkt_rst = IP(dst=ganyu) / TCP(sport=ganyusport, dport=ganyuport, seq=seq1, ack=ack1, flags='A') # ACK
- send(pkt_rst)
- print('%s' % ganyu, ':%s is open' % ganyuport)
- elif ans.getlayer(TCP).flags == 'R': # RST
- print('%s' % ganyu, ':%s is closed' % ganyuport)
之所以被称为半开是由于如果对方端口开放的话,那么收到设备端口发出的SYN数据包后,会返回一个SYN+ACK数据包,表示愿意接收这次连接请求,然后设备不在回应一个ACK数据包,而是发送一个RST数据包中断这次连接
- from scapy.layers.inet import *
- from scapy.sendrecv import *
-
- ganyu = '192.168.2.46'
- ganyuport = 3306
- ganyusport = RandShort() # 由于在和目标端口建立连接的时候,自己也需要使用一个源端口,RandShort用于随机产生一个端口号
- pkt = IP(dst=ganyu) / TCP(sport=ganyusport, dport=ganyuport, flags='S')
- ans = sr1(pkt, timeout=1)
- if str(type(ans)) == "<class 'NoneType'>":print('%s' % ganyu, ':%s is closed' % ganyuport)
- elif ans.haslayer(TCP):
- if ans.getlayer(TCP).flags == 'SA':print('%s' % ganyu, ':%s is open' % ganyuport)
- elif ans.getlayer(TCP).flags == 'R':print('%s' % ganyu, ':%s is closed' % ganyuport)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。