https 带端口_基于scapy模块的8种端口扫描

b9419ecd6c89e3ec00e27bde83356e97.png

少年锦时

投我以木桃,报之以琼瑶;你予我三月春光,我赠你四月桃花。人间真情莫过于此。

俗话说的好

ce9d3e31a02fbfebb0c21999d325469c.png

ef21921e9480cbd4ee55f5a98d32f400.png

前言

95f9533054f03109f742429ea47be5b4.png

?:老哥,是时候前面写的扫描器升升级了,不然都没法打怪了。。。

e821776cedd69976e58d1f81c3d9aa78.png:从何说起啊?

?:一扫就被墙,一扫就被墙,整个*啊!

e821776cedd69976e58d1f81c3d9aa78.png:也难怪,哥技术不好,无法带你飞,今后的路你可要自行摸索啊!!!

?:老哥~6f0fe2e887855e582f9c4681543bee6f.png

e821776cedd69976e58d1f81c3d9aa78.png:老弟~6f0fe2e887855e582f9c4681543bee6f.png

?:行啦!别整这些没用的,快给我想招,要不然弄*你!!!

d8c51255d26457682c86261474173d0e.png

e821776cedd69976e58d1f81c3d9aa78.png:这变脸如翻书啊。。。咳咳嗯~,方法是有滴,前面不是介绍了 scapy 模块嘛,好家伙,这模块,写扫描,写嗅探,游历于各层协议如入无人之境,取敌方主机首级如探囊取物,各种协议分析函数信手拈来,简直人中吕布,马中赤兔,是为网络协议分析之上乘神兵利器也~

7f16118da27e2cd02de2e8a1df8b472c.png

?:逼逼完了就赶紧说说怎么整吧,一个大老爷们磨磨唧唧的。

e821776cedd69976e58d1f81c3d9aa78.png:话说前面写的端口扫描器只有一种扫描方式 -- TCP全连接扫描,在无防火墙/IPS/IDS ,且网络顺畅的情况下,是为最精准的扫描方式,毕竟是经过了三次握手,但是在当今这错综复杂且危机四伏的网络时代,网络链路中怎能 "无墙" 之说,为了应对这种情况,如 Nmap 端口扫描神器的扫描方式也是衍生出了十几种之多(详情可参考下方链接),在实际的渗透测试过程中,多种扫描方式相结合方能起到较为准确的扫描结果。

https://nmap.org/man/zh/man-port-scanning-techniques.html

5bdfdbc9d93ec175403260db2f54b462.png

Nmap常用的扫描方式

e821776cedd69976e58d1f81c3d9aa78.png:下面我们参考 nmap 的端口扫描原理,共采用8种端口扫描方式:TCP 全连接扫描,TCP SYN扫描,TCP ACK扫描,TCP Null扫描,TCP Window扫描,TCP Fin扫描,TCP Xmas扫描和UDP扫描。

?:哟哟哟~你看这个面它又长又宽,再看这个碗它又大又圆~

f88836bb533c362e8ff3e53f24751033.png

ef21921e9480cbd4ee55f5a98d32f400.png

声明

95f9533054f03109f742429ea47be5b4.png

以下内容参考自玄魂大佬的端口扫描器笔记,如有侵权,请联系删除!!!

5df1be0ecf189a7445615d471ccea078.png

ef21921e9480cbd4ee55f5a98d32f400.png

前置知识

95f9533054f03109f742429ea47be5b4.png

前置知识可以看前一篇文章《TCP三次握手/四次挥手》,主要理解TCP三次握手建立连接的过程即可。

利用 argparse 模块接收命令行参数,关于该模块的简介可参考前面的文章。

