【Python】scapy模块学习笔记

文章目录

  • 0x00 scapy安装以及环境配置
  • 0x01 实验1
  • 0x02 实验2
  • 0x03 实验3
  • 0x04 实验4
  • 0x04 实验5
  • 0x06 python代码实现端口扫描

0x00 scapy安装以及环境配置

学习自知乎大佬 ——弈心——网络工程师的Python之路—Scapy基础篇
复现了一波

scapy安装:

pip install scapy

导入scapy方式:

from scapy.all import *

然后是环境配置

kali的IP地址为192.168.43.245,使用的网卡为eth0
【Python】scapy模块学习笔记_第1张图片

物理机的IP地址为192.168.43.1,就是kali的网关
【Python】scapy模块学习笔记_第2张图片

进入scapy,这里我是在kali2020里面进行发送scapy构造的数据包,然后在本机用wireshark进行抓包接收。
【Python】scapy模块学习笔记_第3张图片
物理机监听kali使用的这块网卡
【Python】scapy模块学习笔记_第4张图片
然后是scapy的一些基本用法

首先是ls()命令,用来查看scapy支持的网络协议
【Python】scapy模块学习笔记_第5张图片

除了ls()外,还可以用lsc()函数来查看scapy的指令集(函数)
【Python】scapy模块学习笔记_第6张图片
这里还可以用使用ls()的携带参数模式,比如ls(IP)来查看IP包的各种默认参数。
【Python】scapy模块学习笔记_第7张图片

0x01 实验1

实验目的:使用IP()函数构造一个目的地址为192.168.2.11(即拓扑中的交换机S1)的IP报文,然后用send()函数将该IP报文发送给S1,在S1上开启debug ip packet以验证是否收到该报文。

首先用IP()函数构造一个目的地址为192.168.43.1的IP报文,将其实例化给ip这个变量
在这里插入图片描述
ls(ip)查看这个报文的内容
【Python】scapy模块学习笔记_第8张图片

然后用send()将这个数据包发送给物理机,send()函数只发送不接受
在这里插入图片描述
可以看到物理机已经接收到了这个ip报文
【Python】scapy模块学习笔记_第9张图片

0x02 实验2

实验目的:除了send()外,scapy还有个sendp()函数,两者的区别是前者是发送三层报文,后者则是发送二层报文,实验2将演示如何用sendp()来构造二层报文。

除了send()外,scapy还有个sendp()函数,两者的区别是前者是发送三层报文,后者则是发送二层报文,实验2将演示如何用sendp()来构造二层报文。

先构造一个arp报文,如下

arp = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(hwsrc="00:0c:29:ef:29:24",psrc="192.168.43.245",pdst="192.168.43.1")/'abc'

在这里插入图片描述
这里我们构造了一个源MAC地址为00:0c:29:ef:29:24, 源IP地址为192.168.43.245,,目标IP地址为192.168.43.1,payload为abc的ARP报文。

然后sendp()发送出去

sendp(arp)

在这里插入图片描述
可以看到物理机接收到了来自kali的arp数据包,询问192.168.43.1的mac地址,并且物理机也把自己的mac地址返回给了kali
【Python】scapy模块学习笔记_第10张图片问题:为什么目的MAC地址设置为FF.FF.FF.FF.FF.FF

我们以主机A(192.168.1.5)向主机B(192.168.1.1)发送数据为例。当发送数据时,主机A会在自己的ARP缓存表中寻找是否有目标IP地址。如果找到了,也就知道了目标MAC地址,直接把目标MAC地址写入帧里面发送就可以了;如果在ARP缓存表中没有找到相对应的IP地址,主机A就会在网络上发送一个广播,目标MAC地址是“FF.FF.FF.FF.FF.FF”,这表示向同一网段内的所有主机发出这样的询问:“192.168.1.1的MAC地址是什么?”网络上其他主机并不响应ARP询问,只有主机B接收到这个帧时,才向主机A做出这样的回应:“192.168.1.1的MAC地址是00-aa-00-62-c6-09”。这样,主机A就知道了主机B的MAC地址,它就可以向主机B发送信息了。同时它还更新了自己的ARP缓存表,下次再向主机B发送信息时,直接从ARP缓存表里查找就可以了。ARP缓存表采用了老化机制,在一段时间内如果表中的某一行没有使用,就会被删除,这样可以大大减少ARP缓存表的长度,加快查询速度。

