端口探测

端口扫描的方式

  1. TCP全连接扫描:模拟客户端发起TCP连接的过程,执行完整的三次握手来确定端口是否开放。扫描器会调用操作系统级别的connect()函数来尝试与目标主机的某个端口建立连接。如果端口开放并监听连接,目标主机将回应一个ACK确认数据包,从而建立起连接。这种方式相对容易被防火墙和IDS(入侵检测系统)捕获。

    成功建立连接,则为open;收到RST则为close。

    eg. telnet IP port,nc

  2. TCP半连接扫描:又称SYN扫描,是为了减少痕迹和避免引起警报而设计的更隐蔽的扫描方式。扫描器只发起TCP连接的第一次握手,即发送一个SYN数据包。如果目标端口开放,它会回应一个SYN+ACK数据包。这时,并不会完成第三次握手(即不发送ACK),源ip会发送一个RST(复位)数据包来终止连接。这样就不需要建立完整的连接,降低了被检测的风险。

    源ip收到ACK+SYN则为open;收到RST则为close。

    eg. masscan 默认SYN探测;socket;nmap默认SYN探测;很多扫描器都是这种方式,所以很多IDS会检测这项

  3. UDP扫描:发送空的(没有数据)UDP报头到每个目标端口。一般如果返回ICMP port unreachable说明端口是关闭的,而如果没有回应或有回应(有些UDP服务是有回应的但不常见)则认为是open,但由于UDP的不可靠性,无法判断报文段是丢了还是没有回应,所以一般扫描器会发送多次,然后根据结果再判断。这也是为什么UDP扫描这么慢的原因。

  4. 秘密扫描:如FIN扫描、Xmas Tree扫描、Null扫描等,这些扫描方式通过发送非标准的TCP数据包(如只有FIN标志位的包或多个标志位同时置位的包)来探测端口状态,这些扫描方式的目的在于尽量减少在目标主机上产生的日志信息,以及避免触发防火墙规则或入侵检测系统的警报。

    如果没有响应,说明端口开放;如果返回了RST,说明端口关闭;返回ICMP ERROR,则说明端口被过滤了。

端口扫描

针对单个端口扫描

使用Python的socket库的connect()方法进行tcp扫描 原理:指定IP和端口号,连接到特定主机对应的远程socket,连接失败会抛出timeout错误,通过判断是否连接成功来判断端口是否开放。

  1. 首先使用socket.socket(socket.AF_INET, socket.SOCK_STREAM)建立一个基于网络并且使用TCP协议的套接字;

  2. 然后使用connect()尝试建立连接,需要的参数为IP和端口号;

  3. 最后根据判断返回的结果是成功还是超时,来判断端口是否开放。

代码示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import socket
def star_scan(ip, port):
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # 创建一个基于网络并且使用tcp协议的套接字,用于通信。
        s.settimeout(0.02)	# 设置超时时间
        s.connect((ip, port))
    except Exception as e:
        print(e)
    else:
        res = s.recv(3096).decode('utf-8').encode()
        print(res)
        print("[+]{}:{} \topen".format(ip, port))
    finally:
        s.close()
        
ip = "192.168.247.135"
port = 22
star_scan(ip, port)

批量端口扫描

  1. 可以保存一批端口作为默认端口,要确定哪些作为默认端口:常用的,比如3389、3306等等,以及在网上看别人工具定义的默认端口

  2. 制定端口范围进行扫描

  3. 一个个扫,但是网络I/O非常耗时,程序会耗费很多时间在等待上

  4. 利用协程进行提效:在一个协程进入IO等待时,会自动切换到其他协程继续执行,也就是最大化的利用了IO等待时间,这个过程一直都是单线程,只是程序在一个协程进入等待后切换到另一个协程给CPU处理,而对于CPU来说则一直是同一个程序在给它发任务,CPU是感受不到区别的。

我们每扫一个端口都要建立一次连接,而每次建立连接都会有一次等待,那我们就把每一个要建立的连接都放到一个协程里,在一个连接也就是协程进入IO等待后,系统会自动切换去建立下一个连接,这样效率就会大大提升,这个切换时自动的,完全不需要我们去做什么。

python中可以使用gevent库来实现协程

  • 使用gevent.spawn(start_scan)创建协程
  • 使用gevent.join()或者gevent.joinall()执行协程
  • 协程的数量等于我们要扫描的次数。
  • 创建队列来保存要扫描的IP和port,根据队列长度创建协程,之后每个协程在执行任务的时候就把队列里的信息提取出来一个,当把队列空了的时候,任务也就处理完了。
  • 如果有端口开着但是没扫到,可以考虑增长超时时间

nmap不同扫描模式的说明

  1. -sT:全连接扫描,Nmap会调用系统底层的connect与目标端口建立连接
  2. -sS:半连接扫描(nmap默认SYN探测)
  3. -sN:null扫描,标志位全为0
  4. -sF:FIN扫描,标志位FIN=1,其余为0
  5. -sA:ACK扫描,不能用来确定端口开放状态,但是可以用来映射防火墙规则集,确定是否有状态监测防火墙,以及对哪些端口进行了过滤。当扫描被过滤系统时,无论是开启或关闭的端口都会返回RST数据包,则Nmap会标记为 unfiltered ;若端口没有响应包或收到ICMP不可达报错,则标记为 filtered。
  6. -sU:UDP扫描
  7. -sV:探测服务版本
  8. -A:全面扫描
  9. -O:目标主机的系统版本
  10. -Pn:跳过主机发现,默认目标主机在线
  11. -n:不进行反向DNS解析;反向DNS解析会解析出主机名称,可以推断这个主机的用途;比较耗时
Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计
本博客已稳定运行