和URL扫描的QPS太高把业务告警打挂相匹配的情况:HOST主机漏洞扫描遇到碰瓷的端口服务。
扫描器经常会遇到内网中的某些端口开放的服务,不接收特定格式外的数据,一收到就会报错甚至挂掉,也不用写poc,浏览器贴上 http://ip: 端口直接导致服务报错。形如碰瓷一般,躺在内网中。
一般的说法是,这一块属于业务或者框架方负责,需要做数据校验,校验到不合规的数据,不进行处理抛弃。
但是在报错的处理上,有的业务可以选择直接忽视掉,比如flume某开源服务,发送如http的数据会直接报错,但报错是dba自己接收,可以选择不要,所以直接捕获不抛出就好了。但是作为服务框架,会据理力争报错不能直接丢弃,使用该服务框架的业务也可能传入不正确的数据,业务正常需求上需要报错。
所以在与某中台框架服务商讨后,采用了另一个方案:
框架上设置指纹功能,当发送特定数据的时候,返回特定指纹。扫描器需要做的:
1.采用nmap端口扫描的时候,建立起连接后发送的框架指纹检测数据包应该是最高优先级的,也就是最早发的。
2.端口扫描识别到框架后,该端口在正常流程中不进入扫描,不传递给插件,除非插件选择了二级指纹或其他的配资,需要对该框架进行检测。第二点比较好实现,下面探讨的是第一点的实现方式:采用nmap指纹或端口扫描插件中做处理
(半年一年前采用nmap指纹来做,最近在业务反馈后,花了一天的时间改了端口扫描的代码,更稳定可控。也不是说Nmap本身改探针不好,反复测是第一发包检测出业务的端口,就不会发其他探针了,但-sV偶尔抽一下,发了点别的包,业务拿着报错来找又没法复现,挺打脸的..)
Nmap服务探测 (探针优先级) https://www.cnblogs.com/rab3it/articles/12020756.html
0X01 nmap指纹编写
指纹探针的编写这里写的挺好挺全的,再抄一遍没什么意义
https://www.cnblogs.com/liun1994/p/6986544.html
顺序上,探针的发送顺序是
NULL -> 端口匹配上的探针 -> 端口不匹配的或者没有ports的探针
且nmap只发送 rarity 重要级别在设置以下的探针(级别越低越重要)
tips: ports不匹配也会进行扫描
所以要想探针在最开始的时候被发送,ports 匹配1-65535,否则还会被端口匹配的探针抢在前面。
这一段放在NULL前(其实NULL的下一个也一样,尽量靠前)
vi /usr/share/nmap/nmap-service-probes
##############################MYSELF my_server NEXT PROBE##############################
Probe TCP MYServer q|x12x11
|
rarity 1
ports 1-65535
match my_server m|x00x00i| p/my_server/
##############################NEXT PROBE##############################
缺点
a) 本身不稳定,可能收到预期外的数据包导致业务方程序报错
b) 每一个节点都需要保证nmap的nmap-service-probes是正确的
优点
a) 用nmap本身的指纹,高端优雅
0x02 python nmap插件中略过服务方指纹
只有加上-sV在,在扫描指纹的情况下,才会发送数据。
所以可以先扫描出存活的端口,筛选出非业务服务的端口,-sV扫描其他端口的指纹,不会对业务方的脆弱碰瓷端口发送预期外的数据。
缺点:比较麻烦
优点:稳定易控易修改
import nmap
import time
def check_server_port(target, port):
# 业务端口返回True
# 非业务端口正常返回False
return False
def scan_port(target):
portlist_info = dict()
start = time.time()
nmap_arguments = '-Pn -sT -max-scan-delay 5 -max-retries 2 -T4 -n --host-timeout 1200 --min-parallelism 100'
connect_argument = nmap_arguments + " -p 1-65535"
scanner = nmap.PortScanner()
scanner.scan(target, arguments=connect_argument)
if scanner[target].get('tcp'):
# 扫描存活
for port in scanner[target]['tcp']:
if scanner[target].state() == 'up' and scanner[target]['tcp'] and
scanner[target]['tcp'][int(port)]['state'] == 'open':
portlist_info[port] = ""
print(time.time() - start)
# SCF检测,不扫描指纹
service_port = list(portlist_info.keys())
for port in service_port:
if check_server_port(target, port):
portlist_info[port] = "my_server"
service_port.remove(port)
service_port_str = ",".join([str(port) for port in service_port])
# 扫描指纹
version_argument = nmap_arguments + " -p %s -sV" % service_port_str
scanner.scan(target, arguments=version_argument)
if scanner[target].get('tcp'):
for port in scanner[target]['tcp']:
if scanner[target].state() == 'up' and scanner[target]['tcp'] and
scanner[target]['tcp'][int(port)]['state'] == 'open':
product = scanner[target]['tcp'][int(port)]['product']
portlist_info[port] = product
end = time.time()
print(end-start)
return portlist_info
if __name__ == '__main__':
print(scan_port("127.0.0.1"))