Skip to content

about

在渗透测试过程中,服务识别是一个很重要的环节,如果能识别出目标主机的服务、版本等信息,对于后续的渗透测试有重要的帮助。 对于入侵者来说,发现这些运行在目标上的服务,就可以利用这些软件上的漏洞入侵目标,对于网络安全的维护者来说,也可以提前发现系统的漏洞,从而预防这些入侵行为。

很多扫描工具都采用了一种十分简单的方式,就是根据端口来判断服务类型,因为通常常见的服务都会运行在固定的端口上,如下面的表中,列举了常见的服务和所占用的端口。

文件共享服务端口:

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

远程连接服务端口:

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

Web应用服务端口:

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

数据库服务端口:

端口号说明作用
3306MySQL注入、提权、破解
1433MSSQL注入、提权、SA弱口令、破解
1521OracleTNS破解、注入、反弹shell
5432PostgreSQL破解、注入、弱口令
27017/27018MongoDB破解、未授权访问
6379Redis可尝试未授权访问、弱口令破解
5000SysBase/DB2破解、注入

邮件服务端口:

端口号说明作用
25SMTP邮件服务邮件伪造
110POP3协议破解、嗅探
143IMAP协议破解

网络常见协议端口:

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

特殊服务端口:

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

了解了常见的服务及所占端口号,就可以向目标开放的端口发送探针数据包,根据目标主机返回的banner信息与存储总结的banner信息进行对比,进而确定运行的服务类型。 著名的nmap扫描工具就是采用这种方法,它包含一个十分强大的banner库,而且这个库仍然在不断的完善中。 接下来,就按照上面介绍的思路来编写对目标服务进行扫描的程序。

手动实现

centos7.3 + python3.6.8

思路:

  1. 使用socket确认指定的host:port是否开放
  2. 如果是开放端口,则发送探测信息,获取返回的banner信息
  3. 对返回的banner信息去指纹库进行匹配,识别服务类型

具体代码如下:

python
#!/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

python
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'
	}
}
"""