介绍
Scapy可作为python模块运行,也可以单独运行,scapy在kali自带,可以直接输入scapy进入交互命令行。
Scapy可对网络数据包进行发送、监听、解析等操作,类似于python-nmap模块,只不过scapy更偏向于底层操作。
函数
下面简单了解下scapy的基本使用,这里以kali系统为例,输入scapy进入交互命令行,如下图:
以上是一个小例子,我们简单了解下,首先scapy构造数据包时使用到的底层协议,创建时函数名称和协议名一样,只不过函数名要大写,创建后可以赋值给一个变量,例如上面的ip = IP()就创建了一个ip数据包。类似的还有TCP(),UDP()等。
随后函数中可传入相关参数,键值对形式,例如源地址设置使用src,目标地址使用dst。
很多时候一个数据包是由多个协议组成的,组成形式使用/分割,协议顺序由底层逐渐向上,例如构造一个tcp数据包:tcp = Ether()/IP()/TCP(),这样。
而协议函数中需要传入的属性,例如ip,包含了源地址、目标地址、版本、长度、协议类型、校验和等等,我们可以使用ls来查看,如下图。
下面再看下scapy的一些函数,当构造好数据包后我们要进行发送,发送函数有send和sendp,send工作在第三层,用来发送ip数据包,sendp工作在第二层,用来发送ether数据包。例如构造一个目标地址为192.168.150.135的icmp数据包,如下图。
上面的send和sendp发送数据包但不会接收返回的内容,scapy提供了接收内容的函数,分别是sr、sr1、srp,其中sr、sr1主要用于第三层,srp用于第二层,例如以下示例我们使用sr还向192.168.150.135发送一个icmp包,看下与send的区别,如下图。
可以看到接收到了返回结果,received收到两个包,got获取一个包,remaining剩0个包,结果是两个列表,results是应答包,unanswered是未收到的应答包。所以我们可以把结果存到变量中,然后使用summary函数查看详细内容,如下图。
而sr1和sr的区别在于sr1返回的只有应答包,没有未应答包。
三次握手
做一些示例前,需要对数据包的请求三次握手有一个简单的了解,否则我们无法构造数据包以及判断响应结果。
首先,需要了解下数据包中的flag标志代表的意义,如下表。
缩写 | 全写 | 意义 |
F | FIN | 结束会话 |
S | SYN | 开始会话 |
R | RST | 复位,中断连接 |
P | PUSH | 推送,立即发送数据包 |
A | ACK | 应答 |
U | URG | 紧急 |
E | ECE | 显示拥塞提醒回应 |
W | CWR | 拥塞窗口减少 |
三次握手:例如A和B通信,A首先发送数据包,其中flag字段值为SYN,即开始会话,(第一次)。B收到后会响应A一个包,其中flag为SYN/ACK,即应答会话,(第二次)。A接收到响应包后返回一个ACK,即应答,(第三次)。
全开扫描:即完成整个三次握手的扫描请求,以此判断目标机的情况。
半开扫描:即没有完成三次握手,自己只向目标机发一个syn,目标机返回syn/ack后,自己不发送ack响应,即为半开。
示例
示例1:一个简单的利用半开来判断目标某个端口情况,示例如下:
我们通过ip和tcp组合发送了一个数据包请求,端口为22,flags为S,即SYN开始会话,返回的数据包中可以看到flags的值为SA,即SYN/ACK响应包,此为正常返回,证明目标机22端口开放,此时知道目标机情况后我们没有发送后续也就是第三次握手的ack响应包,所以为半开扫描。
我们这时换一个端口,例如1234,响应如下。
可以发现返回的flags为RA,即RST/ACK,代表中断响应,可以判定目标1234端口为关闭状态。
示例2:
示例2我们简单看下sniff函数的应用,此函数功能可以捕获经过本机网卡的数据包,此函数有filter参数可以对数据包进行过滤,例如filter host过滤指定ip数据包,filter icmp过滤指定协议,其中需要过滤多个条件时也可以使用and、or运算符。
还有一个iface参数,可以指定要监听的网卡,count参数用来指定监听多少个数据包,监听够就停止。
例如这里我们需要一个监听器,监听有关192.168.150.136的icmp数据包,监听够三个就停止,如下。
Ping命令走的icmp协议,我们通过ping去触发了icmp的发送,可以看到sniff捕获到了,随后使用_符号来获取上一条命令的执行结果,然后通过nsummary来查看,这里nsummary和summary的区别在于:nsummary可以获取多个数据包,而summary只能获取单个。
示例3
这里依然来判断一个端口的情况,我们使用sr函数,sr会返回两个列表,一个是应答包一个是未应答包,我们可以通过结果来判断端口是开放、关闭、屏蔽等情况。发送一个syn后,如果返回syn/ack就是开放,rst/ack则关闭,如果是屏蔽状态则不会返回任何响应,屏蔽我们可以直接看未应答包的内容。
这里我们创建一个python脚本,代码如下。
from scapy.all import fuzz,TCP,IP,sr
ans, unans = sr(IP(dst="192.168.150.135")/fuzz(TCP(dport=80,flags="S")))
for s,r in ans:
if r[TCP].flags == 18:
print "This port is open"
if r[TCP].flags == 20:
print "This port is closed"
先来看下运行效果:
代码解析:
首先我们导入了scapy模块,因为scapy模块中有一个all文件,里面导入了所有的子模块,所以我们导入时需要写成scapy.all,随后sr发送构造的数据包,这里用到了一个新函数是fuzz,fuzz会随机填充内容然后发送,因为有些服务器碰到空数据包可能会不响应,fuzz填充来保证正确率。
然后我们使用了for遍历了ans变量,此变量存的是应答包,遍历后赋值给了s和r,这里之所以要赋值给两个变量的原因:我们在命令行看下运行结果:
ans返回的包的整体数量,并没有包详细信息,我们需要用res来查看包具体内容,例如TCP包:
可以发现有两个包,一个是我们发送的syn,一个是应答的syn/ack,而我们for遍历时,s存的就是send发送出去的包,r存的就是reply应答的包。
最后我们通过判断应答包flags的值来确定端口状态,这里注意的是不能通过S、SA去判断,而要通过他们的数值,具体为什么18就是open,20就是close,我们可以通过wireshark抓一个包来辅助理解,一个包的flags结构如下,其中数值从下往上依次是1,2,4,8,16,32,64,128,以此类推,字段值为1说明有返回,例如下图。
上图中syn为1有值,其数为2,ack有值数为16,16+2为18,所以18代表syn/ack,同理rst+ack等于4+16,所以20代表rst/ack,也就是重置关闭的意思。