扫描器篇(六)之python+scapy组合编写基于UDP协议的端口扫描器,顺带解决UDP协议多线程无响应问题

UDP 端口扫描

  • 要对端口进行扫描需要先了解一下UDP协议的特征
    
    •   UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,
        发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
      
    •   由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,
        因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
      
    •   但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,
        因此在传输重要数据时不建议使用UDP协议。UDP传输数据被限制在64K以内。
      

问题记录

  • 在使用scapy构建UDP数据包,调用多线程去发包没收到响应
    
  • 解释器版本为python3.8.2
    

在这里插入图片描述

  • 使用构造的UDP数据包去访问未开启UDP服务端口时,会返回ICMP端口不可达
    
  • 靶机50-60端口范围只开放了53domain端口,理论来说在给这个区间的范围内发送UDP端口请求包,应该会收到9个ICMP端口不可达,然而实际只收到了6个响应,导致程序功能没有实现
    

n

  • 初步怀疑是靶机防火墙可能存在过滤,打开靶机iptables -L 防火墙并没有出入站规则,但死活就是没有收到相应数量的ICMP端口不可达。
    

在这里插入图片描述

  • 尝试添加防火墙出站策略,放行所有UDP,ICMP出站数据包,试试是不是防火墙的问题,再打开tcpdumo抓包
    

在这里插入图片描述

  • 发现和防火墙一毛钱关系都没有,靶机只响应了6个ICMP端口不可达,剩下三个不知道为啥没响应,搜了一下发现一篇文章指出了问题,大致意思是缺少了某个函数。
    

在这里插入图片描述

  • 看了解决问题的程序,奈何自己太垃圾是用其他语言写的,看不懂。开始怀疑自己的是不是对UDP协议理解有问题了,回翻看笔记找到了没收到ICMP不可达的原因,第一和第三都排除了,就剩第二了
    

在这里插入图片描述

  •  上网搜了一下ICMP端口不可达响应速率,发现了一篇文章写了华为设备对icmp信息的relay速度有限制,目的就是为了防止被dos占用设备性能,造成正常服务无法工作。然后再翻了一下自己路由器看到了一个熟悉的菊花logo。
    
  • 	莫名有了种想砸路由器的冲动。回归正题,找到问题所在了就可以通过修改代码来解决问题了。
    

四层主机端口发现

  四层发现指利用OSI中的传输层协议进行主机发现,一般使用TCP、UDP探测。
  •  优点:
     1、可以探测远程主机;
     2、比三层发现更为可靠
    
  • 缺点:花费时间更长,可能被防火墙过滤
    

探测目标主机是否开放

  1.  向目标主机的相应端口发送UDP数据包,如果没有返回回复,则证明这个端口是开放
    
  2. 需要注意的是这点是利用了ICMP不可达的原理,如果目标主机不在线那么也没有返回消息,导致误判,所以要求目标主机必须在线
    
  3. 数据包构造
    
    • result = sr1(IP(dst="192.168.3.107")/UDP(dport=53),timeout=1,verbose=0)
      
    • 最常用的udp端口为53DNS服务
      

代码部分

扫描模块

  • 功能
    
    1.    接收主函数传过来的IP,端口构造UDP数据包
      
    2.  向端口批量发送数据包,并利用是否有返回消息判断端口是否在线
      
def scan(ip, port):
    try:
        packet = IP(dst=ip)/UDP(dport=port,sport=randint(1,65535))
        result = sr1(packet,timeout=5,verbose=0) # timeout=5 为数据包提供五秒等待时间,如果没有回复就放弃,verbose=0,不显示输出
        time.sleep(0.1)		
        if result is None:
            print(port)
    except:
        print("send error")

主函数

  • 功能
    •  接收用户输入的参数
      
    • 判断用户输入的端口是单个还是端口范围
      
    •   如果是端口范围,将起始端口和结束端口for循环进行遍历生成需要扫描的端口
      
def main():

    parser = OptionParser("Usage: targetIP start_Port end_port ") 	# 输出帮助信息
    parser.add_option("-i", '--host',type="string", dest="tgtIP",help="specify target host website")	# 获取ip地址
    parser.add_option("-p", '--port',type="string", dest="hostport",help="specify target host singleport")	# 获取单个端口
    parser.add_option("-s", '--start',type="string", dest="startport",help="specify target host start_port")	# 获取结束端口
    parser.add_option("-e", '--end',type="string", dest="endport",help="specify target host end port") # 获取起始端口
    options,args= parser.parse_args()	#实例化用户输入的参数

	# 为用户输入的参数建立对象
    ip = options.tgtIP
    singleport = int(options.hostport)
    start = options.startport
    end = options.endport

	# 如果是单端口要执行的操作
    if singleport:
        port = singleport
        scan(ip,port)
	# 如果是端口区间则多线程扫描
    if start and end:
        start = int(start)
        end = int(end)
        for p in range(start,end):
            port = int(p)
            t = Thread(target=scan,args=(ip,port))
            t.start()
            time.sleep(1)

完整代码

from random import randint
from time import sleep
from optparse import OptionParser
from scapy.all import *


def scan(ip, port):
    try:
        packet = IP(dst=ip)/UDP(dport=port,sport=randint(1,65535))      # 随机src端口,让扫面不易被察觉
        result = sr1(packet,timeout=5,verbose=0)         # timeout=5 为数据包提供五秒等待时间,如果没有回复就放弃,verbose=0,不显示输出
        time.sleep(0.1)
        if result is None:
            print(ip, port,'is open')
    except:
        print("send error")


def main():

    parser = OptionParser("Usage: targetIP start_Port end_port ")       #输出帮助信息
    parser.add_option("-i", '--host',type="string", dest="tgtIP",help="specify target host website") # 获取ip地址
    parser.add_option("-p", '--port',type="string", dest="hostport",help="specify target host singleport")      # 获取单个端口
    parser.add_option("-s", '--start',type="string", dest="startport",help="specify target host start_port") # 获取起始端口
    parser.add_option("-e", '--end',type="string", dest="endport",help="specify target host end port") #获取结束端口
    options,args = parser.parse_args()  # 实例化用户输入的参数

    ip = options.tgtIP
    singleport = options.hostport
    start = options.startport
    end = options.endport
	# 如果是单端口则执行的操作
    if singleport:
        port = int(singleport)
        scan(ip,port)
	# 如果是端口区间则调用多线程去扫描
    if start and end:
        start = int(start)
        end = int(end)
        for p in range(start,end):
            port = int(p)
            t = Thread(target=scan,args=(ip,port))
            t.start()
            time.sleep(1)

if __name__ == "__main__":
    main()





if __name__ == '__main__':
    main()

运行效果
在这里插入图片描述

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读