网络扫描指的是一组过程,用于调查活动主机、主机类型、打开的端口以及主机上运行的服务类型。网络扫描是情报收集的一部分,攻击者可以通过它创建目标组织的配置文件。
在本章中,我们将介绍以下主题:
- 如何检查实时系统
- 平扫
- TCP 扫描器
- 如何创建高效的 IP 扫描程序
- 在目标计算机上运行的服务
- 端口扫描仪的概念
- 如何创建高效的端口扫描程序
您应该具备 TCP/IP 层通信的基本知识。在继续之前,协议数据单元(PDU的概念应该清楚。
PDU 是协议中指定的数据单位。它是各层数据的通用术语:
- 对于应用层,PDU 表示数据
- 对于传输层,PDU 表示一个段
- 对于 internet 或网络层,PDU 表示数据包
- 对于数据链路层或网络访问层,PDU 表示帧
- 对于物理层,即物理传输,PDU 指示位
ping 扫描涉及向主机发送ICMP 回显请求。如果主机处于活动状态,则返回ICMP 回显回复,如下图所示:
ICMP 请求和回复
操作系统的ping
命令提供了检查主机是否处于活动状态的工具。考虑一种情况,您必须测试 IP 地址的完整列表。在这种情况下,如果逐个测试 IP 地址,将需要花费大量的时间和精力。为了处理这种情况,我们使用 ping-sweep。
Ping 扫描用于通过发送 ICMP 回显请求和 ICMP 回显回复,从一系列 IP 地址中识别活动主机。攻击者或 pentester 可以根据子网和网络地址计算网络范围。在本节中,我将演示如何利用操作系统的 ping 功能。
首先,我将编写一段简单的代码,如下所示:
import os
response = os.popen('ping -n 1 10.0.0.1')
for line in response.readlines():
print line,
在前面的代码中,import os
导入 OS 模块,以便我们可以在 OS 命令上运行。下一行,os.popen('ping -n 1 10.0.0.1')
接受 DOS 命令,作为字符串传入,并返回连接到命令标准输入或输出流的类似文件的对象。ping –n 1 10.0.0.1
命令是一个 Windows 操作系统命令,用于发送一个 ICMP 回显请求数据包。通过读取os.psopen()
函数,可以截获命令的输出。输出存储在response
变量中。在下一行中,readlines()
函数用于读取类似文件的对象的输出。
程序的输出如下:
G:Project SnakeChapter 2ip>ips.py
Pinging 10.0.0.1 with 32 bytes of data:
Reply from 10.0.0.1: bytes=32 time=3ms TTL=64
Ping statistics for 10.0.0.1:
Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 3ms, Maximum = 3ms, Average = 3ms
输出显示reply
、byte
、time
和TTL
值,表示主机处于活动状态。考虑 IP 输出 T4 的程序的另一个输出:
G:Project SnakeChapter 2ip>ips.py
Pinging 10.0.0.2 with 32 bytes of data:
Reply from 10.0.0.16: Destination host unreachable.
Ping statistics for 10.0.0.2:
Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
前面的输出显示主机不是活动的。
上述代码对于正常工作非常重要,与汽车发动机类似。为了使其功能全面,我们需要修改代码,使其独立于平台,并产生易于阅读的输出。
我希望我的代码适用于一系列 IP 地址:
import os
net = raw_input("Enter the Network Address ")
net1= net.split('.')
print net1
a = '.'
net2 = net1[0]+a+net1[1]+a+net1[2]+a
print net2
st1 = int(raw_input("Enter the Starting Number "))
en1 = int(raw_input("Enter the Last Number "))
前面的代码要求提供子网的网络地址,但您可以提供子网的任何 IP 地址。下一行net1= net.split('.')
将 IP 地址分为四部分。net2 = net1[0]+a+net1[1]+a+net1[2]+a
语句构成网络地址。最后两行要求提供一系列 IP 地址。
要使其独立于平台,请使用以下代码:
import os
import platform
oper = platform.system()
if (oper=="Windows"):
ping1 = "ping -n 1 "
elif (oper== "Linux"):
ping1 = "ping -c 1 "
else :
ping1 = "ping -c 1 "
前面的代码确定代码是在 Windows 操作系统上运行还是在 Linux 平台上运行。oper = platform.system()
语句将此通知正在运行的操作系统,因为ping
命令在 Windows 和 Linux 中不同。Windows 操作系统使用ping –n 1
发送 ICMP 回显请求的一个数据包,而 Linux 使用ping –c 1
。
现在,让我们看看完整的代码,如下所示:
import os
import platform
from datetime import datetime
net = raw_input("Enter the Network Address ")
net1= net.split('.')
a = '.'
net2 = net1[0]+a+net1[1]+a+net1[2]+a
st1 = int(raw_input("Enter the Starting Number "))
en1 = int(raw_input("Enter the Last Number "))
en1=en1+1
oper = platform.system()
if (oper=="Windows"):
ping1 = "ping -n 1 "
elif (oper== "Linux"):
ping1 = "ping -c 1 "
else :
ping1 = "ping -c 1 "
t1= datetime.now()
print "Scanning in Progress"
for ip in xrange(st1,en1):
addr = net2+str(ip)
comm = ping1+addr
response = os.popen(comm)
for line in response.readlines():
if 'ttl' in line.lower():
break
if 'ttl' in line.lower():
print addr, "--> Live"
t2= datetime.now()
total =t2-t1
print "scanning complete in " , total
在前面的代码中有一些新的东西。for ip in xrange(st1,en1):
语句提供数字值,即 IP 地址的最后一个八位字节值。在for
循环中,addr = net2+str(ip)
语句使其成为一个完整的 IP 地址,comm = ping1+addr
语句使其成为一个完整的 OS 命令,并传递给os.popen(comm)
。if(line.count("TTL")):
语句检查行中是否出现TTL
。如果在该行中找到任何TTL
值,则使用break
语句中断该行的进一步处理。接下来的两行代码将 IP 地址打印为 live,其中找到了TTL
。我用datetime.now()
来计算扫描的总时间。
ping_sweep.py
程序的输出如下:
G:Project SnakeChapter 2ip>python ping_sweep.py
Enter the Network Address 10.0.0.1
Enter the Starting Number 1
Enter the Last Number 60
Scanning in Progress
10.0.0.1 --> Live
10.0.0.2 --> Live
10.0.0.5 --> Live
10.0.0.6 --> Live
10.0.0.7 --> Live
10.0.0.8 --> Live
10.0.0.9 --> Live
10.0.0.10 --> Live
10.0.0.11 --> Live
scanning complete in 0:02:35.230000
扫描 60 个 IP 地址需要 2 分 35 秒。
Ping-sweep 处理 ICMP 回显请求和 ICMP 回显回复。许多用户关闭 ICMP 回显回复功能或使用防火墙阻止 ICMP 数据包。在这种情况下,ping 扫描扫描仪可能无法工作。在这种情况下,需要进行 TCP 扫描。我希望您熟悉三方握手,如下图所示:
为了建立连接,主机执行三方握手。建立 TCP 连接的三个步骤如下:
- 客户端发送带有SYN标志的段;这意味着客户端请求服务器启动会话
- 服务器以应答的形式发送包含ACK和SYN标志的段
- 客户端以确认标志进行响应
现在,让我们看一下 TCP 扫描的以下代码:
import socket
from datetime import datetime
net= raw_input("Enter the IP address ")
net1= net.split('.')
a = '.'
net2 = net1[0]+a+net1[1]+a+net1[2]+a
st1 = int(raw_input("Enter the Starting Number "))
en1 = int(raw_input("Enter the Last Number "))
en1=en1+1
t1= datetime.now()
def scan(addr):
sock= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket.setdefaulttimeout(1)
result = sock.connect_ex((addr,135))
if result==0:
return 1
else :
return 0
def run1():
for ip in xrange(st1,en1):
addr = net2+str(ip)
if (scan(addr)):
print addr , "is live"
run1()
t2= datetime.now()
total =t2-t1
print "scanning complete in " , total
前面代码的上半部分与前面代码中的相同。这里,我们使用两个函数。首先,scan(addr)
函数使用了第 1 章Python 中讨论过的套接字,具有渗透测试和联网功能。result = sock.connect_ex((addr,135))
语句返回一个错误指示符。如果操作成功,错误指示灯为0
,否则为errno
变量的值。这里,我们使用了端口135
;此扫描仪适用于 Windows 系统。有一些端口通常是打开的,例如137
、138
、139
(NetBIOS 名称服务)和445
(Microsoft DSActive Directory)。因此,为了获得更好的结果,您必须更改端口并重复扫描。
iptcpscan.py
程序的输出如下:
G:Project SnakeChapter 2ip>python iptcpscan.py
Enter the IP address 10.0.0.1
Enter the Starting Number 1
Enter the Last Number 60
10.0.0.8 is live
10.0.0.11 is live
10.0.0.12 is live
10.0.0.15 is live
scanning complete in 0:00:57.415000
G:Project SnakeChapter 2ip>
让我们更改端口号。使用137
,您将看到以下输出:
G:Project SnakeChapter 2ip>python iptcpscan.py
Enter the IP address 10.0.0.1
Enter the Starting Number 1
Enter the Last Number 60
scanning complete in 0:01:00.027000
G:Project SnakeChapter 2ip>
该端口号不会产生任何结果。再次更改端口号。使用445
,输出如下:
G:Project SnakeChapter 2ip>python iptcpscan.py
Enter the IP address 10.0.0.1
Enter the Starting Number 1
Enter the Last Number 60
10.0.0.5 is live
10.0.0.13 is live
scanning complete in 0:00:58.369000
G:Project SnakeChapter 2ip>
前面三个输出显示10.0.0.5
、10.0.0.8
、10.0.0.11
、10.0.0.12
、10.0.0.13
和10.0.0.15
处于活动状态。这些 IP 地址在 Windows 操作系统上运行。这是一个用于检查 Linux 的公共开放端口并使 IP 成为完整的 IP TCP 扫描程序的练习。
到目前为止,您已经看到了 ping 扫描扫描程序和 IP-TCP 扫描程序。想象一下,你买了一辆拥有所有必要设施的汽车,但它的速度非常慢;你觉得这是浪费时间和金钱。当我们的程序执行非常慢时,也会发生同样的事情。为了扫描 60 台主机,ping_sweep.py
程序花了 2 分 35 秒来扫描相同范围的 IP 地址,TCP 扫描程序花了将近一分钟。这花了很多时间来产生结果。但别担心。Python 为您提供了多线程,这将使您的程序更快。
我已经编写了一个关于使用多线程 ping sweep 的完整程序,我将在本节中向您解释:
import os
import collections
import platform
import socket, subprocess,sys
import threading
from datetime import datetime
''' section 1 '''
net = raw_input("Enter the Network Address ")
net1= net.split('.')
a = '.'
net2 = net1[0]+a+net1[1]+a+net1[2]+a
st1 = int(raw_input("Enter the Starting Number "))
en1 = int(raw_input("Enter the Last Number "))
en1 =en1+1
dic = collections.OrderedDict()
oper = platform.system()
if (oper=="Windows"):
ping1 = "ping -n 1 "
elif (oper== "Linux"):
ping1 = "ping -c 1 "
else :
ping1 = "ping -c 1 "
t1= datetime.now()
'''section 2'''
class myThread (threading.Thread):
def __init__(self,st,en):
threading.Thread.__init__(self)
self.st = st
self.en = en
def run(self):
run1(self.st,self.en)
'''section 3'''
def run1(st1,en1):
#print "Scanning in Progess"
for ip in xrange(st1,en1):
#print ".",
addr = net2+str(ip)
comm = ping1+addr
response = os.popen(comm)
for line in response.readlines():
if(line.count("TTL")):
break
if (line.count("TTL")):
#print addr, "--> Live"
dic[ip]= addr
''' Section 4 '''
total_ip =en1-st1
tn =20 # number of ip handled by one thread
total_thread = total_ip/tn
total_thread=total_thread+1
threads= []
try:
for i in xrange(total_thread):
en = st1+tn
if(en >en1):
en =en1
thread = myThread(st1,en)
thread.start()
threads.append(thread)
st1 =en
except:
print "Error: unable to start thread"
print "t
Number of Threads active:", threading.activeCount()
for t in threads:
t.join()
print "Exiting Main Thread"
dict = collections.OrderedDict(sorted(dic.items()))
for key in dict:
print dict[key],"-->" "Live"
t2= datetime.now()
total =t2-t1
print "scanning complete in " , total
section 1
部分与上一程序相同。这里添加了一个有序字典,因为它记住了内容添加的顺序。如果您想知道哪个线程首先给出输出,那么有序字典适合这里。section 2
部分包含线程类,class myThread (threading.Thread):
语句初始化线程类。self.st = st
和self.en = en
语句取 IP 地址的起始和结束范围。section 3
部分包含run1
函数的定义,该函数是汽车的引擎,每个线程使用不同的 IP 地址范围调用该函数。dic[ip]= addr
语句将主机 ID 存储为密钥,将 IP 地址存储为有序字典中的值。section 4
语句在本规范中是全新的;total_ip
变量是要扫描的 IP 地址总数。
tn =20
变量的意义在于它表示一个线程将扫描 20 个 IP 地址。total_thread
变量包含需要扫描total_ip
的线程总数,表示 IP 地址的数量。threads= []
语句创建一个空列表,用于存储线程。for
循环for i in xrange(total_thread):
产生线程:
en = st1+tn
if(en >en1):
en =en1
thread = myThread(st1,en)
thread.start()
st1 =en
前面的代码生成 20-20 个 IP 地址的范围,例如st1-20, 20-40 ......-en1
。thread = myThread(st1,en)
语句是 threading 类的 thread 对象:
for t in threads:
t.join()
前面的代码终止所有线程。下一行dict = collections.OrderedDict(sorted(dic.items()))
创建了一个新的排序字典dict
,其中按顺序包含 IP 地址。下一行按顺序打印实时 IP。threading.activeCount()
语句显示产生了多少线程。一张图片上写着 1000 个单词。下图做了同样的事情:
创建和处理线程
ping_sweep_th_.py
程序的输出如下:
G:Project SnakeChapter 2ip>python ping_sweep_th.py
Enter the Network Address 10.0.0.1
Enter the Starting Number 1
Enter the Last Number 60
Number of Threads active: 4
Exiting Main Thread
10.0.0.1 -->Live
10.0.0.2 -->Live
10.0.0.5 -->Live
10.0.0.6 -->Live
10.0.0.10 -->Live
10.0.0.13 -->Live
scanning complete in 0:01:11.817000
扫描在 1 分 11 秒内完成。作为练习,更改tn
变量的值,将其从2
设置为30
,然后研究结果,找出tn
的最合适和最佳值。
到目前为止,您已经看到了通过多线程进行 ping 扫描;现在,我已经用 TCP 扫描方法编写了一个多线程程序:
import threading
import time
import socket, subprocess,sys
import thread
import collections
from datetime import datetime
'''section 1'''
net = raw_input("Enter the Network Address ")
st1 = int(raw_input("Enter the starting Number "))
en1 = int(raw_input("Enter the last Number "))
en1=en1+1
dic = collections.OrderedDict()
net1= net.split('.')
a = '.'
net2 = net1[0]+a+net1[1]+a+net1[2]+a
t1= datetime.now()
'''section 2'''
class myThread (threading.Thread):
def __init__(self,st,en):
threading.Thread.__init__(self)
self.st = st
self.en = en
def run(self):
run1(self.st,self.en)
'''section 3'''
def scan(addr):
sock= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket.setdefaulttimeout(1)
result = sock.connect_ex((addr,135))
if result==0:
sock.close()
return 1
else :
sock.close()
def run1(st1,en1):
for ip in xrange(st1,en1):
addr = net2+str(ip)
if scan(addr):
dic[ip]= addr
'''section 4'''
total_ip =en1-st1
tn =20 # number of ip handled by one thread
total_thread = total_ip/tn
total_thread=total_thread+1
threads= []
try:
for i in xrange(total_thread):
#print "i is ",i
en = st1+tn
if(en >en1):
en =en1
thread = myThread(st1,en)
thread.start()
threads.append(thread)
st1 =en
except:
print "Error: unable to start thread"
print "t Number of Threads active:", threading.activeCount()
for t in threads:
t.join()
print "Exiting Main Thread"
dict = collections.OrderedDict(sorted(dic.items()))
for key in dict:
print dict[key],"-->" "Live"
t2= datetime.now()
total =t2-t1
print "scanning complete in " , total
理解这个程序应该没有困难。下图显示了所有内容:
IP-TCP 扫描器
该类将范围作为输入,并调用run1()
函数。section 4
部分创建一个线程,它是一个类的实例,占用一个较短的范围,并调用run1()
函数。run1()
函数有一个 IP 地址,从线程获取范围,并生成输出。
iptcpscan.py
程序的输出如下:
G:Project SnakeChapter 2ip>python iptcpscan_t.py
Enter the Network Address 10.0.0.1
Enter the starting Number 1
Enter the last Number 60
Number of Threads active: 4
Exiting Main Thread
10.0.0.5 -->Live
10.0.0.13 -->Live
scanning complete in 0:00:20.018000
20 秒内 60 个 IP 地址;表演还不错。作为练习,将两个扫描仪合并为一个扫描仪。
以前的 IP 扫描程序可以在 Windows 和 Linux 上工作。现在,我将解释一个 IP 扫描器,它速度非常快,但只能在 Linux 机器上工作。在前面的代码中,我们使用了 ping 实用程序,但是现在我们将使用我们自己的 ping 包来进行 ping
IP 扫描仪背后的概念非常简单。我们将生成多个线程来向不同的 IP 地址发送 ping 数据包。一个守护进程线程将负责捕获这些 ping 数据包的响应。要运行 IP 扫描程序,您需要安装 ping 模块。您可以从这里下载 ping 模块的.zip
文件:https://pypi.python.org/pypi/ping 。只需解压缩或解压,浏览文件夹,然后运行以下命令:
python setup.py install
如果您不想安装模块,那么只需从解压缩文件夹中复制ping.py
文件,并将其粘贴到您要运行 IP 扫描程序代码的文件夹中即可
*让我们看看ping_sweep_send_rec.py
的代码:
import socket
from datetime import datetime
import ping
import struct
import binascii
from threading import Thread
import time
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))
net = raw_input("Enter the Network Address ")
net1= net.rsplit('.',1)
net2 = net1[0]+'.'
start1 = int(raw_input("Enter the Starting Number "))
end1 = int(raw_input("Enter the Last Number "))
end1 =end1+1
seq_ip = []
total_ip =end1-start1
tn =10 # number of ip handled by one thread
total_thread = total_ip/tn
total_thread=total_thread+1
threads= []
t1= datetime.now()
def send_ping(st1,en1):
for each in xrange(st1,en1):
try:
ip = net2+str(each)
ping.do_one(ip,1,32)
except Exception as e :
print "Error in send_ping", e
def icmp_sniff():
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, 8)
while True:
pkt = s.recvfrom(2048)
num = pkt[0][14].encode('hex')
ip_length = (int(num) % 10) * 4
ipheader = pkt[0][14:14+ip_length]
icmp_h =pkt[0][14+ip_length]
ip_hdr = struct.unpack("!8sBB2s4s4s",ipheader[:20])
icmp_hdr = struct.unpack("!B",icmp_h)
if(ip_hdr[2]==1) and (icmp_hdr[0]==0):
ip = socket.inet_ntoa(ip_hdr[4])
ip1= ip.rsplit('.',1)
list_temp = [ip1[1].zfill(3),ip]
seq_ip.append(list_temp)
scan_thread = Thread(target=icmp_sniff)
scan_thread.setDaemon(True)
scan_thread.start()
st1 = start1
try:
for i in xrange(total_thread):
en = st1+tn
if(en >end1):
en =end1
ping_thread = Thread(target=send_ping,args=(st1,en,) )
ping_thread.start()
threads.append(ping_thread)
st1 =en
except Exception as e :
print "Error in Thread", e
for t in threads:
t.join()
time.sleep(1)
seq_ip.sort(key=lambda x: int(x[0]))
print "S.no\t","IP"
for each in seq_ip:
print each[0]," ", each[1]
t2= datetime.now()
print "Time taken ", t2-t1
在前面的代码中,IP 计算和线程创建部分与前面的代码块非常相似。线程调用send_ping
函数,在 ping 模块的帮助下发送 ping 数据包。在语法ping.do_one(ip,1,32)
中,第二个和第三个参数分别表示超时和数据包大小。因此,我将1
设置为超时,将32
设置为 ping 数据包大小。icmp_sniff
中的代码对您来说可能是新的。您将在第 3 章、嗅探和渗透测试中了解所有语法的全部细节。简而言之,icmp_sniff
函数从传入的 ICMP 应答包中捕获发送方的 IP 地址。我们已经知道,ICMP 应答包的代码是0
。语法if(ip_hdr[2]==1)
和(icmp_hdr[0]==0)
意味着我们只需要 ICMP 和 ICMP 回复数据包
让我们运行代码并查看输出:
前面的输出显示,该程序在 254 台主机上执行扫描只需约 11 秒。在前面的代码中,我们为每个线程设置了 10 个 IP 地址。您可以更改每个线程的 IP 地址。使用不同的值并优化每个线程每个 IP 的值
本节专门介绍 nmap 爱好者。您可以在 Python 中使用nmap
。您只需安装python-nmap
模块和nmap
。安装它们的命令非常简单。通过使用 pip,我们可以安装python-nmap
:
pip install python-nmap
安装python-nmap
模块后,您可以通过导入来检查nmap
模块。如果导入时没有错误,则表示已成功安装。让我们检查一下nmap
中的内容:
>>>import nmap
>>> dir(nmap)
['ET', 'PortScanner', 'PortScannerAsync', 'PortScannerError', 'PortScannerHostDict', 'PortScannerYield', 'Process', '__author__', '__builtins__', '__doc__', '__file__', '__last_modification__', '__name__', '__package__', '__path__', '__version__', 'convert_nmap_output_to_encoding', 'csv', 'io', 'nmap', 'os', 're', 'shlex', 'subprocess', 'sys']
我们将使用PortScanner
类进行此操作。让我们看看代码,然后运行它:
import nmap, sys
syntax="OS_detection.py <hostname/IP address>"
if len(sys.argv) == 1:
print (syntax)
sys.exit()
host = sys.argv[1]
nm=nmap.PortScanner()
open_ports_dict = nm.scan(host, arguments="-O").get("scan").get(host).get("tcp")
print "Open ports ", " Description"
port_list = open_ports_dict.keys()
port_list.sort()
for port in port_list:
print port, "---\t-->",open_ports_dict.get(port)['name']
print "\n--------------OS detail---------------------\n"
print "Details about the scanned host are: \t", nm[host]['osmatch'][0]['osclass'][0]['cpe']
print "Operating system family is: \t\t", nm[host]['osmatch'][0]['osclass'][0]['osfamily']
print "Type of OS is: \t\t\t\t", nm[host]['osmatch'][0]['osclass'][0]['type']
print "Generation of Operating System :\t", nm[host]['osmatch'][0]['osclass'][0]['osgen']
print "Operating System Vendor is:\t\t", nm[host]['osmatch'][0]['osclass'][0]['vendor']
print "Accuracy of detection is:\t\t", nm[host]['osmatch'][0]['osclass'][0]['accuracy']
前面的代码非常简单:只需将nm=nmap.PortScanner()
作为一个对象。当你调用nm.scan(host, arguments="-O")
方法时,你会得到一个非常复杂的字典。以下输出是字典的一部分:
'scan': {'192.168.0.1': {'status': {'state': 'up', 'reason': 'localhost-response'}, 'uptime': {'seconds': '7191', 'lastboot': 'Mon Mar 19 20:43:41 2018'}, 'vendor': {}, 'addresses': {'ipv4': '192.168.0.1'}, 'tcp': {902: {'product': '', 'state': 'open', 'version': '', 'name': 'iss-realsecure', 'conf': '3', 'extrainfo': '', 'reason': 'syn-ack', 'cpe': ''}, 135: {'product': '', 'state': 'open', 'version': '', 'name': 'msrpc', 'conf': '3', 'extrainfo': '', 'reason': 'syn-ack', 'cpe': ''}, 139: {'product': '', 'state': 'open', 'version': '', 'name': 'netbios-ssn', 'conf': '3', 'extrainfo': '', 'reason': 'syn-ack', 'cpe': ''}, 5357: {'product': '', 'state': 'open', 'version': '', 'name': 'wsdapi', 'conf': '3', 'extrainfo': '', 'reason': 'syn-ack', 'cpe': ''}, 912: {'product': '', 'state': 'open', 'version': '', 'name': 'apex-mesh', 'conf': '3', 'extrainfo': '', 'reason': 'syn-ack', 'cpe': ''}, 445: {'product': '', 'state': 'open', 'version': '', 'name': 'microsoft-ds', 'conf': '3', 'extrainfo': '', 'reason': 'syn-ack', 'cpe': ''}}, 'hostnames': [{'type': '', 'name': ''}], 'osmatch': [{'osclass': [{'osfamily': 'Windows', 'vendor': 'Microsoft', 'cpe': ['cpe:/o:microsoft:windows_10'], 'type': 'general purpose', 'osgen': '10', 'accuracy': '100'}], 'line': '65478', 'name': 'Microsoft Windows 10 10586 - 14393', 'accuracy': '100'}], 'portused': [{'state': 'open', 'portid': '135', 'proto': 'tcp'}, {'state': 'closed', 'portid': '1', 'proto': 'tcp'}, {'state': 'closed', 'portid': '34487', 'proto': 'udp'}]}}}
从前面的代码中,很容易获得您需要的信息;但是需要基本的 Python 知识。让我们在四种不同的操作系统上运行代码。首先,我在 RedhatLinux5.3 和 Debian7 上运行了代码。您可以在以下输出中看到这一点:
从前面的输出中,您可以看到nmap
成功地找到了打开的 TCP 端口和所需的操作系统详细信息
让我们在 Windows 操作系统上运行nmap
:
在前面的输出中,nmap
成功找到了 Windows XP 和 Windows 10。nmap
模块中还有很多其他功能。您可以自己探索这些,并编写适当的代码
现在,您已经熟悉了如何扫描 IP 地址并识别子网中的活动主机。在本节中,我们将讨论在主机上运行的服务。这些服务是使用网络连接的服务。使用网络连接的服务必须打开一个端口;通过端口号,我们可以识别目标机器上正在运行的服务。在 pentesting 中,端口扫描的意义在于检查主机上是否运行非法服务。
考虑用户通常使用他们的计算机下载游戏的情况,并且在安装游戏期间识别木马。特洛伊木马进入隐藏模式;打开一个端口;将所有击键(包括日志信息)发送给黑客。在这种情况下,端口扫描有助于识别受害者计算机上运行的未知服务。
端口号范围从0
到65535
。众所周知的端口(也称为系统端口)是那些范围从0
到1023
并为特权服务保留的端口。范围从1024
到49151
的端口是用于应用的注册端口类供应商;例如,3306
端口是为 MySQL 保留的。
TCP 的三次握手作为端口扫描器的逻辑;在 TCP/IP 扫描程序中,您已经看到端口(137
或135
是一个 IP 地址在一定范围内的端口。但是,在端口扫描程序中,IP 只是一个范围内的一个端口。使用一个 IP 并尝试将每个端口连接为用户给定的范围。如果连接成功,端口将打开;否则,端口将保持关闭状态。
我已经为端口扫描编写了一些非常简单的代码:
import socket, subprocess,sys
from datetime import datetime
subprocess.call('clear',shell=True)
rmip = raw_input("t Enter the remote host IP to scan:")
r1 = int(raw_input("t Enter the start port numbert"))
r2 = int (raw_input("t Enter the last port numbert"))
print "*"*40
print "n Mohit's Scanner is working on ",rmip
print "*"*40
t1= datetime.now()
try:
for port in range(r1,r2):
sock= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket.setdefaulttimeout(1)
result = sock.connect_ex((rmip,port))
if result==0:
print "Port Open:-->t", port
# print desc[port]
sock.close()
except KeyboardInterrupt:
print "You stop this "
sys.exit()
except Exception as e :
print e
sys.exit()
t2= datetime.now()
total =t2-t1
print "scanning complete in " , total
主逻辑已写入try
块,表示汽车的发动机。您熟悉语法。让我们对输出进行 R&D。
portsc.py
程序的输出如下:
root@Mohit|Raj:/port#python portsc.py
Enter the remote host IP to scan:192.168.0.3
Enter the start port number 1
Enter the last port number 4000
****************************************
Mohit's Scanner is working on 192.168.0.3
****************************************
Port Open:--> 22
Port Open:--> 80
Port Open:--> 111
Port Open:--> 443
Port Open:--> 924
Port Open:--> 3306
scanning complete in 0:00:00.766535
前面的输出显示端口扫描仪在0.7
秒内扫描了 1000 个端口;连接已满,因为目标计算机和扫描仪计算机位于同一子网中。
让我们讨论另一个输出:
Enter the remote host IP to scan:10.0.0.1
Enter the start port number 1
Enter the last port number 4000
****************************************
Mohit's Scanner is working on 10.0.0.1
****************************************
Port Open:--> 23
Port Open:--> 53
Port Open:--> 80
Port Open:--> 1780
scanning complete in 1:06:43.272751
现在,让我们分析输出:为了扫描 4000 个端口,扫描仪花费了1:06:43.272751
小时。这花了很长时间。拓扑结构是:
192.168.0.10 --> 192.168.0.1 --> 10.0.0.16 ---> 10.0.0.1
192.168.0.1
和10.0.0.16
IP 地址是网关接口。我们在socket.setdefaulttimeout(1)
中放置了 1 秒,这意味着扫描仪在每个端口上最多花费 1 秒。总共 4000 个端口意味着如果所有端口都关闭,则所需的总时间将为 4000 秒;如果我们把它转换成小时,它将变成 1.07 小时,这几乎等于我们程序的输出。如果我们设置socket.setdefaulttimeout(.5)
,所花费的时间将减少到 30 分钟,这仍然是一个很长的时间。没有人会使用我们的扫描仪。对于 4000 个端口,所用时间应少于 100 秒。
我已经说明了一些要点,对于一个好的端口扫描器,应该加以考虑:
- 多线程应该用于高性能
socket.setdefaulttimeout(1)
方法应根据情况设置- 端口扫描程序应该能够获取主机名和域名
- 端口应提供带有端口号的服务名称
- 端口扫描应考虑总时间
- 要扫描端口
0
至65535
,所需时间应为 3 分钟左右
因此,现在我已经编写了我的端口扫描程序,通常用于端口扫描:
from threading import Thread
import time
import socket
from datetime import datetime
import cPickle
'''Section1'''
pickle_file = open("port_description.dat",'r')
data=skill=cPickle.load(pickle_file)
def scantcp(r1,r2,):
try:
for port in range(r1,r2):
sock= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket.setdefaulttimeout(c)
result = sock.connect_ex((rmip,port))
if result==0:
print "Port Open:-->\t", port,"--", data.get(port, "Not in Database")
sock.close()
except Exception as e:
print e
'''Section 2 '''
print "*"*60
print " \tWelcome, this is the Port scanner \n "
d=raw_input("\tPress D for Domain Name or Press I for IP Address\t")
if (d=='D' or d=='d'):
rmserver = raw_input("\t Enter the Domain Name to scan:\t")
rmip = socket.gethostbyname(rmserver)
elif(d=='I' or d=='i'):
rmip = raw_input("\t Enter the IP Address to scan: ")
else:
print "Wrong input"
port_start1 = int(raw_input("\t Enter the start port number\t"))
port_last1 = int(raw_input("\t Enter the last port number\t"))
if port_last1>65535:
print "Range not Ok"
port_last1 = 65535
print "Setting last port 65535"
conect=raw_input("For low connectivity press L and High connectivity Press H\t")
if (conect=='L' or conect=='l'):
c =1.5
elif(conect =='H' or conect=='h'):
c=0.5
else:
print "\twrong Input"
'''Section 3'''
print "\n Mohit's port Scanner is working on ",rmip
print "*"*60
t1= datetime.now()
total_ports=port_last1-port_start1
ports_by_one_thread =30
# tn number of port handled by one thread
total_threads=total_ports/ports_by_one_thread # tnum number of threads
if (total_ports%ports_by_one_thread!= 0):
total_threads= total_threads+1
if (total_threads > 300):
ports_by_one_thread= total_ports/300
if (total_ports%300 !=0):
ports_by_one_thread= ports_by_one_thread+1
total_threads = total_ports/ports_by_one_thread
if (total_ports%total_threads != 0):
total_threads= total_threads+1
threads= []
start1 = port_start1
try:
for i in range(total_threads):
last1=start1+ports_by_one_thread
# thread=str(i)
if last1>=port_last1:
last1 = port_last1
port_thread = Thread(target=scantcp,args=(start1,last1,) )
port_thread.start()
threads.append(port_thread)
start1=last1
except Exception as e :
print e
'''Section 4'''
for t in threads:
t.join()
print "Exiting Main Thread"
t2= datetime.now()
total =t2-t1
print "scanning complete in " , total
不要害怕看到完整的代码;我花了两个星期。我将向您解释完整的代码部分。在section1
中,前两行与存储端口信息的数据库文件相关,将在创建数据库文件时解释。scantcp()
函数由线程执行。在section 2
中,用于用户输入。如果用户提供的端口范围超过65535
,则代码会自动处理错误。低连接性和高连接性意味着如果您正在使用 internet,请使用低连接性。如果您在自己的网络上使用该代码,则可以使用高连接性。section 3
中写入了线程创建逻辑。30
端口将由一个线程处理,但如果线程数超过300
,则将重新计算每个线程的端口数公式。在for
循环中,创建线程,每个线程都有自己的端口范围。在section 4
中,线程被终止
我在做了大量实验后编写了前面的代码。
现在,是时候查看portsc15.py
程序的输出了:
K:\Book_projects\Project Snake 2nd\Chapter2_scanning>python port_scanner15.py
************************************************************
Welcome, this is the Port scanner
Press D for Domain Name or Press I for IP Address i
Enter the IP Address to scan: 10.0.0.1
Enter the start port number 1
Enter the last port number 4000
For low connectivity press L and High connectivity Press H l
Mohit's port Scanner is working on 10.0.0.1
************************************************************
Port Open:--> 875 -- Not in Database
Port Open:--> 3306 -- MySQL database system Official
Port Open:--> 80 -- QUIC (from Chromium) for HTTP Unofficial
Port Open:--> 111 -- ONC RPC (Sun RPC) Official
Port Open:--> 443 -- QUIC (from Chromium) for HTTPS Unofficial
Port Open:--> 22 -- , SCTP : Secure Shell (SSH)ΓÇöused for secure logins, file transfers (scp, sftp) and port forwarding Official
Port Open:--> 53 -- Domain Name System (DNS) Official
Exiting Main Thread
scanning complete in 0:00:31.778000
K:\Book_projects\Project Snake 2nd\Chapter2_scanning>
我们高效的端口扫描器提供的输出与以前的简单扫描器相同,但从性能角度来看,有很大的不同。一个简单的扫描仪花费的时间是1:06:43.272751
,但新的多线程扫描仪只花了 32 秒。它还显示服务名称。让我们检查端口1
到50000
的更多输出:
K:\Book_projects\Project Snake 2nd\Chapter2_scanning>python port_scanner15.py
************************************************************
Welcome, this is the Port scanner
Press D for Domain Name or Press I for IP Address i
Enter the IP Address to scan: 192.168.0.3
Enter the start port number 1
Enter the last port number 50000
For low connectivity press L and High connectivity Press H l
Mohit's port Scanner is working on 192.168.0.3
************************************************************
Port Open:--> 22 -- , SCTP : Secure Shell (SSH)ΓÇöused for secure logins, file transfers (scp, sftp) and port forwarding Official
Port Open:--> 875 -- Not in Database
Port Open:--> 53 -- Domain Name System (DNS) Official
Port Open:--> 80 -- QUIC (from Chromium) for HTTP Unofficial
Port Open:--> 8443 -- SW Soft Plesk Control Panel, Apache Tomcat SSL, Promise WebPAM SSL, McAfee ePolicy Orchestrator (ePO) Unofficial
Port Open:--> 111 -- ONC RPC (Sun RPC) Official
Port Open:--> 443 -- QUIC (from Chromium) for HTTPS Unofficial
Port Open:--> 3306 -- MySQL database system Official
Exiting Main Thread
scanning complete in 0:02:48.718000
所用时间为 2 分 48 秒;我在高连通性中做了同样的实验,所用的时间是0:01:23.819774
,几乎是前一次的一半。
现在,我将教您如何创建包含所有端口号描述的数据库文件;让我们了解如何创建包含所有端口描述的 pickle 数据库文件。打开以下链接:https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers 。
复制端口描述部分并将其保存在文本文件中。请参见以下屏幕截图:
让我们看看creatdicnew.py
将前面的文件转换为pickle
文件的代码:
import cPickle
pickle_file = open("port_description.dat","w")
file_name = raw_input("Enter the file name ")
f = open(file_name,"r")
dict1 = {}
for line in f:
key, value = line.split(':', 1)
dict1[int(key.strip())] = value.strip()
print "Dictionary is created"
cPickle.dump(dict1,pickle_file)
pickle_file.close()
print "port_description.dat is created"
运行上述代码时,代码将要求您输入文本文件名。给出文件名后,代码将文本文件转换为名为port_description.dat
的 pickle 文件。
完成网络扫描以收集有关网络、主机和主机上运行的服务的信息。使用操作系统的ping
命令进行网络扫描;ping 扫描利用 ping 功能并扫描 IP 地址列表。有时,ping-sweep 不起作用,因为用户可能会关闭其 ICMP 回显回复功能或使用防火墙阻止 ICMP 数据包。在这种情况下,ping 扫描扫描仪可能无法工作。在这种情况下,我们必须利用 TCP 三方握手;TCP 在传输层工作,因此我们必须选择要在其上执行 TCP 连接扫描的端口号。Windows 操作系统的某些端口始终处于打开状态,因此您可以利用这些打开的端口。第一个主要部分专门用于网络扫描;当您执行网络扫描时,您的程序应该具有最大的性能并占用最少的时间。为了显著提高性能,应该使用多线程。
扫描活动主机后,端口扫描用于检查特定主机上运行的服务;有时,一些程序使用允许木马和端口扫描的 internet 连接来检测这些类型的威胁。为了进行有效的端口扫描,多线程起着至关重要的作用,因为端口号从0
到65536
不等。要扫描一个巨大的列表,必须使用多线程。
在下一章中,您将看到嗅探及其两种类型:被动嗅探和主动嗅探。您还将学习如何捕获数据、数据包制作的概念以及使用 Scapy 库制作定制数据包。*