使用Nmap模块进行网络端口扫描
1、Nmap模块简介
python-nmap是一个帮助使用nmap端口扫描器的python库。它允许轻松操纵nmap进行扫描,并且在扫描后生成对应的结果报告。
Nmap模块安装
pip install python-namp
常见的类与方法
在Nmap模块中,最常使用的就是PortScanner扫描类,这个类实现了Nmap工具功能上的封装。
PortScanner类常用方法:
all_hosts():返回扫描后所有主机的列表
analyse_nmap_xml_scan(nmap_xml_output=None, nmap_err='', nmap_err_keep_trace='', nmap_warn_keep_trace=''):分析NMAP xml扫描输出,其中param nmap_xml_output参数是要分析的xml字符串
command_line():返回用于扫描的命令行,命令行返回后可以通过使用nmap工具的方式进行扫描
csv():将CSV输出作为文本返回
get_nmap_last_output():以原始文本返回nmap的最后文本输出
has_host(host):如果主机有结果则返回True,否则返回False
listscan(hosts='127.0.0.1'):不进行扫描,但解释目标主机并返回一个主机列表
nmap_version():如果检测到返回nmap版本
scaninfo():返回扫描过程中的扫描参数{'tcp': {'services': '22', 'method': 'connect'}}
scanstats():返回扫描过程中的历时参数{'uphosts': '3', 'timestr': 'Thu Jun 3 21:45:07 2010', 'downhosts': '253', 'totalhosts': '256', 'elapsed': '5.79'}
scan(hosts='127.0.0.1', ports=None, arguments='-sV', sudo=False):扫描指定的主机,如果nmap输出不是xml,可能会引发PortScannerError异常。
scan方法参数详解:
hosts:需要扫描的IP或IP段
ports:扫描的端口
sudo:是否使用sudo启动nmap
arguments:
`-O` 系统扫描
`-V,-v,-D,-d,-p` debug信息
`–fuzzy` 推测操作系统检测结果
`-sT` TCP端口扫描(完整三次握手)
`-sU` UDP端口扫描(不回应可能打开,回应则关闭)
`-sL` DNS反向解析
`-sS` 隐藏扫描(半开SYN)
`-sP` 发现存活主机(直连arp,非直连TCP80,ICMP)
`-sO` 确定主机协议扫描
`-sW` 对滑动窗口的扫描
`-sA` TCP ACK扫描
`-sN` 关闭主机扫描(不管是否存活直接扫描)
`-sF` fin扫描
`-sX` Xmas扫描(fin psh urg为置位)
`-sI` 完全隐藏(以一个跳板为主机(无流量)扫描另一台主机)
`-sV` 服务器版本
`-sC` 跟安全有关的脚本
`-PN` 扫描自己
来自Nmap官方公布的示例代码
>>> import nmap
>>> nm = nmap.PortScanner()
>>> nm.scan('127.0.0.1', '22-443')
>>> nm.command_line()
'nmap -oX - -p 22-443 -sV 127.0.0.1'
>>> nm.scaninfo()
{'tcp': {'services': '22-443', 'method': 'connect'}}
>>> nm.all_hosts()
['127.0.0.1']
>>> nm['127.0.0.1'].hostname()
'localhost'
>>> nm['127.0.0.1'].state()
'up'
>>> nm['127.0.0.1'].all_protocols()
['tcp']
>>> nm['127.0.0.1']['tcp'].keys()
[80, 25, 443, 22, 111]
>>> nm['127.0.0.1'].has_tcp(22)
True
>>> nm['127.0.0.1'].has_tcp(23)
False
>>> nm['127.0.0.1']['tcp'][22]
{'state': 'open', 'reason': 'syn-ack', 'name': 'ssh'}
>>> nm['127.0.0.1'].tcp(22)
{'state': 'open', 'reason': 'syn-ack', 'name': 'ssh'}
>>> nm['127.0.0.1']['tcp'][22]['state']
'open'
>>> for host in nm.all_hosts():
>>> print('----------------------------------------------------')
>>> print('Host : %s (%s)' % (host, nm[host].hostname()))
>>> print('State : %s' % nm[host].state())
>>> for proto in nm[host].all_protocols():
>>> print('----------')
>>> print('Protocol : %s' % proto)
>>>
>>> lport = nm[host][proto].keys()
>>> lport.sort()
>>> for port in lport:
>>> print ('port : %s\tstate : %s' % (port, nm[host][proto][port]['state']))
----------------------------------------------------
Host : 127.0.0.1 (localhost)
State : up
----------
Protocol : tcp
port : 22 state : open
port : 25 state : open
port : 80 state : open
port : 111 state : open
port : 443 state : open
导出扫描结果信息
>>> print(nm.csv())
host;protocol;port;name;state;product;extrainfo;reason;version;conf
127.0.0.1;tcp;22;ssh;open;OpenSSH;protocol 2.0;syn-ack;5.9p1 Debian 5ubuntu1;10
127.0.0.1;tcp;25;smtp;open;Exim smtpd;;syn-ack;4.76;10
127.0.0.1;tcp;53;domain;open;dnsmasq;;syn-ack;2.59;10
127.0.0.1;tcp;80;http;open;Apache httpd;(Ubuntu);syn-ack;2.2.22;10
127.0.0.1;tcp;111;rpcbind;open;;;syn-ack;;10
127.0.0.1;tcp;139;netbios-ssn;open;Samba smbd;workgroup: WORKGROUP;syn-ack;3.X;10
127.0.0.1;tcp;443;;open;;;syn-ack;;
检测网络状态
>>> nm.scan(hosts='192.168.1.0/24', arguments='-n -sP -PE -PA21,23,80,3389')
>>> hosts_list = [(x, nm[x]['status']['state']) for x in nm.all_hosts()]
>>> for host, status in hosts_list:
>>> print('{0}:{1}'.host)
192.168.1.0:down
192.168.1.1:up
192.168.1.10:down
192.168.1.100:down
192.168.1.101:down
192.168.1.102:down
192.168.1.103:down
192.168.1.104:down
192.168.1.105:down
[...]
2、使用Nmap开发简型的端口扫描脚本
### 导入Nmap模块
import nmap
### 设计IPV4的正则格式模块和端口的正则格式模块,用于在后续中提取IP地址和端口号
ip_add_pattern = re.compile("^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$")
port_range_pattern = re.compile("([0-9]+)-([0-9]+)")
### 初始化的端口扫描范围
port_min = 0
port_max = 65535
### 维护一个端口列表,用于存放存活的端口号
open_ports = []
while True:
### 要求输入一个ip地址
ip_add_entered = input("\nPlease enter the ip address that you want to scan: ")
### 判断Ip地址是否符合要求
if ip_add_pattern.search(ip_add_entered):
print(f"{ip_add_entered} is a valid ip address")
break
while True:
### 输入端口号范围
print("Please enter the range of ports you want to scan in format: <int>-<int> (ex would be 60-120)")
port_range = input("Enter port range: ")
port_range_valid = port_range_pattern.search(port_range.replace(" ",""))
### 验证端口号是否有效,并且分别提取最低范围端口号和最高范围端口号
if port_range_valid:
port_min = int(port_range_valid.group(1))
port_max = int(port_range_valid.group(2))
break
### 创建PortScanner扫描类
nm = nmap.PortScanner()
for port in range(port_min, port_max + 1):
try:
### 进行目标ip和端口的扫描
result = nm.scan(ip_add_entered, str(port))
### 端口状态
port_status = (result['scan'][ip_add_entered]['tcp'][port]['state'])
### 打印存活的端口和端口的状态
print(f"Port {port} is {port_status}")
### 将端口号加入到维护的存活列表中
open_ports.append(port)
### 若无法扫描,则会抛出异常
except:
print(f"Cannot scan port {port}.")