Python Penetration Testing 简明教程

Python Network Scanner

端口扫描可以定义为一种监控技术,用于查找特定主机上可用的开放端口。网络管理员、渗透测试人员或黑客都可以使用此技术。我们可以根据我们的要求配置端口扫描器,以从目标系统获取最大的信息。

现在,考虑在运行端口扫描后我们可以获得的信息 −

  1. Information about open ports.

  2. 有关在每个端口上运行的服务的信息。

  3. 目标主机的操作系统和 MAC 地址信息。

端口扫描就像一个窃贼,他想要进入一栋房子,通过检查每一扇门和窗户来查看哪扇是开着的。如前所述,用于互联网通信的 TCP/IP 协议套件由两个协议组成,即 TCP 和 UDP。这两个协议均有 0 至 65535 个端口。我们总是建议关闭系统中不必要的端口,因此基本上有超过 65000 个门(端口)需要锁定。这 65535 个端口可以分为以下三个范围:

  1. 系统或众所周知的端口:从 0 到 1023

  2. 用户或注册端口:从 1024 到 49151

  3. 动态或专用端口:全部 > 49151

Port Scanner using Socket

在我们的上一章节中,我们讨论了套接字是什么。现在,我们将使用套接字构建一个简单的端口扫描程序。以下是使用套接字的端口扫描程序的 Python 脚本:

from socket import *
import time
startTime = time.time()

if __name__ == '__main__':
   target = input('Enter the host to be scanned: ')
   t_IP = gethostbyname(target)
   print ('Starting scan on host: ', t_IP)

   for i in range(50, 500):
      s = socket(AF_INET, SOCK_STREAM)

      conn = s.connect_ex((t_IP, i))
      if(conn == 0) :
         print ('Port %d: OPEN' % (i,))
      s.close()
print('Time taken:', time.time() - startTime)

当我们运行上述脚本时,它会提示输入主机名,你可以提供任何主机名,例如任何网站的名称,但请小心,因为端口扫描可能被视为犯罪。在未经服务器或你所针对的计算机的所有者的明确书面许可的情况下,我们绝不应针对任何网站或 IP 地址执行端口扫描。端口扫描类似于去某人的房子检查他们的门窗。这就是建议在本地主机或你自己的网站(如果有的话)上使用端口扫描程序的原因。

Output

上述脚本会生成以下输出来:

Enter the host to be scanned: localhost
Starting scan on host: 127.0.0.1
Port 135: OPEN
Port 445: OPEN
Time taken: 452.3990001678467

输出显示在 50 到 500 的范围内(如脚本中所提供),此端口扫描程序发现了两个端口——端口 135 和 445 开启。我们可以更改此范围并可以检查其他端口。

Port Scanner using ICMP (Live hosts in a network)

ICMP 不是端口扫描,但它用于 ping 远程主机以检查主机是否启动。当我们必须检查网络中的许多活动主机时,此扫描非常有用。它涉及向主机发送 ICMP ECHO 请求,如果该主机处于活动状态,它会返回 ICMP ECHO 响应。

port scanner using icmp

发送 ICMP 请求的上述过程也称为 ping 扫描,由操作系统的 ping 命令提供。

Concept of Ping Sweep

实际上,某种意义上讲,ping 扫描也被称为 ping 扫描。唯一区别在于,ping 扫描是查找某个网络范围内的多台机器可用性的过程。例如,假设我们要测试 IP 地址的完整列表,那么使用 ping 扫描(即操作系统的 ping 命令)将需要花费很长时间来逐个扫描 IP 地址。这就是我们需要使用 ping 扫描脚本的原因。以下是使用 ping 扫描查找活动主机的 Python 脚本:

import os
import platform

from datetime import datetime
net = input("Enter the Network Address: ")
net1= net.split('.')
a = '.'

