在渗透测试的过程中,服务识别是一个很重要的环节,如果能识别出目标主机的服务、版本等信息,对于渗透测试将有重要帮助。对于入侵者来说,发现这些运行在目标上的服务,就可以利用这些软件上的漏洞入侵目标;对于网络安全的维护者来说,也可以提前发现系统的漏洞,从而预防这些入侵行为
很多的扫描工具都是采用了一种十分简单的方式,就是根据端口号来判断服务的类型,因为通常很多常见的服务都会运行在固定的端口上,如下表

端口号 说明 作用


21/22/69 FTP/TFTP 允许匿名上传、下载、破解和嗅探攻击
2049 NFS 服务 配置不当
139 Samba 服务 破解、未授权访问、远程代码执行
389 LDAP(目录访问协议) 注入、允许匿名访问、使用弱口令

文件共享服务端口

端口号 说明 作用


22 SSH 远程连接 破解、SSH 隧道以及内网代理转发,文件传输
23 Telnet 远程连接 破解、嗅探、弱口令
3389 Rdp 远程桌面连接 Shift 后门(需要 Windows Server 2003 以下的系统)、破解
5900 VNC 弱口令破解
5632 PyAnywhere 抓密码、代码执行

远程连接服务端口

端口号 说明 作用


80/443/8080 常见 Web 服务端口 Web 攻击、破解、服务器版本漏洞
7001/7002 WebLogic 控制台 Java 反序列化、弱口令
8080/8089 Jboss/Resin/Jetty/JenKins 反序列化、控制台弱口令
9090 WebSphere 控制台 Java 反序列化、弱口令
4848 GlassFish 控制台 弱口令
1352 Lotus Domino 邮件服务 弱口令、信息泄露、破解
10000 Webmin-Web 控制面板 弱口令

Web 应用服务端口

端口号 说明 作用


3306 MySOL 注入、提权、破解
1433 MSSQL 注入、提权、SA 弱口令、破解
1521 Oracle 数据库 TNS 破解、注入、反弹 shell
5432 PostgreSQL 数据库 破解、注入、弱口令
27017/27018 MongoDB 破解、未授权访问
6379 Redis 数据库 可尝试未授权访问、弱口令破解
5000 SysBase/DB2 破解、注入

数据库服务端口

端口号 说明 作用


25 SMTP 邮件服务 邮件伪造
110 POP3 协议 破解、嗅探
143 IMAP 协议 破解、收集目标内网信息

邮件服务端口

端口号 说明 作用


53 DNS 域名系统 允许区域传送、DNS 劫持、缓存投毒、欺骗
67/68 DHCP 服务 劫持、欺骗
161 SNMP 协议 破解、搜集目标内网信息

网络常见协议端口

端口号 说明 作用


2181 Zookeeper 服务 未授权访问
8096 Zabbix 服务 远程执行、SQL 注入
9200/9300 Elasticsearch 远程执行
11211 Memcache 未授权访问
512/513/514 Linux Rexec 服务 破解、Rlogin 登录
873 Rsync 服务 匿名访问、文件上传
3690 SVN 服务 SVN 泄露、未授权访问
50000 SAP Management Console 远程执行

特殊服务端口

而对端口的服务进行探测的方法就是向目标开放的端口发送探针数据包,根据目标主机返回的 banner 信息与已经存储总结的 banner 信息进行对比,进而确认服务类型。而强大的 Nmap 也是利用了这种方法,它有一个十分强大的 banner 库,这个库也还在不断的完善当中

根据这个特性与方法,就可以来编写扫描端口服务类型的程序了

先导入所需要用到的模块,time 用于模块主要用于产生延迟时间,optparse 模块用于生成命令行参数,socket 模块用于产生 TCP 请求,re 模块为正则表达式模块,与指纹信息进行有效匹配,进而确定服务类型。SIGNS 为指纹库用于对目标主机返回的 banner 信息进行匹配,读者可自行添加扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from optparse import OptionParser
import time
import socket
import re

SIGNS = (
#协议|版本|关键字
b'FTP|FTP|^220.*FTP',
b'MySQL|MySQL|mysql_native_password',
b'oracle-https|^220- ora',
b'Telnet|Telnet|Telnet',
b'Telnet|Telnet|^\r\n%connection closed by remote host!\x00$',
b'VNC|VNC|^RFB',
b'IMAP|IMAP|^\* OK.*?IMAP',
b'POP|POP|^\+OK.*?',
b'SMTP|SMTP|^220.*?SMTP',
b'Kangle|Kangle|HTTP.*kangle',
b'SMTP|SMTP|^554 SMTP',
b'SSH|SSH|^SSH-',
b'HTTPS|HTTPS|Location:https',
b'HTTP|HTTP|HTTP/1.1',
b'HTTP|HTTP|HTTP/1.0',
)

接着利用 optparse 模块生成命令行参数化形式,对用户输入的参数进行接收和批量的处理,最后将处理后的 IP 地址及端口 port 传入 request () 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def main():
parser = OptionParser("Usage:%prog -i <target host>")#输出帮助信息
parser.add_option('-i',type='string',dest='IP',help='specify target host')
#获取IP地址参数
parser.add_option('-p',type='string',dest='PORT',help='specify target host')
#获取端口参数
options,args = parser.parse_args()
ip = options.IP
port = options.PORT
print("Scan report for "+ip+"\n")
for line in port.split(','):
request(ip,line)
time.sleep(0.2)
print("\nScan finished!...\n")

if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("interrupted by user,killing all threads...")

在 request () 函数中,首先调用 sock.connect () 函数探测目标主机的端口是否开放,如果端口开放,则利用 sock.sendall () 函数将 PROBE 探针发送给目标端口。sock.recv () 函数用于接收返回的指纹信息,并将指纹信息及端口发送到 regex () 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def request(ip,port):
response = ''
PROBE = 'GET / HTTP/1.0\r\n\r\n'
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.settimeout(10)
result = sock.connect_ex((ip,int(port)))
if result == 0:
try:
sock.sendall(PROBE.encode())
response = sock.recv(256)
if response:
regex(response,port)
except(ConnectionResetError,socket.timeout):
pass
else:
pass

利用 re.search () 函数将返回的 banner 信息与 SIGNS 包含的指纹信息进行正则匹配,并将匹配到的结果输出。如果没有在 SIGNS 中找到想匹配的信息,则输出 Unrecognized

1
2
3
4
5
6
7
8
9
10
11
12
def regex(response,port):
text = ""
if re.search(b'<title>502 Bad Geteway',response):
proto = {"Service failed to access!!"}
for pattern in SIGNS:
pattern = pattern.split(b'|')
if re.search(pattern[-1],response,re.IGNORECASE):
proto = "["+port+"]"+"open"+pattern[1].decode()
break
else:
proto = "["+port+"]"+"open"+"Unrecognized"
print(proto)

市面上的软件种类繁多,版本也不一样,因此端口服务版本要想实现比较困难,