Scapy—Ping扫描
一、项目背景
最近在学 Scapy
,决定先从最基本的开始做起,于是有了这篇水文,先来看看文档对它的定义:
Scapy
是一个可以让用户发送、侦听和解析并伪装网络报文的Python
程序。这些功能可以用于制作侦测、扫描和攻击网络的工具。换言之,Scapy
是一个强大的操纵报文的交互程序。它可以伪造或者解析多种协议的报文,还具有发送、捕获、匹配请求和响应这些报文以及更多的功能。Scapy
可以轻松地做到像扫描(scanning
)、路由跟踪(tracerouting
)、探测(probing
)、单元测试(unit tests
)、攻击(attacks
)和发现网络(network discorvery
)这样的传统任务。它可以代替hping
,arpspoof
,arp-sk
,arping
,p0f
甚至是部分的Namp
,tcpdump
和tshark
的功能。
我总结一句,它说的真的都能实现(手动狗头)…
1、安装Scapy
pip3 install scapy
2、数据包的基本构成
数据包是从底层到高层构造的,ICMP
协议是应用层协议,这里要知道的是当 Type
为 0
时为 echo-reply
;当 Type
为 8
时为 echo-request
先来看基本的 ping
命令:
➜ ~ ping -c 1 baidu.com
PING baidu.com (39.156.69.79) 56(84) bytes of data.
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=1 ttl=49 time=38.4 ms
可以看到,上面的一条 ping
有显示的有序列号,生存周期,花费时间,生成周期一般都是 64
或 128
,序列号则是需要填的部分。
>>> p = IP(dst='10.87.51.1')/ICMP()
#这还可以换成 Ether()/IP(dst='10.87.51.1')/ICMP(),但是前面要加上以太网帧
>>> p.show()
###[ IP ]###
version= 4 #IP版本
ihl= None
tos= 0x0
len= None #长度
id= 1 #id是需要填的部分
flags=
frag= 0
ttl= 64 #生存周期
proto= icmp #协议
chksum= None
src= 10.87.51.1 #源地址
dst= 10.87.51.1 #目的地址
\options\
###[ ICMP ]###
type= echo-request #类型为请求包,reply的话会变成echo-reply
code= 0
chksum= None
id= 0x0 #需要填的部分加一
seq= 0x0 #需要填的部分加二
二、项目代码
1、扫描单个IP
先入门嘛,这先搞一个最基本的,扫描单个IP
。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# --author:valecalida--
# Edit time: 2020/4/17 19:29
from scapy.layers.inet import IP, ICMP, sr1
from random import randint
ip = '10.87.51.1'
ip_id = randint(1, 65535)
icmp_id = randint(1, 65535)
icmp_seq = randint(1, 65535)
packet = IP(dst=ip, ttl=64, id=ip_id)/ICMP(id=icmp_id, seq=icmp_seq)
response = sr1(packet, timeout=1, verbose=0)
if response:
print("[+] %s is alive" % str(ip))
else:
print("[-] %s is not alive" % str(ip))
控制台输出如下:
[+] 10.87.51.1 is alive
2、扫描整个网段
扫描网段就需要用到一个新的库:ipaddress
,它有功能可以根据掩码直接生成好 IP地址
。
from ipaddress import ip_network
host = '225.225.225.0/30'
ip_list = ip_network(host)
for ip in ip_list:
print(str(ip))
这里给它地址空间小一点,看看控制台,已经自动生成好了对应的 IP地址
。
225.225.225.0
225.225.225.1
225.225.225.2
225.225.225.3
大家也看出来问题了,就是它生成的地址中并没有去除不可用的地址,不过不影响程序进行,继续上代码,把扫描单个地址写出一个函数,在批量扫描的时候使用多进程,调用扫描单个的函数。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# --author:valecalida--
# Edit time: 2020/4/17 16:30
from scapy.layers.inet import IP, ICMP, sr1
from random import randint
from ipaddress import ip_network
from threading import Thread
import time
def ping_single(ip):
ip_id = randint(1, 65535)
icmp_id = randint(1, 65535)
icmp_seq = randint(1, 65535)
packet = IP(dst=ip, ttl=64, id=ip_id)/ICMP(id=icmp_id, seq=icmp_seq)
response = sr1(packet, timeout=1, verbose=0) #verbose不显示详情,也可替换为False
if response:
print("[+] %s is alive" % str(ip))
def ping_scan(network):
ip_list = ip_network(network)
for ip in ip_list:
t = Thread(target=ping_single, args=[str(ip)])
t.start()
if __name__ == '__main__':
host = '10.87.51.0/24'
t1 = time.time()
ping_scan(host)
t2 = time.time()
print("[+] 本次扫描共花费 %s 秒" % (t2 - t1))
看看控制台输出情况,扫描 256
台主机,共花费了约 10s
,其实还算可以(自卖自夸)。
[+] 10.87.51.1 is alive
[+] 本次扫描共花费 9.795329570770264 秒