Scapy实现基于ARP/ICMP/TCP/UDP的活跃设备扫描技术,以及TCP全/半端口扫描

28 篇文章 6 订阅
24 篇文章 2 订阅

目录

基于ARP的活跃设备发现

基于ICMP的活跃设备发现

基于TCP扫描的活跃设备发现

基于UDP扫描的活跃设备发现

Scapy实现端口扫描

基于TCP全开端口扫描

基于TCP半开端口扫描


基于ARP的活跃设备发现技术

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的活跃设备发现技术

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扫描的活跃设备发现技术

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(连接重置)数据包,如下

  1. 当源地址对目的地址发送一个SYN数据包后,如果目的地址80端口关闭了,所以会返回一个RST(连接重置)数据包;

  2. 当源地址对目的地址发送一个SYN数据包后,如果目的地址80端口开启了,会返回一个SYN-ACK数据包;

  3. 当源地址对目的地址发送一个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扫描的活跃设备发现技术

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')

Scapy实现端口扫描

如果把服务器比作房子,那么端口就是通往不同房间的大门,如果需要去了解对方服务器,那么这栋房子里都有什么样子的门,门里面有什么,是我们首要需要去了解的

基于TCP全开端口扫描技术

如果目标端口开放,那么在收到设备的端口发出的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)

基于TCP半开端口扫描技术

之所以被称为半开是由于如果对方端口开放的话,那么收到设备端口发出的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)

  • 3
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中可以使用scapy库来实现端口扫描探测。scapy是一个强大的网络包处理工具,可以用于构建、发送和解析网络数据包。 下面是使用scapy实现端口扫描探测的基本步骤: 1. 导入必要的模块和库: ```python from scapy.all import * import sys ``` 2. 定义一个函数来发送TCP SYN包并接收响应: ```python def scan_port(target_ip, port): src_port = RandShort() # 随机选择源端口号 response = sr1(IP(dst=target_ip)/TCP(sport=src_port, dport=port, flags="S"), timeout=1, verbose=0) if response is None: return "Closed" elif response.haslayer(TCP) and response.getlayer(TCP).flags == 0x12: send_rst = sr(IP(dst=target_ip)/TCP(sport=src_port, dport=port, flags="AR"), timeout=1, verbose=0) return "Open" elif response.haslayer(TCP) and response.getlayer(TCP).flags == 0x14: return "Closed" ``` 3. 定义一个函数来扫描指定范围的端口: ```python def scan_ports(target_ip, start_port, end_port): for port in range(start_port, end_port+1): result = scan_port(target_ip, port) print(f"Port {port}: {result}") ``` 4. 在主程序中调用函数进行端口扫描: ```python if __name__ == "__main__": target_ip = sys.argv[1] # 从命令行参数获取目标IP地址 start_port = int(sys.argv[2]) # 从命令行参数获取起始端口号 end_port = int(sys.argv[3]) # 从命令行参数获取结束端口号 scan_ports(target_ip, start_port, end_port) ``` 这样,你就可以通过运行Python脚本,并传入目标IP地址、起始端口号和结束端口号来实现端口扫描探测了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值