如何利用僵尸扫描来发现服务器的开放端口
0x00:简介
在渗透测试信息搜集阶段,当我们发现目标服务器开放了端口时,可以使用三种扫描方式进行端口探测:隐蔽扫描、全链接扫描和僵尸扫描。全链接扫描是发送正常的请求,包括三次握手,判断端口开放的依据是第三次握手返回的信息是否为FIN/ACK。
隐蔽扫描是只发送第一次握手的SYN包,判断端口开放的依据是返回的信息是否为SYN/ACK,如果是,则端口开放;否则为未开放状态,会返回RST/ACK的信息。僵尸扫描是利用僵尸机(非自己的机器)对目标服务器进行端口探测,由于使用的是僵尸机,其隐蔽性高,追踪不到自己的IP,利用程度比隐蔽扫描和全链接扫描更高。
0x01:原理
僵尸扫描涉及自己的机器、僵尸机和目标服务器三个角色。
第一步:扫描者直接向僵尸机发送第二次握手包,即SYN/ACK,类似于隐蔽扫描,直接发送SYN/ACK,僵尸机会返回一个RST包,其中包含了ipid参数,即数据包的序列号,记录下此序列号,例如为x。
第二步:扫描者伪造自己的IP为僵尸机的IP,向目标服务器发送第一次握手包SYN,发送后立即关闭连接,这时目标服务器收到的是一个异常的SYN包。
第三步:端口开放的情况下,当目标服务器收到SYN后会返回一个SYN/ACK包,这个包是返回给僵尸机的,因为SYN是自己伪造僵尸机发出的,所以僵尸机会直接返回一个RST包,此时RST的序号为x+1。
第四步:端口关闭的情况下,当目标服务器只收到一个SYN时,由于没有完整的后续握手,所以会返回一个RST包。由于僵尸机直接收到了一个RST包,后续不会再发送其他握手包,此时包序号仍停留在x。
第五步:扫描者再次直接向僵尸机发送SYN/ACK包,不完整的握手,仍类似于隐蔽扫描,所以僵尸机会直接返回RST包,此时RST的ipid序号如果是x+1,则证明目标端口未开放;如果是x+2,则证明收到过目标机的SYN/ACK包,目标端口是开放状态。
以上为僵尸扫描的流程和原理,整个过程需要一个基础条件,即僵尸机是足够空闲的状态,不会产生其他的数据包通信,并且ipid序号是递增的。由于这一点,僵尸扫描的难度较大,且难以利用,递增ipid的机器包括XP、2000、2003等。
僵尸扫描的特点包括:极度隐蔽、实施条件苛刻、可伪造源地址、需要一个僵尸机、僵尸机必须是空闲系统、僵尸机的ipid递增。
0x02:实验
在实验中,我们使用kali作为扫描机器(IP为103),win2003作为僵尸机(IP为105),metasploitable作为目标机(IP为104)。首先使用scapy库来实现僵尸扫描发现端口的过程。
在kali的命令行中输入scapy进入scapy环境,构造一个SYN/ACK包发送给僵尸机,命令如下:
其中通过IP头和TCP头组合了一个请求包,dst为目标IP地址,即僵尸机的IP地址。dport为目标端口,这里使用的是445,445是Windows中的文件共享服务,一般是开放的状态。最后通过display命令查看组合好的请求包。
获取了僵尸机的请求包后,需要再构造一个发送给目标机的请求包,命令如下:
其中src指定了发送的源地址,这里伪造自己为僵尸机的IP,检测的是目标服务器的21端口。请求包配置完成后,按照僵尸扫描流程,首先发送给僵尸机,然后发送给目标服务器,最后再发送一次给僵尸机。在scapy中,发送包的命令使用sr1,三次发送的命令如下:
可以通过僵尸扫描流程图和上述命令执行结果图进行对比,更清晰地理解整个过程。首先使用sr1(rz)命令发送了僵尸机的包,可以看到id为108,返回的flags为SA,即SYN/ACK。其次使用sr1(rt)发送了目标服务器的包,最后再一次向僵尸机发送了请求包。需要注意的是,最后一次请求包的id为110,差值为2,证明目标服务器的21端口是开放状态。
下面是端口未开放时scapy的执行过程,发送给僵尸机的包不变,只需改变发送给目标服务器的端口号,如下图所示:
检测目标服务器的33端口,33端口为关闭状态。发送过程与上述相同,执行结果如下:
#!/usr/bin/python import logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) from scapy.all import * def ipid(zombie): reply1 = sr1(IP(dst=zombie)/TCP(flags="SA"),timeout=2,verbose=0) send(IP(dst=zombie)/TCP(flags="SA"),verbose=0) reply2 = sr1(IP(dst=zombie)/TCP(flags="SA"),timeout=2,verbose=0) if reply2[IP].id == (reply1[IP].id + 2): print "IPID sequence is incremental and target appears to be idle. zombie located" reponse = raw_input("Do you want to use this zombie to perform a scan? (Y or N):") if reponse == "Y": target = raw_input("Enter the IP address of the target system:") zombiescan(target,zombie) else: print "Either the IPID sequence is not incremental or the target is not idle. not a good zombie" def zombiescan(target,zombie): print "\nScanning target " + target + " with zombie " + zombie print "\n----------Open Ports on Target----------\n" for port in range(1,100): try: start = sr1(IP(dst=zombie)/TCP(flags="SA",dport=port),timeout=2,verbose=0) send(IP(src=zombie,dst=target)/TCP(flags="S",dport=port),verbose=0) end = sr1(IP(dst=zombie)/TCP(flags="SA"),timeout=2,verbose=0) if end[IP].id == (start[IP].id + 2): print port except: pass print "----------Zombie Scan Suite----------\n" print "1 - Identify Zombie Host\n" print "2 - Perform Zombie Scan\n" ans = raw_input("Select an Option (1 or 2): ") if ans == "1": zombie = raw_input("Enter IP address to test IPID sequence: ") ipid(zombie) else: if ans == "2": zombie = raw_input("Enter IP address for zombie system: ") target = raw_input("Enter IP address for scan target: ") zombiescan(target,zombie)
第一次发送给僵尸机的数据包返回结果中id为146,然后发送给目标服务器的数据包返回结果中flags不再是SA,而是RA。最后再一次向僵尸机发送的数据包返回结果中id为147,与第一次的差值为1,证明目标端口为关闭状态。
使用scapy通过僵尸扫描方式发现目标服务器端口后,可以使用Python脚本实现批量检测。以下是示例脚本:
脚本中的ipid函数用于检测一个机器是否是合格的僵尸机,判断方式是通过id的序号进行判断。zombiescan函数是僵尸扫描函数,与前面的scapy命令行执行流程相同,脚本中指定了扫描端口范围为1-100。
使用过程:运行脚本后,首先会打印出1为僵尸机检测,2为僵尸扫描,然后根据需求选择1或2,选择1需要输入僵尸机的IP,选择2需要输入僵尸机的IP和目标机器的IP。在僵尸机检测合格后,会提示僵尸机合格,并询问是否要进行扫描,输入Y后将跳转到zombiescan函数,执行过程如下:
运行脚本后,如果目标服务器的1-100范围的端口在线,则会被打印出来。为了更清晰地了解僵尸扫描的过程,可以通过Wireshark进行抓包,部分过程如下:
对于僵尸扫描,除了使用scapy外,nmap也具备此检测功能,并且使用更简单方便。在检测一个机器是否为合格的僵尸机时,nmap提供了ipidseq脚本,使用方法如下:
扫描结果除了列出端口的开放情况和服务,还包括主机的MAC信息和系统信息,最后给出是否可以作为一个僵尸机的判断,ipidseq给出的值为incremental,即递增的意思,表示ipid是递增的,代表其合格。有了合格的僵尸机后,再次使用nmap进行端口发现,主要使用参数-sI,该参数用于指定僵尸机的IP地址,命令说明如下:
命令格式为:nmap 192.168.123.104 -sI 192.168.123.105 -Pn -p 0-100,执行结果如下:
结果与前面使用scapy检测的结果一致。nmap僵尸检测端口的Wireshark抓包如下:
0x03:总结
在端口扫描的三种方式中,僵尸扫描是最隐蔽的一种方式,但执行难度也较高。确认目标端口的开放情况时,不建议仅依赖一种扫描方式的结果,而是建议使用多种不同的发现方式,因为每种方式都可能存在多少许误差,不是百分之百准确。在多种扫描方式的结果下,做出总结,确定端口的开放情况。