![「Kali Linux网络扫描秘籍系列」使用 Scapy 跨网段发现IP](http://p2.ttnews.xyz/loading.gif)
本秘籍简介:
Scapy 是一种工具,允许用户制作并向网络中注入自定义数据包。 此工具可以用于构建 ICMP 协议请求,并将它们注入网络来分析响应。 这个特定的秘籍演示了如何使用 Scapy 在远程主机上执行第3层发现。
准备
使用 Scapy 执行第三层发现不需要实验环境,因为 Internet 上的许多系统都将回复ICMP 回显请求。 但是,强烈建议你只在您自己的实验环境中执行任何类型的网络扫描,除非你完全熟悉您受到任何管理机构施加的法律法规。 如果你希望在实验环境中执行此技术,你需要至少有一个响应 ICMP 请求的系统。
操作步骤
为了使用 Scapy 发送 ICMP 回显请求,我们需要开始堆叠层级来发送请求。 堆叠数据包时的一个好的经验法则是,通过 OSI 按照的各层进行处理。 你可以通过使用斜杠分隔每个层级来堆叠多个层级。 为了生成 ICMP 回显请求,IP 层需要与 ICMP请求堆叠。 为了开始,请使用 scapy 命令打开 Scapy 交互式控制台,然后将 IP 对象赋给变量:
root@kali:~# scapy
INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
WARNING: No route found for IPv6 destination :: (no default route?)
WARNING: IPython not available. Using standard Python shell instead.
AutoCompletion, History are disabled.
aSPY//YASa
apyyyyCY//////////YCa |
sY//////YSpcs scpCY//Pp | Welcome to Scapy
ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0
AYAsAYYYYYYYY///Ps cY//S |
pCCCCY//p cSSps y//Y | [url=https://github.com/secdev/scapy]https://github.com/secdev/scapy[/url]
SPPPP///a pP///AC//Y |
A//A cyP////C | Have fun!
p///Ac sC///a |
P////YCpc A//A | We are in France, we say Skappee.
scccccp///pSP///p p//Y | OK? Merci.
sY/////////y caa S//P | -- Sebastien Chabal
cayCyayP//Ya pY/Ya |
sY/PsY////YCc aC//Yp
sc sccaCY//PCypaapyCP//YSs
spCPY//////YPSps
ccaacs
>>> ip=IP()
>>> ip.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= hopopt
chksum= None
class="lazy" src="//p2.ttnews.xyz/loading.gif" data-original= 127.0.0.1
dst= 127.0.0.1
\options\
将新值赋给目标地址属性后,可以通过再次调用 display() 函数来验证更改。 请注意,当目标 IP 地址值更改为任何其他值时,源地址也会从回送地址自动更新为与默认接口关联的 IP 地址。 现在 IP 对象的属性已经适当修改了,我们将需要在我们的封包栈中创建第二层。 要添加到栈的下一个层是 ICMP 层,我们将其赋给单独的变量:
>>> ping=ICMP()
>>> ping.display()
###[ ICMP ]###
type= echo-request
code= 0
chksum= None
id= 0x0
seq= 0x0
在所提供的示例中,ICMP 对象使用 ping 变量名称初始化。 然后可以调用 display() 函数来显示 ICMP 属性的默认配置。 为了执行 ICMP 回显请求,默认配置就足够了。 现在两个层都已正确配置,它们可以堆叠来准备发送。 在Scapy 中,可以通过使用斜杠分隔每个层级来堆叠层级。 看看下面的命令集:
>>> ip.dst="172.16.155.1"
>>> ping_request=(ip/ping)
>>> ping_request.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= icmp
chksum= None
class="lazy" data-original= 172.16.155.150
dst= 172.16.155.1
\options\
###[ ICMP ]###
type= echo-request
code= 0
chksum= None
id= 0x0
seq= 0x0
一旦堆叠层级被赋给一个变量, display() 函数可以显示整个栈。 以这种方式堆叠层的过程通常被称为数据报封装。 现在已经堆叠了层级,并已经准备好发送请求。 这可以使用 Scapy 中的 sr1() 函数来完成:
>>> ping_reply=sr1(ping_request)
Begin emission:
...........................*Finished sending 1 packets.
Received 28 packets, got 1 answers, remaining 0 packets
>>> ping_reply.display()
###[ IP ]###
version= 4
ihl= 5
tos= 0x0
len= 28
id= 47924
flags=
frag= 0
ttl= 64
proto= icmp
chksum= 0x30f4
class="lazy" data-original= 172.16.155.1
dst= 172.16.155.150
\options\
###[ ICMP ]###
type= echo-reply
code= 0
chksum= 0xffff
id= 0x0
seq= 0x0
###[ Padding ]###
load= '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
在提供的示例中, sr1() 函数赋给了 ping_reply 变量。 这将执行该函数,然后将结果传递给此变量。 在接收到响应后,在 ping_reply 变量上调用 display() 函数来查看响应的内容。 请注意,此数据包是从我们发送初始请求的主机发送的,目标地址是 Kali 系统的 IP 地址。 另外,注意响应的 ICMP 类型是回应应答。 基于此示例,使用 Scapy 发送和接收 ICMP 的过程看起来很有用,但如果你尝试对非响应的目标地址使用相同的步骤,你会很快注意到问题:
>>> ip.dst="172.16.155.100"
>>> ping_request=(ip/ping)
>>> ping_reply=sr1(ping_request)
Begin emission:
..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................WARNING: Mac address to reach destination not found. Using broadcast.
Finished sending 1 packets.
......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................^C
Received 4340 packets, got 0 answers, remaining 1 packets
示例输出被截断,但此输出应该无限继续,直到你使用 Ctrl + C 强制关闭。 不向函数提供超时值, sr1() 函数会继续监听,直到接收到响应。 如果主机不是活动的,或者如果 IP 地址没有与任何主机关联,则不会发送响应,并且该功能也不会退出。 为了在脚本中有效使用此函数,应定义超时值:
>>> ping_reply=sr1(ping_request,timeout=1)
Begin emission:
....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................WARNING: Mac address to reach destination not found. Using broadcast.
Finished sending 1 packets.
................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Received 2340 packets, got 0 answers, remaining 1 packets
>>>
通过提供超时值作为传递给 sr1() 函数的第二个参数,如果在指定的秒数内没有收到响应,进程将退出。 在所提供的示例中, sr1() 函数用于将 ICMP 请求发送到无响应地址,因为未收到响应,会在 1 秒后退出。 到目前为止提供的示例中,我们将函数赋值给变量,来创建持久化和可操作的对象。 但是,这些函数不必复制给变量,也可以通过直接调用函数生成。
>>> answer=sr1(IP(dst="172.16.155.1")/ICMP(),timeout=1)
Begin emission:
..................................................................Finished sending 1 packets.
*
Received 67 packets, got 1 answers, remaining 0 packets
>>> answer.display()
###[ IP ]###
version= 4
ihl= 5
tos= 0x0
len= 28
id= 2141
flags=
frag= 0
ttl= 64
proto= icmp
chksum= 0xe3cb
class="lazy" data-original= 172.16.155.1
dst= 172.16.155.150
\options\
###[ ICMP ]###
type= echo-reply
code= 0
chksum= 0xffff
id= 0x0
seq= 0x0
###[ Padding ]###
load= '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
在这里提供的示例中,之前使用四个单独的命令完成的所有工作,实际上可以通过直接调用函数的单个命令来完成。 请注意,如果在超时值指定的时间范围内,ICMP 请求没有收到 IP 地址的回复,调用对象会产生异常。 由于未收到响应,因此此示例中赋值为响应的应答变量不会初始化:
>>> answer=sr1(IP(dst="172.16.155.100")/ICMP(),timeout=1)
Begin emission:
....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................WARNING: Mac address to reach destination not found. Using broadcast.
...Finished sending 1 packets.
.....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Received 2348 packets, got 0 answers, remaining 1 packets
>>> answer.dislay()
Traceback (most recent call last):
File "
", line 1, in AttributeError: 'NoneType' object has no attribute 'dislay'
有关这些不同响应的知识,可以用于生成在多个 IP 地址上按顺序执行 ICMP 请求的脚本。 脚本会循环遍历目标 IP 地址中最后一个八位字节的所有可能值,并为每个值发送一个 ICMP 请求。 当从每个 sr1() 函数返回时,将评估响应来确定是否接收到应答的响应:
#!/usr/bin/python3
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
if len(sys.argv)!=2:
print("Usage - ./ping.py [/24 network address]")
print("Exameple - ./pinger.py 172.16.155.1")
print("Exameple will perform an ICMP scan of the 172.16.155.0/24 range")
sys.exit()
address=str(sys.argv[1])
prefix=address.split(".")[0]+'.'+address.split(".")[1]+'.'+address.split(".")[2]+'.'
for addr in range(1,255):
answer=sr1(IP(dst=prefix+str(addr))/ICMP(),timeout=1,verbose=0)
if answer==None:
pass
else:
print(prefix+str(addr))
脚本的第一行标识了 Python 解释器所在的位置,以便脚本可以在不传递到解释器的情况下执行。 然后脚本导入所有 Scapy 函数,并定义 Scapy 日志记录级别,以消除脚本中不必要的输出。 还导入了子过程库,以便于从系统调用中提取信息。 第二个代码块是条件测试,用于评估是否向脚本提供了所需的参数。 如果在执行时未提供所需的参数,则脚本将输出使用情况的说明。 该说明包括工具的用法,示例和所执行任务的解释。
在这个代码块之后,有一个单独的代码行将所提供的参数赋值给 interface 变量。 下一个代码块使用 check_output() 子进程函数执行 ifconfig 系统调用,该调用也使用 grep 和 cut 从作为参数提供的本地接口提取 IP 地址。 然后将此输出赋给 ip 变量。 然后使用 split 函数从 IP 地址字符串中提取 / 24 网络前缀。 例如,如果 ip 变量包含 192.168.11.4 字符串,则值为 192.168.11 。 它将赋给 prefix 变量。
最后一个代码块是一个用于执行实际扫描的 for 循环。 for 循环遍历介于 0 和254 之间的所有值,并且对于每次迭代,该值随后附加到网络前缀后面。 在早先提供的示例的中,将针对 192.168.11.0 和 192.168.11.254 之间的每个 IP 地址发送 ICMP 回显请求。 然后对于每个回复的活动主机,将相应的 IP 地址打印到屏幕上,以表明主机在 LAN 上活动。 一旦脚本被写入本地目录,你可以在终端中使用句号和斜杠,然后是可执行脚本的名称来执行它。 看看以下用于执行脚本的命令:
root@kali:~# ./ping.py
Usage - ./ping.py [/24 network address]
Exameple - ./pinger.py 172.16.155.1
Exameple will perform an ICMP scan of the 172.16.155.0/24 range
root@kali:~# ./ping.py 172.16.155.0
172.16.155.1
172.16.155.2
如果在没有提供任何参数的情况下执行脚本,则会将使用方法输出到屏幕。 使用方法输出表明,此脚本需要用于定义要扫描的 / 24 网络的单个参数。 提供的示例使用 172.16.155.0 网络地址来执行脚本。 该脚本然后输出在 / 24 网络范围上的活动 IP 地址的列表。 此输出也可以使用尖括号重定向到输出文本文件,后跟输出文件名。 一个例子如下:
root@kali:~# ./ping.py 172.16.155.0 > output.txt
root@kali:~# ls output.txt
output.txt
root@kali:~# cat output.txt
172.16.155.1
172.16.155.2
然后可以使用 ls 命令来验证输出文件是否已写入文件系统,或者可以使用 cat 命令查看其内容。 也可以修改此脚本,来接受 IP 地址列表作为输入。 为此,必须更改 for 循环来循环遍历从指定的文本文件读取的行。 一个例子如下:
#!/usr/bin/python3
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
if len(sys.argv)!=2:
print("Usage - ./ping.py [filename]")
print("Exameple - ./pinger.py iplist.txt")
print("Exameple will perform an ICMP scan of the IP addresses in iplist.txt")
sys.exit()
filename=str(sys.argv[1])
file1=open(filename,"r")
for addr in file1:
answer=sr1(IP(dst=addr.strip())/ICMP(),timeout=1,verbose=0)
if answer==None:
pass
else:
print(str(addr.strip()))
与之前的脚本唯一的主要区别是,它接受一个输入文件名作为参数,然后循环遍历此文件中列出的每个 IP 地址进行扫描。 与其他脚本类似,生成的输出包括响应ICMP 回显请求的系统的相关 IP 地址的简单列表,其中包含 ICMP 回显响应:
root@kali:~# ./ping2.py
Usage - ./ping.py [filename]
Exameple - ./pinger.py iplist.txt
Exameple will perform an ICMP scan of the IP addresses in iplist.txt
root@kali:~# ./ping2.py iplist.txt
172.16.155.1
172.16.155.2
此脚本的输出可以以相同的方式重定向到输出文件。 使用作为参数提供的输入文件来执行脚本,然后使用尖括号重定向输出,后跟输出文本文件的名称。 一个例子如下:
root@kali:~# ./ping2.py iplist.txt > output.txt
root@kali:~# ls output.txt
output.txt
root@kali:~# cat output.txt
172.16.155.1
172.16.155.2
工作原理
此处使用 Scapy 通过构造包括 IP 层和附加的 ICMP 请求的请求来执行 ICMP 第三层发现。 IP 层能够将封包路由到本地网络之外,并且 ICMP 请求用于从远程系统请求响应。 在 Python 脚本中使用此技术,可以按顺序执行此任务,来扫描多个系统或整个网络范围。
閱讀更多 Web安全陪跑團 的文章