在不断完善ThreatScan这个项目时,遇到了不少的坑,当然也学习到不少,为此根据ThreatScan的实现,做一些关键功能实现的分享
ThreatScan是一款扫描器,主要用于渗透测试的第一阶段:信息搜集。
端口扫描:获取目标主机的端口信息显得十分有必要,通过一些常见端口,可以大致得出目标主机运行的服务,为后续渗透测试薄弱点提供参考
ThreatScan效果:
围观地址:https://scan.top15.cn
0x00 端口扫描的原理简述
端口扫描的方式比较多,基于TCP/IP
协议,ICMP
等协议,利用这些协议的“容错”之处,以此提升端口扫描结果的正确性、高效性。
套接字:TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket
)或插口。(百度百科)。简单来讲,例如:<127.0.0.1:80>
,认为其是一个套接字对象
在TCP建立连接三次握手,也就是发三次包以确认并建立可靠稳定连接的过程
TCP三次握手建立连接过程:
在建立套接字的时候,就是一个TCP/IP
三次握手的过程,通过三次握手可以确认目标主机的服务是否在线
0x01 利用socket连接扫描端口
在python的 socket.connect()
,就是一个TCP/IP
三次握手的过程
# 创建套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# TCP/IP三次握手建立连接
result = sock.connect_ex((ip, int(port))) #sock.connect()的扩展,错误会返回错误码,正确返回0
if result == 0:
print(str(port) + "端口存在!")
是不是特别的简单呐?
确实是,因为我们并没有涉及到将TCP/IP协议做一些拆分,例如做ACK的扫描
,FIN扫描
等不完整的扫描过程以此来绕过某些IDS的检测,在实现过程中发现UDP
扫描其实是一个非常慢的过程,后续我们再考虑如何实现其他的扫描方式
0x02 判断服务
根据套接字的端口信息,其实我们想进一步知道端口所对应的服务
在实现此功能时,其实是基于已有的端口+服务名的字典对照得出的结果,这个结果偏向经验型。例如管理员可能会把HTTP
服务放到任意端口,而不是常见的80
端口
因此,考虑还需要查看建立连接时的HTTP Banner
信息
if result == 0: # 成功建立TCP链接
self.port.append(port) # 结果集中增加端口
for i in PROBE: # 通过HTTP1.1刺探
sock.sendall(i.encode()) # 发送完整的TCP数据包
response = sock.recv(256) # 接受最大256byte(字节数),1Mb == 1024Kb == 1024*1024b == 1024*1024*8bit
sock.close()
if response:
break
if response:
for pattern in SIGNS:
pattern = pattern.split(b'|')
if re.search(pattern[-1], response, re.IGNORECASE): # 正则匹配banner信息与字典中的服务
proto = '{}:{}'.format(pattern[1].decode(), port)
self.out.append(proto) # 添加至输出结果
break
0x03 速度优化
大家都知道,如果这样单线程(串行)阻塞运行,耗费的时间可能都够我们自己玩儿一局游戏
因此,通过并发的方式,并发请求,提升扫描速度,通过对比扫描300+
端口,单线程需要30+s
,多线程仅需10s
左右
想着用协程的方式,下次再说吧,先得验证协程可行性,目前来说多线程的效果是可以接受
此处实现原理可参考文章:《python之concurrent.futures — 启动并行任务》
import concurrent.futures
THREADNUM = 64
def run():
# ... 省略大部分代码
with concurrent.futures.ThreadPoolExecutor(max_workers=THREADNUM) as executor:
executor.map(self.socket_scan, hosts)
目前在现网上( https://scan.top15.cn ) 的表现还可以,平均时间都在10s左右
0x04 其他问题
- 目标主机存在扫描拦截、WAF、IDS等
- 目标主机限制并发数量
如上情况都会影响扫描结果的准确性
针对拦截等问题,那就是通过比如UDP
扫描,SYN
等等链接过程不完整性来绕过检测(在NAMP
中,常常使用的是SYN扫描:nmap -sS -P0 -sV -O
)
针对并发数量限制问题,目前在单主机上的解决方案就是限制并发数量在较小的范围,及时断开socket
连接,增加延时扫描
既然在单主机上降低了效率,那么分布于多台服务器上一起扫描呐?预想的方案是Redis
来控制扫描任务,其他 worker
主机去执行扫描任务就好了