在局域网里抓了一会儿包,目的地址果然都是FF.FF.FF.FF.FF.FF,FF.FF.FF.FF.FF.FF就代表广播broadcast
【Python】scapy模块学习笔记_第11张图片

0x03 实验3

实验目的:从实验1和实验2的例子可以看出:send()和sendp()函数只能发送报文,而不能接收返回的报文。如果要想查看返回的3层报文,需要用到sr()函数,实验3将演示如何使用sr()函数。

先构造一个目的地址为物理机的ICMP()包。可以看到返回的结果是一个tuple(元组),该元组里的元素是两个列表,其中一个列表叫Results(响应),另一个叫Unanswered(未响应)。
【Python】scapy模块学习笔记_第12张图片
我们可以将sr()函数返回的元组里的两个元素分别赋值给两个变量,第一个变量叫ans,对应Results(响应)这个元素,第二个变量叫unans,对应Unanswered(未响应)这个元素。
【Python】scapy模块学习笔记_第13张图片
wireshark捕获到了发送给物理机的icmp包和物理机的应答包
在这里插入图片描述

0x04 实验4

实验目的:实验3讲到了sr(),它是用来接收返回的3层报文。实验4将使用srp()来接收返回的2层报文。

用srp()配合Ether()和ARP()构造一个arp报文,二层目的地址为ff:ff:ff:ff:ff:ff,三层目的地址为192.168.2.0/24, 因为我们是向整个/24网络发送arp, 耗时会很长,所以这里用timeout = 5,表示将整个过程限制在5秒钟之内完成

ans, unans = srp(Ether(dst = "ff:ff:ff:ff:ff:ff") / ARP(pdst = "192.168.43.0/24"), timeout = 5)

wireshark捕获到了从192.168.43.1到192.168.43.254的整个c段的arp包,因为只有192.168.43.1(物理机)、192.168.43.2(dns服务器)和192.168.43.245(kali),所以只会收到三个应答包
【Python】scapy模块学习笔记_第14张图片
下面用ans.summary()来具体看看到底是哪3个IP响应了我们的’who has’类型的arp报文。

ans.summary()

在这里插入图片描述
用unans.summary()来查看那些没有给予我们’who has’类型arp报文回复的IP地址【Python】scapy模块学习笔记_第15张图片
可以看到询问其他IP的’who has’类型arp报文没有人响应。我们可以用这个方法来写一个python判断存活主机的脚本,向局域网整个段发送arp、icmp请求,如果收到回应即为存活,否则为不存活。可以参考这篇博客:使用python的scapy和nmap模块进行主机存活探测

0x04 实验5

实验目的:使用tcp()函数构造四层报文,理解和应用RandShort(),RandNum()和Fuzz()函数。

用nmap来扫描一下物理机开启了哪些端口,这里就用8082端口来测试
【Python】scapy模块学习笔记_第16张图片

在scapy上使用ip()和tcp()函数来构造一个目的地IP为192.168.43.1(物理机),源端口为30,目的端口为8082的TCP SYN报文。

ans, unans = sr(IP(dst = "192.168.43.1") / TCP(sport = 30, dport = 8082, flags = "S"))

在这里插入图片描述

wireshark捕获到了三次tcp握手
【Python】scapy模块学习笔记_第17张图片
TCP端口号除了手动指定外,还可以使用RandShort(), RandNum()和Fuzz()这几个函数来让scapy帮你自动生成一个随机的端口号,通常可以用作sport(源端口号)。

首先来看RandShort(),RandShort()会在1-65535的范围内随机生成一个TCP端口号,将上面的sport = 30 替换成 sport = RandShort()即可使用。

