1-测试 - 服务识别
about
在渗透测试过程中,服务识别是一个很重要的环节,如果能识别出目标主机的服务、版本等信息,对于后续的渗透测试有重要的帮助。
对于入侵者来说,发现这些运行在目标上的服务,就可以利用这些软件上的漏洞入侵目标,对于网络安全的维护者来说,也可以提前发现系统的漏洞,从而预防这些入侵行为。
很多扫描工具都采用了一种十分简单的方式,就是根据端口来判断服务类型,因为通常常见的服务都会运行在固定的端口上,如下面的表中,列举了常见的服务和所占用的端口。
文件共享服务端口:
端口号 | 说明 | 作用 |
---|---|---|
21/22/69 | FTP/TFTP | 允许匿名上传、下载、破解和嗅探攻击 |
2049 | NFS服务 | 配置不当 |
139 | Samba服务 | 破解、未授权访问、远程代码执行 |
389 | LDAP(目录访问协议) | 注入、允许匿名访问、使用弱口令 |
远程连接服务端口:
端口号 | 说明 | 作用 |
---|---|---|
22 | SSH远程连接 | 破解、SSH隧道及内网代理转发、文件传输 |
23 | Telnet远程连接 | 破解、嗅探、弱口令 |
3389 | Rdp远程桌面连接 | Shift后门(需要Windows service 2003以下的系统)、破解 |
5900 | VNC | 弱口令破解 |
5632 | PyAnywhere | 抓密码、代码执行 |
Web应用服务端口:
端口号 | 说明 | 作用 |
---|---|---|
80/443/8080 | 常见Web服务端口 | Web攻击、破解、服务器版本漏洞 |
7001/7002 | WebLogic控制台 | Java反序列化、弱口令 |
8080/8089 | Jboss/Resin/Jetty/Jenkins | 反序列化、控制台弱口令 |
9090 | WebSphere控制台 | Java反序列化、弱口令 |
4848 | ClassFish控制台 | 弱口令 |
1352 | Lotus Domino邮件服务 | 弱口令、信息泄露、破解 |
10000 | Webmin-Web控制面板 | 弱口令 |
数据库服务端口:
端口号 | 说明 | 作用 |
---|---|---|
3306 | MySQL | 注入、提权、破解 |
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服务 | 未授权访问 |
8069 | 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库,而且这个库仍然在不断的完善中。
接下来,就按照上面介绍的思路来编写对目标服务进行扫描的程序。
手动实现
centos7.3 + python3.6.8
思路:
- 使用socket确认指定的
host:port
是否开放 - 如果是开放端口,则发送探测信息,获取返回的banner信息
- 对返回的banner信息去指纹库进行匹配,识别服务类型
具体代码如下:
#!/bin/bash
# -*- coding: utf-8 -*-
# @Time : 2020/12/9 9:56
# @Author : 张开
# File : service_idt.py
import re
import socket
from argparse import ArgumentParser
from os import cpu_count
from concurrent.futures import ThreadPoolExecutor
# banner指纹库,用于匹配开放端口的服务类型,可以自行扩展,该库越大匹配精度越高
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',
)
def regex(response, port, host):
"""
利用re.search将返回的banner信息与SIGNS包含的指纹信息进行正则匹配
如果匹配到了,就输出该端口和识别结果,如 [3306] open MySQL
如果没有匹配到,说明SIGNS指纹库没有记录该服务,输出端口和Unrecognized,表示没有识别出来服务类型
"""
# 首先判断判断探测是否被拒绝
if re.search(b'<title>502 Bad Gateway', response):
console = "Service failed to access!!!"
# 循环指纹库进行匹配,匹配到就打印匹配结果,并不在往下进行匹配
for pattern in SIGNS:
pattern = pattern.split(b'|')
if re.search(pattern[-1], response, re.IGNORECASE):
console = "[{}:{}] open {}".format(host, port, pattern[1].decode())
break
# 如果循环完整个指纹库,也没有匹配上,说明该开放端口没有识别出来服务类型
else:
console = "[{}:{}] open {}".format(host, port, 'Unrecognized')
print(console)
def send_msg(host, port):
"""
首先调用socket.connect_ex探测目标主机端口是否开放,如果端口开放,则利用sock.sendall将PROBE探针发送给目标端口
并用sock.recv接收返回的指纹信息
再将指纹信息交给regex进行指纹匹配识别服务类型
"""
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((host, port))
if result == 0:
try:
sock.sendall(PROBE.encode())
response = sock.recv(512)
if response:
regex(response, port, host)
except (ConnectionResetError, socket.timeout) as e:
print('[{}:{}] detect error!! {}'.format(host, port, e))
sock.close()
def main(args):
"""
从args中读取port端口范围,判断是单个端口还是范围端口,然后将端口存储到临时的列表中
使用线程池来处理大量的端口
"""
port_list = []
print('host:{} port: {}'.format(args.host, args.port))
if '-' in args.port:
startPort, endPort = args.port.split('-')
for i in range(int(startPort), int(endPort) + 1):
port_list.append(int(i))
else:
# 如果是单个端口,没有必要使用线程池
send_msg(args.host, int(args.port))
return
# 为了性能考虑,如果端口数量小于cpu_count() * 5, 线程池中线程的数量等于端口的数量
if len(port_list) <= args.threadNum:
threadNum = len(port_list)
else:
# 否则线程池中线程的数量等于cpu_count() * 5
threadNum = args.threadNum
t = ThreadPoolExecutor(threadNum)
for i in port_list:
t.submit(send_msg, args.host, i)
if __name__ == '__main__':
try:
parse = ArgumentParser()
parse.add_argument('--host', dest='host', default='127.0.0.1', type=str, help='获取host')
parse.add_argument('--port', dest='port', default="900-1000", type=str, help='获取port')
parse.add_argument('--threadNum', dest='threadNum', default=cpu_count() * 5, type=str, help='获取port')
args = parse.parse_args()
main(args)
except Exception as e:
print(e)
"""
[root@cs tmp]# python3 service_idt.py --port 1-20000
host:127.0.0.1 port: 1-20000
[127.0.0.1:21] open FTP
[127.0.0.1:25] open SMTP
[127.0.0.1:22] open SSH
[127.0.0.1:3306] open MySQL
[127.0.0.1:5672] open Unrecognized
[127.0.0.1:5676] open Unrecognized
[127.0.0.1:5674] detect error!! [Errno 104] Connection reset by peer
[127.0.0.1:5675] detect error!! [Errno 104] Connection reset by peer
[127.0.0.1:5673] open Unrecognized
[127.0.0.1:6379] open Unrecognized
[127.0.0.1:15672] open HTTP
[127.0.0.1:15675] detect error!! [Errno 104] Connection reset by peer
[127.0.0.1:15674] detect error!! [Errno 104] Connection reset by peer
[127.0.0.1:15676] open HTTP
[127.0.0.1:15673] open HTTP
"""
PS:windows环境代码运行很慢,Linux系统就很快了.....
使用nmap模块实现
centos7.3 + python3.6.8
nmap模块的安装及使用:https://www.cnblogs.com/Neeo/articles/14105947.html
import nmap3
nmap = nmap3.Nmap()
nmap.scan_top_ports("127.0.0.1")
"""
{
'127.0.0.1': [{
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '21',
'state': 'open',
'reason': 'syn-ack',
'reason_ttl': '64',
'service': {
'name': 'ftp',
'method': 'table',
'conf': '3'
}
}, {
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '22',
'state': 'open',
'reason': 'syn-ack',
'reason_ttl': '64',
'service': {
'name': 'ssh',
'method': 'table',
'conf': '3'
}
}, {
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '23',
'state': 'closed',
'reason': 'reset',
'reason_ttl': '64',
'service': {
'name': 'telnet',
'method': 'table',
'conf': '3'
}
}, {
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '25',
'state': 'open',
'reason': 'syn-ack',
'reason_ttl': '64',
'service': {
'name': 'smtp',
'method': 'table',
'conf': '3'
}
}, {
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '80',
'state': 'closed',
'reason': 'reset',
'reason_ttl': '64',
'service': {
'name': 'http',
'method': 'table',
'conf': '3'
}
}, {
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '110',
'state': 'closed',
'reason': 'reset',
'reason_ttl': '64',
'service': {
'name': 'pop3',
'method': 'table',
'conf': '3'
}
}, {
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '139',
'state': 'closed',
'reason': 'reset',
'reason_ttl': '64',
'service': {
'name': 'netbios-ssn',
'method': 'table',
'conf': '3'
}
}, {
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '443',
'state': 'closed',
'reason': 'reset',
'reason_ttl': '64',
'service': {
'name': 'https',
'method': 'table',
'conf': '3'
}
}, {
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '445',
'state': 'closed',
'reason': 'reset',
'reason_ttl': '64',
'service': {
'name': 'microsoft-ds',
'method': 'table',
'conf': '3'
}
}, {
'host': '127.0.0.1',
'protocol': 'tcp',
'portid': '3389',
'state': 'closed',
'reason': 'reset',
'reason_ttl': '64',
'service': {
'name': 'ms-wbt-server',
'method': 'table',
'conf': '3'
}
}],
'runtime': {
'time': '1607490186',
'timestr': 'Wed Dec 9 13:03:06 2020',
'summary': 'Nmap done at Wed Dec 9 13:03:06 2020; 1 IP address (1 host up) scanned in 0.07 seconds',
'elapsed': '0.07',
'exit': 'success'
},
'stats': {
'scanner': 'nmap',
'args': '/usr/bin/nmap -oX - --top-ports 10 127.0.0.1',
'start': '1607490186',
'startstr': 'Wed Dec 9 13:03:06 2020',
'version': '7.91',
'xmloutputversion': '1.05'
}
}
"""