一, 背景
关于端口识别与服务监控问题, 早有大牛提供过 masscan 扫端口 + nmap 扫服务思路. 但在亲自实践过程中, 发现最致命的问题是 nmap 扫描速度太慢, 导致无法接受这个扫描时长, 尤其是存在大量 ip 的情况下. 于是考虑 nmap 参数调优, 多进程扫描等方式, 但效果甚微, 最后与几位未曾相识的大牛们在群里讨论过之后, 最终得到一个比较满意的结果, 下面就详细得分享下这个项目.
二, 思路
总体思路依然 masscan 扫端口 + nmap 扫服务, 众所周知, masscan 扫端口的速度是相当得快, 所以关于端口扫描速度这块不需要考虑, 只需对输出结果进行整理即可. 最主要的是要考虑如何在有限得资源下来提升 nmap 扫服务的速度, 整个项目实现过程磕磕绊绊, 最终总结出以下三个重难点:
进行 nmap 参数调优, 减少不必要的等待时长;
在存在大量 ip 与开放端口的情况下, 尽可能减少 nmap 初始化次数, 以便能有效得节约系统资源;
实现 nmap 并发扫描, 提升扫描效率.
其中重中之重是第 3 点, 一开始是考虑通过 for 循环来实现多进程后台并发执行, 但结果是扫描速度提升效果不显著, 而且系统性能也被消耗得厉害. 后来与借鉴了几位大佬的意见之后, 开始考虑用队列来实现并发.
三, 具体实现
啰嗦了这么多, 下面就来看一下具体实现, 本人非程序猿, 撸个脚本都感觉是十分崩溃的, 真是羡慕各位又会写程序又懂安全人又风趣的 gg 们.
1.masscan 开放端口扫描
主要分为两个模块: 开放端口扫描 + 扫描结果处理
a. 开放端口扫描
masscan -p 1-65535 --rate 100000 --open-only -iL /data/portscan/iplist/allip.txt --excludefile /data/portscan/iplist/ip.exclude
这里主要提下 - rate 参数,-rate 参数是指发包速率, 需要根据你的实际带宽来配置, 高了容易误报, 低了影响扫描速率.
b. 扫描结果处理
扫描结果处理是为下一步使用 nmap 进行服务探测做准备的. 服务探测有两种方式可选择,"单 ip 单端口并发扫描" 或者 "单 ip 多端口并发扫描". 上一小节思路重难点的第 2 点已经提过,"在存在大量 ip 与开放端口的情况下, 尽可能减少 nmap 初始化次数, 以便能有效得节约系统资源", 所以最终选择了 "单 ip 多端口并发扫描" 方式. 相应得, masscan 端口扫描结果也要按此方式进行处理.
下面上代码, 一系列 sed/awk......
- # 扫描完后只保留存活 ip 与端口信息, 并写在 port.list 文件中
- masscan -p 1-65535 --rate 100000 --open-only -iL /data/portscan/iplist/allip.txt --excludefile /data/portscan/iplist/ip.exclude | sed 's/\/tcp//g' |awk -F "" {'print $6,$4'}>/data/portscan/portresult/port.list
- # 对 port.list 中结果去重后保存在 port.list.tmp 文件中
- sort /data/portscan/portresult/port.list | uniq> /data/portscan/portresult/port.list.tmp
- # 将 port.list.tmp 文件中结果按 "ip 端口 1, 端口 2, 端口 3,......" 方式处理后保存在 port.list.nmap 中
- awk '{a[$1]=$2","a[$1]}END{for(i in a){print i,a[i]}}' /data/portscan/portresult/port.list.tmp | sed "s/,$//g"> /data/portscan/portresult/port.list.nmap
2. 服务版本探测
这里主要考虑两个方面, nmap 参数调优和队列控制并发扫描.
a.nmap 参数调优
nmap -T4 -Pn -sV -n -p $port $ip
-T4: 将扫描延迟降低到 10ms;
-sV: 对服务的版本信息进行探测, 加上这个参数后更耗时, 但是探测结果可以细致到版本号, 主要还是看各位需求.
b. 队列控制并发扫描
队列控制可以使系统达到较好的利用率, 线程数可以自己根据需要进行调整, 我目前使用一台 8 核 24G 的虚拟机跑 1w + 个 ip,40w + 个端口, 12 个小时内完成所有的端口与应用识别. 上代码:
- # 创建有名管道
- [ -e /tmp/fd1 ] || mkfifo /tmp/fd1
- # 创建文件描述符, 以可读 (<) 可写 (>) 的方式关联管道文件, 这时候文件描述符 3 就有了有名管道文件的所有特性
- exec 3<>/tmp/fd1
- # 关联后的文件描述符拥有管道文件的所有特性, 所以这时候管道文件可以删除, 我们留下文件描述符来用就可以了
- rm -rf /tmp/fd1
- # 并发执行的进程数量
- thread=30
- for ((i=1;i<=$thread;i++))
- do
- #&3 代表引用文件描述符 3, 这条命令代表往管道里面放入了一个 "令牌"
- echo>&3
- done
- # 总的 ip 数目
- ipnum=`wc -l /data/portscan/iplist/port.list.nmap | awk {'print $1'}`
- for ((i=1;i<=$ipnum;i++))
- do
- # 代表从管道中读取一个令牌
- read -u3
- {
- # 提取 ip 地址和端口号
- ip=`sed -n ""$i"p"/data/portscan/portresult/port.list.nmap| awk {'print $1'}`
- port=`sed -n ""$i"p"/data/portscan/portresult/port.list.nmap| awk {'print $2'}`
- # 当开放端口数量超过 500 时, 直接扫全量端口, 这样反而速度会更快
- if [[ `echo $port | awk -F "," {'print NF'}` -gt 500 ]] ; then
- port="1-65535"
- fi
- # 扫描完成后立即将结果按 "ip 地址 nmap 端口号 开放状态 开放服务 版本号" 方式进行处理
- /usr/bin/nmap -T4 -Pn -sV -n -p $port $ip | grep -v "Nmap" |grep -v "SUBMIT INDIVIDUALLY"|grep -v "MAC Address"| grep -v "Host is up" | grep -v "Service Info"| grep -v -E "SF:" | grep -v "SF-"|grep -v "please submit" | grep -v -E "PORT\s+STATE" |grep -v "Service detection"|sed '/^\s*$/d' | sed 's/^/'"$ip"'nmap /g'|sed 's/\/tcp//g' | sed 's/\s\+/ /g'>>/var/log/nmap 2>> /data/portscan/servicescan.log
- # 命令执行到最后, 把令牌放回管道
- echo>&3
- } 2>> /data/portscan/servicescan.log &
- done
- wait
- exec 3<&- #关闭文件描述符的读
- exec 3>&- #关闭文件描述符的写
四, 结语
接下来考虑使用扫描结果进行高危端口监控和服务识别了, 我是结合 splunk 来做分析的, 各位是愿意接入数据分析平台还是直接撸脚本就看手上的资源和自己的兴趣了. 另外, 有兴趣的小伙伴还可以试着用 python 来实现.
来源: http://www.tuicool.com/articles/YVjiuue