频道栏目
首页 > 安全 > 网络安全 > 正文

Python的无状态SYN快速扫描

2017-01-28 10:21:00           
收藏   我要投稿

Python的无状态SYN快速扫描

scapy

Scapy 是一个python的库,是一个强大的操纵报文的交互程序。它可以伪造或者解析多种协议的报文,还具有发送、捕获、匹配请求和响应这些报文以及更多的功能。所以我们使用scapy编写扫描程序。

有状态的扫描

#! /usr/bin/python

import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

dst_ip = "10.0.0.1"

src_port = RandShort()

dst_port=80

stealth_scan_resp = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="S"),timeout=10)

if(str(type(stealth_scan_resp))==""):

print "Filtered"

elif(stealth_scan_resp.haslayer(TCP)):

if(stealth_scan_resp.getlayer(TCP).flags == 0x12):

send_rst = sr(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="R"),timeout=10)

print "Open"

elif (stealth_scan_resp.getlayer(TCP).flags == 0x14):

print "Closed"

elif(stealth_scan_resp.haslayer(ICMP)):

if(int(stealth_scan_resp.getlayer(ICMP).type)==3 and int(stealth_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):

print "Filtered"

这是那篇文章中给出的syn扫描的代码,可以看到对dst_ip的dport端口发送了SYN,然后对返回的数据包进行了详细的处理。

代码中发送数据包的函数均为scapy包中的sr*发包函数,他们会等待服务器的回复,所以要设置timeout参数,当进行大量扫描时,这个等待的时间会成为提高扫描速度的瓶颈,不论timeout -1s还是减了几秒,还是使用多线程也还是很慢。因此考虑用异步的无状态的扫描提升速度。

无状态的扫描

前面我们知道了提升扫描速度的瓶颈在于发包后等待回复的时间,所以去掉这个等待时间我们就可以提升扫描速度,同时因为去掉了等待,我们需要另加一个收包的模块,用来收集扫描后返回的信息。

在无状态扫描中,收发是异步的,发包的模块不关心收包模块会不会收到回复、收包模块也不知道发包模块向谁发送了什么,也就是收发包模块间没有交互,发包的函数只负责发送,收包的模块接收特定tcp flags字段的数据包就好,这样就没有了等待回复的时间。

这也导致了:

1. 得到扫描结果的顺序和发包的顺序不一致,因为不同的目标可能有不同的响应时间。

2. 扫描的速度取决于带宽,我们可以一次发送大量的包出去,所以需要根据你的网络环境,选择合适的发包速度 (寝室的路由器就被搞崩过)

3. 使用扫描器时本机的网络环境需要很安静,因为收包的模块不知道这个数据包是被探测的服务器返回的,还是本机的程序进行的通信,比如mac会进行各种请求。。kali就是完全安静的。

4. 由于发包后不需要等待回复,所以可以用scapy包中的send函数,发包模块发包后拍拍屁股就可以走了。

5. 网络不好的时候,可能出现同一个目标的ip出现多次,所以必要时需要对结果进行去重,并且降低扫描速度。

python实现

发包部分

from scapy.all import *

import netaddr

ip = ['1.34.0.0/16', '1.33.0.0/16']

port = [80, 81, 82]

ipArray = []

portArray = []

for i in ip: ipArray.extend([str(i) for i in netaddr.IPNetwork(i).subnet(24)])

portArray = [port[i:i+3] for i in range(0, len(port), 3)]

for i in ipArray:

for j in portArray:

send(IP(dst=i)/TCP(dport=j, flags=2), verbose=False)

print i, j

发包模块使用了scapy和netaddr包,ip 和port 两个列表定义了要被扫描的部分

netaddr包用于处理ip,由于scapy的send发包函数可以传入一个IP段为目的ip,而且实践证明这样比一个for循环一个一个发快的多的多,测试了几次之后发现一次探测一个c段比较好,能兼顾速度和准确率。所以将字符串的ip段”1.34.0.0/16″初始化一个IPNetwork类,并使用subnet函数分割为c段,返回一个列表,再将这些列表合并,就得到了由c段组成的所有需要扫描的ip地址。

同理端口一次只扫描三个,将端口分为三个一组,存进portArray中。

最后发包过程中,可以选择先遍历ip或先遍历端口,注意send函数verbose参数为False避免输出很多东西,构造的数据包TCP首部flags为2,也就是flags字段只有SYN标志。如下图

 

\

 

收包部分

from scapy.all import *

iface = 'eth0'

userIP = '192.168.205.160'

def prn(pkt):

print pkt.sprintf("%IP.src%:%IP.sport% %TCP.flags%")

sniff(iface=iface, filter='tcp and dst %s and tcp[13:1] & 18==18'%userIP, prn=prn)

收包模块部分也需要导入scapy包,定义了用户的网卡名iface和本机ip userIP,传入本机ip的目的是过滤到目标为本机的数据包,在虚拟机上使用时需要格外注意。

sniff函数为scapy包的嗅探函数,用途为将iface网卡上的、符合filter的数据包传给prn回调函数进行处理,首先注意嗅探需要root权限,然后是filter函数,他也可以写成这样

sniff(iface=iface, lfilter=lambda x:x.haslayer(TCP) and x[IP].dst==userIP and x.flags==18, prn=prn)

lfilter参数传入一个函数,返回True则留下这个数据包,反之False则丢弃它, filter 传入的是tcpdump的过滤语法,有更高的效率,所以推荐使用filter

至于tcp[13:1] & 18==18是怎么来的,因为在syn扫描中,我们向目标端口发送SYN,如果它开放的话会回复SYN+ACK,也就是SYN ACK位均为1,在上面tcp首部的图中,ACK为高位,SYN为低位,2(SYN) + 16(ACK) = 18。

此外tcp[13:1]是tcpdump里的一个高级语法,意为取tcp数据包的下标为13的字节(也就是第14个字节)开始的1个字节,也就是上面图中flags所在的字节,这样用其值与18与一下,就过滤掉了别的包。

在回调函数prn中,可以对扫描结果进行处理,可以打印出来,也可以存入文件中。(sprintf是scapy包中的格式化输出函数)

组合起来

上面实际是两个文件,可以用多线程,主线程发包,另开一个线程sniff嗅探达到整合的目的。

当然也可以粗暴的开两个命令行,分别执行两个文件,在收包那里,就可以看到扫描结果很快的出来啦。

上一篇:shellcode编程:在内存中解析API地址
下一篇:为苹果ATS和微信小程序搭建 Nginx + HTTPS 服务
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站