ans, unans = sr(IP(dst = "192.168.43.1") / TCP(sport = RandShort(), dport = 8082, flags = "S")/"test")

这里可以看到RandShort()替我们随机生成了53780这个TCP源端口号

在这里插入图片描述
如果你想指定scapy生成端口号的范围,可以使用RandNum(),比如你只想在1000-1500这个范围内生成端口号,可以使用RandNum(1000,1500)来指定,举例如下:

ans, unans = sr(IP(dst = "192.168.43.1") / TCP(sport = RandNum(666,888), dport = 8082, flags = "S")/"test")

这里RandNum()帮我们生成了837这个源端口号
在这里插入图片描述

0x06 python代码实现端口扫描

tcp中标记位flags有如下几种

S (SYN), F (FIN), P (PUSH), R (RST), W (ECN CWR) , E (ECN-Echo) , . (no flags)

我们可以利用TCP三次握手原理进行主机存活的探测,当向目标主机直接发送ACK数据包时,如果目标主机存活就会返回一个RST数据包以终止这个不正常的TCP连接。其工作原理主要依据目标主机响应数据包中flags字段,如果flags字段有值则表示主机存活,该字段通常包括:SYN, FIN, ACK, PSH, RST, URG六种类型。SYN表示建立连接,FIN表示关闭连接,ACK表示应答,PSH表示包含DATA数据传输,RST表示连接重置,USG表示紧急指针。

使用python的scapy和nmap模块进行主机存活探测

扫描端口也是同理,当发送端的TCP SYN包发出后,大致会有下面四种情况发生:

  • 目的端口在接收端打开(也可以说接收端正在侦听该端口),收到SYN包的接收端回复SYN/ACK包给发送端,收到SYN/ACK包的发送端此时知道目的端口是打开的(open)。
  • 目的端口在接收端被关闭,收到SYN包的接收端回复RST包给发送端,告知发送端该目的端口已经被关闭了(closed)。
  • 如果发送端和接收端之间有防火墙或者使用ACL进行包过滤的路由器,那么SYN包在到达接收端之前就被防火墙或路由器拦截下来,此时防火墙或路由器会回复一个类型3(Unreachable,不可达)的ICMP包(注意不再是TCP包)给发送端告知该目的端口已经被过滤了(filtered)。
  • 如果ICMP在防火墙或路由器上被关闭了,这时SYN包会被防火墙、路由器"静悄悄"地丢弃,路由器和防火墙不会发送类型3的ICMP包告知发送端。此时发送端收不到任何回应(no response),这里我们同样可以判断该目的端口已经被过滤了(filtered)。

因为用惯了python3所以改成了python3的,并且改为了探测全端口,代码如下

from scapy.all import *

target = '192.168.43.1'

ans, unans = sr(IP(dst=target) / TCP(sport=RandShort(), dport=[i for i in range(1, 65535)], flags="S"), timeout=5)

for sent, received in ans:
    if received.haslayer(TCP) and str(received[TCP].flags) == "SA":
        print("Port " + str(sent[TCP].dport) + " of " + target + " is OPEN!")
    elif received.haslayer(TCP) and str(received[TCP].flags) == "RA":
        print("Port " + str(sent[TCP].dport) + " of " + target + " is closed!")
    elif received.haslayer(ICMP) and str(received[ICMP].type) == "3":
        print("Port " + str(sent[TCP].dport) + " of " + target + " is filtered!")

for sent in unans:
    print(str(sent[TCP].dport) + " is filtered!")

物理机用wireshark抓到了kali发送的目的端口为从1到65535的tcp数据包
【Python】scapy模块学习笔记_第18张图片
并且open的端口会返回一个SYN+ACK数据包
【Python】scapy模块学习笔记_第19张图片

看看kali的结果是否正确
【Python】scapy模块学习笔记_第20张图片

和用nmap的SYN扫描结果相同
【Python】scapy模块学习笔记_第21张图片

你可能感兴趣的:(#,python)