ThreatScan-端口扫描的实现

在不断完善ThreatScan这个项目时,遇到了不少的坑,当然也学习到不少,为此根据ThreatScan的实现,做一些关键功能实现的分享

图源freebuf

ThreatScan是一款扫描器,主要用于渗透测试的第一阶段:信息搜集。

端口扫描:获取目标主机的端口信息显得十分有必要,通过一些常见端口,可以大致得出目标主机运行的服务,为后续渗透测试薄弱点提供参考

ThreatScan效果:

ThreatScan效果

围观地址:https://scan.top15.cn

0x00 端口扫描的原理简述

端口扫描的方式比较多,基于TCP/IP协议,ICMP等协议,利用这些协议的“容错”之处,以此提升端口扫描结果的正确性、高效性。

套接字:TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。(百度百科)。简单来讲,例如:<127.0.0.1:80>,认为其是一个套接字对象

在TCP建立连接三次握手,也就是发三次包以确认并建立可靠稳定连接的过程

TCP三次握手建立连接过程:

三次握手(图源简书)

在建立套接字的时候,就是一个TCP/IP三次握手的过程,通过三次握手可以确认目标主机的服务是否在线

其实从这里可以知道,我们应该使用SYN扫描的方式,这样“半连接”的方式,效率应该会更高一点,同时可以绕过大部分的检测吧!


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 主机去执行扫描任务就好了

0x05 开源地址:

https://github.com/dyboy2017/TScan

发表评论 / Comment

用心评论~

金玉良言 / Appraise
Vision.视觉LV 1
2020-01-27 15:20
师傅写个syn扫描器