net2 = net1[0] + a + net1[1] + a + net1[2] + a
st1 = int(input("Enter the Starting Number: "))
en1 = int(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 range(st1,en1):
   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")

t2 = datetime.now()
total = t2 - t1
print ("Scanning completed in: ",total)

上述脚本分为三部分。它首先通过将 IP 地址范围分成几部分来选择要 ping 扫描的范围。接下来,它使用该函数,该函数将根据操作系统选择 ping 扫描命令,最后它给出有关主机和完成扫描过程所需时间的响应。

Output

上述脚本会生成以下输出来:

Enter the Network Address: 127.0.0.1
Enter the Starting Number: 1
Enter the Last Number: 100

Scanning in Progress:
Scanning completed in: 0:00:02.711155

上述输出显示没有活动端口,因为防火墙已启动,并且 ICMP 入站设置也已禁用。在更改这些设置后,我们可以从输出中提供的 1 到 100 的范围内获取活动端口的列表。

Port Scanner using TCP scan

要建立 TCP 连接,主机必须执行三向握手。按照以下步骤执行此操作:

Step 1 − Packet with SYN flag set

在此步骤中,尝试发起连接的系统从一个已设置 SYN 标志的数据包开始。

Step 2 − Packet with SYN-ACK flag set

在此步骤中,目标系统返回一个已设置 SYN 和 ACK 标志的数据包。

Step 3 − Packet with ACK flag set

最后,发起系统的将一个已设置 ACK 标志的数据包返回给原始目标系统。

然而,这里出现的问题是,如果我们可以使用 ICMP 回显请求和响应方法(ping 扫描扫描程序)进行端口扫描,那么我们为什么还需要 TCP 扫描?这背后的主要原因是,假设我们将 ICMP ECHO 响应功能关闭,或者使用防火墙对 ICMP 数据包进行关闭,那么 ping 扫描扫描程序将无法工作,我们需要 TCP 扫描。

import socket
from datetime import datetime
net = input("Enter the IP address: ")
net1 = net.split('.')
a = '.'

net2 = net1[0] + a + net1[1] + a + net1[2] + a
st1 = int(input("Enter the Starting Number: "))
en1 = int(input("Enter the Last Number: "))
en1 = en1 + 1
t1 = datetime.now()

def scan(addr):
   s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
   socket.setdefaulttimeout(1)
   result = s.connect_ex((addr,135))
   if result == 0:
      return 1
   else :
      return 0

def run1():
   for ip in range(st1,en1):
      addr = net2 + str(ip)
      if (scan(addr)):
         print (addr , "is live")

run1()
t2 = datetime.now()
total = t2 - t1
print ("Scanning completed in: " , total)

上面的脚本分三个部分工作。它通过将 IP 地址的范围拆分为多个部分,来让其进行 ping 扫描。然后使用一个扫描地址的函数,该函数进一步使用 socket。随后,它给出了有关主机和完成扫描过程花费时间的信息。result = s. connect_exaddr,135 语句返回了一个错误指示符。如果操作成功,则错误指示符为 0,否则它就是 errno 变量的值。这里,我们使用了端口 135;此扫描仪适用于 Windows 系统。另一个将在这里发挥作用的端口是 445(Microsoft-DSActive Directory),并且通常是打开的。

Output

上述脚本会生成以下输出来:

Enter the IP address: 127.0.0.1
Enter the Starting Number: 1
Enter the Last Number: 10

127.0.0.1 is live
127.0.0.2 is live
127.0.0.3 is live
127.0.0.4 is live
127.0.0.5 is live
127.0.0.6 is live
127.0.0.7 is live
127.0.0.8 is live
127.0.0.9 is live
127.0.0.10 is live
Scanning completed in: 0:00:00.230025

Threaded Port Scanner for increasing efficiency

正如我们在以上案例中看到的,端口扫描可是非常慢的。例如,在扫描 50 到 500 的端口时,使用 socket 端口扫描仪花费的时间是 452.3990001678467。为了提高速度,我们可以使用线程。以下是用 threading 进行端口扫描的一个示例:

import socket
import time
import threading

from queue import Queue
socket.setdefaulttimeout(0.25)
print_lock = threading.Lock()

target = input('Enter the host to be scanned: ')
t_IP = socket.gethostbyname(target)
print ('Starting scan on host: ', t_IP)

def portscan(port):
   s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   try:
      con = s.connect((t_IP, port))
      with print_lock:
         print(port, 'is open')
      con.close()
   except:
      pass

def threader():
   while True:
      worker = q.get()
      portscan(worker)
      q.task_done()

q = Queue()
   startTime = time.time()

for x in range(100):
   t = threading.Thread(target = threader)
   t.daemon = True
   t.start()

for worker in range(1, 500):
   q.put(worker)

q.join()
print('Time taken:', time.time() - startTime)

在上方的脚本中,我们需要导入 threading 模块,该模块内置于 Python 包中。我们使用线程锁定概念 thread_lock = threading.Lock() ,以避免一次有多个修改。基本上,threading.Lock() 将允许一次仅一个线程访问变量。因此,不会发生双重修改。

随后,我们定义一个 threader() 函数,该函数将从 worker for 循环中获取工作(端口)。然后调用 portscan() 方法以连接到端口并打印结果。端口号作为参数传递。一旦任务完成,就会调用 q.task_done() 方法。

现在,在运行了以上脚本后,我们可以看到扫描 50 到 500 个端口的速度差异。它仅花费了 1.3589999675750732 秒,与 socket 端口扫描仪用于扫描本地主机同等数量的端口所花费的 452.3990001678467 秒相比,要少很多。

Output

上述脚本会生成以下输出来:

Enter the host to be scanned: localhost
Starting scan on host: 127.0.0.1
135 is open
445 is open
Time taken: 1.3589999675750732