Python 扫描测试详解

网络扫描指的是一组过程,用于调查活动主机、主机类型、打开的端口以及主机上运行的服务类型。网络扫描是情报收集的一部分,攻击者可以通过它创建目标组织的配置文件。

在本章中,我们将介绍以下主题:

您应该具备 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

输出显示replybytetimeTTL值,表示主机处于活动状态。考虑 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 连接的三个步骤如下:

  1. 客户端发送带有SYN标志的段;这意味着客户端请求服务器启动会话
  2. 服务器以应答的形式发送包含ACKSYN标志的段
  3. 客户端以确认标志进行响应

现在,让我们看一下 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 系统。有一些端口通常是打开的,例如137138139(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.510.0.0.810.0.0.1110.0.0.1210.0.0.1310.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 = stself.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 ......-en1thread = 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 中,端口扫描的意义在于检查主机上是否运行非法服务。

考虑用户通常使用他们的计算机下载游戏的情况,并且在安装游戏期间识别木马。特洛伊木马进入隐藏模式;打开一个端口;将所有击键(包括日志信息)发送给黑客。在这种情况下,端口扫描有助于识别受害者计算机上运行的未知服务。

端口号范围从065535。众所周知的端口(也称为系统端口)是那些范围从01023并为特权服务保留的端口。范围从102449151的端口是用于应用的注册端口类供应商;例如,3306端口是为 MySQL 保留的。

TCP 的三次握手作为端口扫描器的逻辑;在 TCP/IP 扫描程序中,您已经看到端口(137135是一个 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.110.0.0.16IP 地址是网关接口。我们在socket.setdefaulttimeout(1)中放置了 1 秒,这意味着扫描仪在每个端口上最多花费 1 秒。总共 4000 个端口意味着如果所有端口都关闭,则所需的总时间将为 4000 秒;如果我们把它转换成小时,它将变成 1.07 小时,这几乎等于我们程序的输出。如果我们设置socket.setdefaulttimeout(.5),所花费的时间将减少到 30 分钟,这仍然是一个很长的时间。没有人会使用我们的扫描仪。对于 4000 个端口,所用时间应少于 100 秒。

我已经说明了一些要点,对于一个好的端口扫描器,应该加以考虑:

  • 多线程应该用于高性能
  • socket.setdefaulttimeout(1)方法应根据情况设置
  • 端口扫描程序应该能够获取主机名和域名
  • 端口应提供带有端口号的服务名称
  • 端口扫描应考虑总时间
  • 要扫描端口065535,所需时间应为 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 秒。它还显示服务名称。让我们检查端口150000的更多输出:

 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 连接来检测这些类型的威胁。为了进行有效的端口扫描,多线程起着至关重要的作用,因为端口号从065536不等。要扫描一个巨大的列表,必须使用多线程。

在下一章中,您将看到嗅探及其两种类型:被动嗅探和主动嗅探。您还将学习如何捕获数据、数据包制作的概念以及使用 Scapy 库制作定制数据包。*

教程来源于Github,感谢apachecn大佬的无私奉献,致敬!

技术教程推荐

趣谈网络协议 -〔刘超〕

软件测试52讲 -〔茹炳晟〕

编译原理之美 -〔宫文学〕

Selenium自动化测试实战 -〔郭宏志〕

Linux内核技术实战课 -〔邵亚方〕

爱上跑步 -〔钱亮〕

说透数字化转型 -〔付晓岩〕

讲好故事 -〔涵柏〕

商业思维案例笔记 -〔曹雄峰〕