import argparse# 打印窗口的状态def print_ports(port, state):    print("%s | %s" % (port, state))# TCP全连接扫描def tcpScan(target, ports):    print("TCP全连接扫描")# TCP SYN扫描def synScan(target, ports):    print("TCP SYN扫描")# TCP ACK扫描def ackScan(target, ports):    print("TCP ACK扫描")# TCP window扫描def windowScan(target, ports):    print("TCP window扫描")# TCP Null扫描def nullScan(target, ports):    print("TCP Null扫描")# TCP FIN扫描def finScan(target, ports):    print("TCP FIN扫描")# TCP XMAX扫描def xmaxScan(target, ports):    print("TCP XMAX扫描")# UDP 扫描def udpScan(target, ports):    print("UDP 扫描")if __name__ == '__main__':    parser = argparse.ArgumentParser("")    parser.add_argument("-t", "--target", dest="target",                        help="目标IP", required=True)    parser.add_argument("-p", "--ports", dest="ports",                        type=int, nargs="+", help="指定端口列表(21 23 80 ...)")    parser.add_argument("-s", "--scantype", dest="scantype", help="""        "T":全连接扫描        "S":syn扫描        "A":ack扫描        "W":TCPwindows扫描        "N":NULL扫描        "F":FIN扫描        "X":Xmas扫描        "U":UDP扫描        """, required=True)    args = parser.parse_args()    target = args.target    scantype = args.scantype    if args.ports:        ports = args.ports    else:        ports = range(1, 65535)    print("扫描主机{},端口:{}".format(target, ports))    if scantype == "T":        tcpScan(target, ports)    elif scantype == "S":        synScan(target, ports)    elif scantype == "A":        ackScan(target, ports)    elif scantype == "W":        windowScan(target, ports)    elif scantype == "N":        nullScan(target, ports)    elif scantype == "F":        finScan(target, ports)    elif scantype == "X":        xmaxScan(target, ports)    elif scantype == "U":        udpScan(target, ports)    else:        print("不支持当前模式。")

a06ffd87ff72588e97fa6cd75bdcc7b5.png

ef21921e9480cbd4ee55f5a98d32f400.png

TCP Connect扫描

95f9533054f03109f742429ea47be5b4.png

TCP Connect扫描又叫做全连接扫描,客户端与服务器建立 TCP 连接要进行一次三次握手,如果进行了一次成功的三次握手,则说明端口开放。

假设客户端想与服务端的80端口进行通信,首先客户端会发送一个带有SYN标识和端口号的TCP数据包给服务器,如果服务器这个端口是开放的,则会接受这个连接并返回一个带有SYN和ACK标识的数据包给客户端,随后客户端会发送带有ACK和RST标识的数据包给服务点,此时客户端与服务器建立了连接。如果端口不开放则会返回一个RST标识的数据包给客户端。

3d997f8e662919a8ae80444454e731dd.png

当客户端发送一个带有 SYN 标识和端口号的 TCP 数据包给服务器后,如果服务器端返回一个带 RST 标识的数据包,则说明端口处于关闭状态。

4e6ce16644296543478944f33c16b148.png

代码实现如下:

def tcpScan(target,ports):    print("tcp全连接扫描 %s with ports %s" % (target, ports))    for port in ports:        send=sr1(IP(dst=target)/TCP(dport=port,flags="S"),timeout=2,verbose=0) #sr1()工作在第三层,用来发送数据包和返回一个应答数据包,verbose=0 是不显示数据收发过程        if (send is None):            print_ports(port,"closed")        elif send.haslayer("TCP"): #返回的数据包有 TCP 部分            if send["TCP"].flags == "SA": #TCP部分SYN和ACK都置位,说明对方主机响应连接                send_1 = sr1(IP(dst=target) / TCP(dport=port, flags="AR"), timeout=2, verbose=0) #将 TCP 部分的 ACK和RST 都置位,断开连接                print_ports(port,"open")            elif send["TCP"].flags == "RA": #TCP部分的RST和ACK置位,说明对方主机拒绝连接                print_ports(port,"close")            else:                print_ports(port,"unknown")        else:            print_ports(port,"unknown")

上面的代码,组合IP和TCP报文,依据三次握手的流程对端口状态做判断。主义TCP标志位的设置和判断。使用scapy发送tcp数据包的时候TCP的标志位使用如下简写形式:

  • F : FIN - 结束; 结束会话

  • S : SYN - 同步; 表示开始会话请求

  • R : RST - 复位;中断一个连接

  • P : PUSH - 推送; 数据包立即发送

  • A : ACK - 应答

  • U : URG - 紧急

  • E : ECE - 显式拥塞提醒回应

  • W : CWR - 拥塞窗口减少

ef21921e9480cbd4ee55f5a98d32f400.png

TCP SYN扫描

95f9533054f03109f742429ea47be5b4.png

TCP SYN扫描又称半开式扫描,该过程不会和服务端建立完整的连接,首先客户端会发送一个带有SYN标识和端口号的TCP数据包给服务器,如果服务器这个端口是开放的,则会接受这个连接并返回一个带有SYN和ACK标识的数据包给客户端,随后客户端会返回带有RST标识的数据包而不是返回一个带有ACK和RST标识的数据包。

1caa25fe83190b22caa2f10631e9b278.png

如果目标端口处于关闭状态,则服务端会返回一个RST标识的数据包。

4e6ce16644296543478944f33c16b148.png

代码实现如下:

def synScan(target, ports):    print("tcp半开放扫描 %s with ports %s" % (target, ports))    for port in ports:        send = sr1(IP(dst=target) / TCP(dport=port,flags="S"), timeout=2, verbose=0)        if send is None:            print_ports(port, "close")        elif send.haslayer("TCP"):            # print(send["TCP"].flags)            if send["TCP"].flags == "SA":                send_1 = sr1(IP(dst=target) / TCP(dport=port,flags="R"), timeout=2, verbose=0)                print_ports(port, "open")            elif send["TCP"].flags == "RA":                print_ports(port, "close")            else:                print_ports(port, "unkonwn")        else:            print_ports(port, "unkonwn")

注:SYN扫描和TCP全连接扫描的区别在于结束与服务端连接的数据包:SYN扫描是直接发RST,而TCP全连接扫描是发ACK和RST

ef21921e9480cbd4ee55f5a98d32f400.png

TCP ACK扫描

95f9533054f03109f742429ea47be5b4.png

ACK扫描只返回两种状态:unfiltered/filtered

发送一个flags为ACK报文,open(开放的)和closed(关闭的) 端口 都会返回RST报文,至于他们是open还是closed状态我们无法确定。不响应的端口,或者发送特定的ICMP错误消息(类型3,代号1,2,3,9,10, 或者13)的端口,标记为 filtered(被过滤的)。大致的流程如下图:

df3d7f01a94776bfb1f1061012f542c4.png

上面那种情况下是服务器REJECT掉数据包,所以客户端会有个ICMP包返回,如果是直接DROP掉的话,就会什么也不会返回,所以我们要判断该主机是否存在,因为如果一个主机存在的话,向它发送一个flags为ACK包的话,无论端口是否关闭都会有返回一个flags为RST包,如果是DROP是话就会一个数据包都不会返回,所以我们可以利用这一点去判断端口是否被过滤了,大致流程如下:

e30635c42394b87f3af43d188f3cff6c.png

代码实现如下:

def ackScan(target, ports):    print("tcp ack扫描 %s with ports %s" % (target, ports))    for port in ports:        ack_flag_scan_resp = sr1(            IP(dst=target) / TCP(dport=port, flags="A"), timeout=5, verbose=0)        # print(str(type(ack_flag_scan_resp)))        if str(type(ack_flag_scan_resp)) == "":            print_ports(port, "filtered")        elif ack_flag_scan_resp.haslayer(TCP):            if ack_flag_scan_resp.getlayer(TCP).flags == "R":                print_ports(port, "unfiltered")            else:                print_ports(port, "unkonwn")        elif ack_flag_scan_resp.haslayer(ICMP):            if int(ack_flag_scan_resp.getlayer(ICMP).type) == 3 and int(ack_flag_scan_resp.getlayer(ICMP).code) in [1, 2, 3, 9, 10, 13]:                print_ports(port, "filtered")            else:                print_ports(port, "filtered")        else:            print_ports(port, "unkonwn")
ef21921e9480cbd4ee55f5a98d32f400.png

TCP Window扫描

95f9533054f03109f742429ea47be5b4.png

TCP 窗口扫描的流程同 ACK 扫描类似,同样是客户端向服务器发送一个带有 ACK 标识和端口号的 TCP 数据包,但是这种扫描能够用于发现目标服务器端口的状态。在 ACK 扫描中返回 RST 表明没有被过滤,但在窗口扫描中,当收到返回的 RST 数据包后,它会检查窗口大小的值。如果窗口大小的值是个非零值,则说明目标端口是开放的。

4383e99a8b7b86c7106eab742cd94d27.png

如果返回的 RST 数据包中的窗口大小为0,则说明目标端口是关闭的。

注:TCP窗口扫描和ACK扫描类似,区别在于TCP窗口扫描在接收到RST包时会检查窗口大小,如果窗口是一个非零值,就说明端口是开放的。

代码实现:

def windowScan(target, ports):    print("tcp window扫描 %s with ports %s " % (target, ports))    for port in ports:        window_scan_resp = sr1(            IP(dst=target) / TCP(dport=port, flags="A"), timeout=5, verbose=0)        # print(str(type(window_scan_resp)))        if str(type(window_scan_resp)) == "":            print_ports(port, "close")        elif window_scan_resp.haslayer(TCP):            if window_scan_resp.getlayer(TCP).window == 0:                print_ports(port, "close")            elif window_scan_resp.getlayer(TCP).window > 0:                print_ports(port, "open")            else:                print_ports(port, "unkonwn")        else:            print_ports(port, "close")
ef21921e9480cbd4ee55f5a98d32f400.png

TCP Null扫描

95f9533054f03109f742429ea47be5b4.png

在空扫描中,客户端发出的 TCP 数据包仅仅只会包含端口号而不会有其他任何的标识信息。如果目标端口是开放的则不会回复任何信息。

如果服务器返回了一个 RST(或者RST+ACK) 数据包,则说明目标端口是关闭的。

867874786deea6dc273708c016358419.png

如果返回 ICMP 错误类型3且代码为1,2,3,9,10或13的数据包,则说明端口被服务器过滤了。

1150959047c64d3fce061eb3344e76ac.png

代码实现:

def nullScan(target, ports):    print("tcp null 扫描 %s with ports %s" % (target, ports))    for port in ports:        null_scan_resp = sr1(            IP(dst=target) / TCP(dport=port, flags=""), timeout=5, verbose=0)        if str(type(null_scan_resp)) == "":            print_ports(port, "open|filtered")        elif null_scan_resp.haslayer(TCP):            if null_scan_resp.getlayer(TCP).flags == "R" or null_scan_resp.getlayer(TCP).flags == "RA":                print_ports(port, "close")            else:                print_ports(port, "unkonwn")        elif null_scan_resp.haslayer(ICMP):            if int(null_scan_resp.getlayer(ICMP).type) == 3 and int(null_scan_resp.getlayer(ICMP).code) in [1, 2, 3, 9, 10, 13]:                print_ports(port, "filtered")            else:                print_ports(port, "unkonwn")        else:            print_ports(port, "unkonwn")
ef21921e9480cbd4ee55f5a98d32f400.png

TCP FIN扫描

95f9533054f03109f742429ea47be5b4.png

FIN 扫描会向服务器发送带有 FIN 标识和端口号的 TCP 数据包。如果没有服务器端回应则说明端口开放。

acd5adbef311e275d4d960c97253c827.png

如果服务器返回一个 RST 数据包,则说明目标端口是关闭的。

ee628b7b2af5b5de7830539af1e34439.png

如果服务器返回了一个 ICMP 数据包,其中包含 ICMP 目标不可达错误类型3以及 ICMP 代码为1,2,3,9,10或13,则说明目标端口被过滤了无法确定端口状态。

96d0ccf0103eecafbc721bd91cfe2652.png

代码实现:

def finScan(target, ports):    print("tcp FIN扫描 %s with ports %s" % (target, ports))    for port in ports:        fin_scan_resp = sr1(IP(dst=target) / TCP(dport=port,                                                 flags="F"), timeout=3, verbose=0)        if str(type(fin_scan_resp)) == "":            print_ports(port, "open|filtered")        elif fin_scan_resp.haslayer(TCP):            if fin_scan_resp.getlayer(TCP).flags == 0x14:  # 0x14就是RA                print_ports(port, 'close')            else:                print_ports(port, "unkonwn")        elif fin_scan_resp.haslayer(ICMP):            if int(fin_scan_resp.getlayer(ICMP).type) == 3 and int(fin_scan_resp.getlayer(ICMP).code) in [1, 2, 3, 9, 10, 13]:                print_ports(port, "filtered")            else:                print_ports(port, "unkonwn")        else:            print_ports(port, "unkonwn")
ef21921e9480cbd4ee55f5a98d32f400.png

TCP 圣诞树(Xmas扫描)

95f9533054f03109f742429ea47be5b4.png

在发送的数据包中设置PSH(PUSH推送,数据包立刻发送),FIN(结束会话),URG(紧急)标志位,如果目标端口是开放的则不会回复任何信息。

a46a5cc7336deda7082e6d2d9448dbbf.png

如果目标端口关闭则会返回一个RST+ACK的数据包。

1d58226e5a3bb6b42d41e19d1648a92b.png

但如果服务器返回了一个 ICMP 数据包,其中包含 ICMP 目标不可达错误类型3以及 ICMP 状态码为1,2,3,9,10或13,则说明目标端口被过滤了无法确定是否处于开放状态。

aafd3aa1bcc1d692f01f6c8378400f02.png

代码实现:

def xmaxScan(target, ports):    print("tcp xamx扫描 %s with ports %s" % (target, ports))    for port in ports:        xmax_scan_resp = sr1(            IP(dst=target) / TCP(dport=port, flags="FPU"), timeout=3, verbose=0)        if str(type(xmax_scan_resp)) == "":            print_ports(port, "open|filtered")        elif xmax_scan_resp.haslayer(TCP):            if xmax_scan_resp.getlayer(TCP).flags == "R":                print_ports(port, "close")            else:                print_ports(port, "unkonwn")        elif xmax_scan_resp.haslayer(ICMP):            if int(xmax_scan_resp.getlayer(ICMP).type) == 3 and int(xmax_scan_resp.getlayer(ICMP).code) in [1, 2, 3, 9, 10, 13]:                print_ports(port, "filtered")            else:                print_ports(port, "unkonwn")        else:            print_ports(port, "unkonwn")
ef21921e9480cbd4ee55f5a98d32f400.png

UDP扫描

95f9533054f03109f742429ea47be5b4.png

TCP 是面向连接的协议,而UDP则是无连接的协议。

面向连接的协议会先在客户端和服务器之间建立通信信道,然后才会开始传输数据。如果客户端和服务器之间没有建立通信信道,则不会有任何产生任何通信数据。

无连接的协议则不会事先建立客户端和服务器之间的通信信道,只要客户端到服务器存在可用信道,就会假设目标是可达的然后向对方发送数据。

客户端会向服务器发送一个带有端口号的 UDP 数据包。如果服务器回复了 UDP 数据包,则目标端口是开放的。

99115f3bbfb0287b04b2946975990765.png

如果服务器返回了一个 ICMP 目标不可达的错误和代码3,则意味着目标端口处于关闭状态。

e296b531bc90273148bfa44725ecdd80.png

如果服务器返回一个 ICMP 错误类型3且代码为1,2,9,10或13的数据包,则说明目标端口被服务器过滤了。

4d86acb0a8e9e69fb05c9f48c72f10f6.png

但如果服务器没有任何相应客户端的 UDP 请求,则可以断定目标端口可能是开放或被过滤的,无法判断端口的最终状态。

55bfbcb241aaf20dd78d737f8c4676bc.png

代码实战:

def udpScan(target, ports):    print("UDP扫描 %s with ports %s" % (target, ports))    for port in ports:        udp_scan_resp = sr1(            IP(dst=target) / UDP(dport=port), timeout=5, verbose=0)        if str(type(udp_scan_resp)) == "":            print_ports(port, "open|filtered")        elif udp_scan_resp.haslayer(UDP):            if udp_scan_resp.getlayer(UDP).flags == "R":                print_ports(port, "open")            else:                print_ports(port, "unkonwn")        elif udp_scan_resp.haslayer(ICMP):            if int(udp_scan_resp.getlayer(ICMP).type) == 3 and int(udp_scan_resp.getlayer(ICMP).code) in [1, 2, 9, 10, 13]:                print_ports(port, "filtered")            elif int(udp_scan_resp.getlayer(ICMP).type) == 3 and int(udp_scan_resp.getlayer(ICMP).code) == 3:                print_ports(port, "close")            else:                print_ports(port, "unkonwn")        else:            print_ports(port, "unkonwn")
ef21921e9480cbd4ee55f5a98d32f400.png

测试

95f9533054f03109f742429ea47be5b4.png

9430c04c3e99073643ed79cc94a18c71.png

ef21921e9480cbd4ee55f5a98d32f400.png

尾声

95f9533054f03109f742429ea47be5b4.png

我是匠心,一个在清流旁默默磨剑的匠人,期待那一天能利刃出鞘,仗剑走江湖。

763f781d5ef860741ed6a016e8b0ed0e.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值