端口扫描工具codenomicon_c端口扫描器源码

hacker|
130

如何用Scapy写一个端口扫描器

常见的端口扫描类型有:

1. TCP 连接扫描

2. TCP SYN 扫描(也称为半开放扫描或stealth扫描)

3. TCP 圣诞树(Xmas Tree)扫描

4. TCP FIN 扫描

5. TCP 空扫描(Null)

6. TCP ACK 扫描

7. TCP 窗口扫描

8. UDP 扫描

下面先讲解每种扫描的原理,随后提供具体实现代码。

TCP 连接扫描

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

客户端想要连接服务器80端口时,会先发送一个带有 SYN 标识和端口号的 TCP 数据包给服务器(本例中为80端口)。如果端口是开放的,则服务器会接受这个连接并返回一个带有 SYN 和 ACK 标识的数据包给客户端。随后客户端会返回带有 ACK 和 RST 标识的数据包,此时客户端与服务器建立了连接。如果完成一次三次握手,那么服务器上对应的端口肯定就是开放的。

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

代码:

#! /usr/bin/python

import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

dst_ip = "10.0.0.1"

src_port = RandShort()

dst_port=80

tcp_connect_scan_resp = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="S"),timeout=10)

if(str(type(tcp_connect_scan_resp))=="type 'NoneType'"):

print "Closed"

elif(tcp_connect_scan_resp.haslayer(TCP)):

if(tcp_connect_scan_resp.getlayer(TCP).flags == 0x12):

send_rst = sr(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="AR"),timeout=10)

print "Open"

elif (tcp_connect_scan_resp.getlayer(TCP).flags == 0x14):

print "Closed"

TCP SYN 扫描

这个技术同 TCP 连接扫描非常相似。同样是客户端向服务器发送一个带有 SYN 标识和端口号的数据包,如果目标端口开发,则会返回带有 SYN 和 ACK 标识的 TCP 数据包。但是,这时客户端不会返回 RST+ACK 而是返回一个只带有 RST 标识的数据包。这种技术主要用于躲避防火墙的检测。

如果目标端口处于关闭状态,那么同之前一样,服务器会返回一个 RST 数据包。

代码:

#! /usr/bin/python

import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

dst_ip = "10.0.0.1"

src_port = RandShort()

dst_port=80

stealth_scan_resp = sr1(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="S"),timeout=10)

if(str(type(stealth_scan_resp))=="type 'NoneType'"):

print "Filtered"

elif(stealth_scan_resp.haslayer(TCP)):

if(stealth_scan_resp.getlayer(TCP).flags == 0x12):

send_rst = sr(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="R"),timeout=10)

print "Open"

elif (stealth_scan_resp.getlayer(TCP).flags == 0x14):

print "Closed"

elif(stealth_scan_resp.haslayer(ICMP)):

if(int(stealth_scan_resp.getlayer(ICMP).type)==3 and int(stealth_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):

print "Filtered"

TCP 圣诞树(Xmas Tree)扫描

在圣诞树扫描中,客户端会向服务器发送带有 PSH,FIN,URG 标识和端口号的数据包给服务器。如果目标端口是开放的,那么不会有任何来自服务器的回应。

如果服务器返回了一个带有 RST 标识的 TCP 数据包,那么说明端口处于关闭状态。

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

代码:

#! /usr/bin/python

import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

dst_ip = "10.0.0.1"

src_port = RandShort()

dst_port=80

xmas_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="FPU"),timeout=10)

if (str(type(xmas_scan_resp))=="type 'NoneType'"):

print "Open|Filtered"

elif(xmas_scan_resp.haslayer(TCP)):

if(xmas_scan_resp.getlayer(TCP).flags == 0x14):

print "Closed"

elif(xmas_scan_resp.haslayer(ICMP)):

if(int(xmas_scan_resp.getlayer(ICMP).type)==3 and int(xmas_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):

print "Filtered"

TCP FIN扫描

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

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

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

代码:

#! /usr/bin/python

import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

dst_ip = "10.0.0.1"

src_port = RandShort()

dst_port=80

fin_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="F"),timeout=10)

if (str(type(fin_scan_resp))=="type 'NoneType'"):

print "Open|Filtered"

elif(fin_scan_resp.haslayer(TCP)):

if(fin_scan_resp.getlayer(TCP).flags == 0x14):

print "Closed"

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 "Filtered"

TCP 空扫描(Null)

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

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

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

代码:

#! /usr/bin/python

import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

dst_ip = "10.0.0.1"

src_port = RandShort()

dst_port=80

null_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags=""),timeout=10)

if (str(type(null_scan_resp))=="type 'NoneType'"):

print "Open|Filtered"

elif(null_scan_resp.haslayer(TCP)):

if(null_scan_resp.getlayer(TCP).flags == 0x14):

print "Closed"

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 "Filtered"

TCP ACK扫描

ACK 扫描不是用于发现端口开启或关闭状态的,而是用于发现服务器上是否存在有状态防火墙的。它的结果只能说明端口是否被过滤。再次强调,ACK 扫描不能发现端口是否处于开启或关闭状态。

客户端会发送一个带有 ACK 标识和端口号的数据包给服务器。如果服务器返回一个带有 RST 标识的 TCP 数据包,则说明端口没有被过滤,不存在状态防火墙。

如果目标服务器没有任何回应或者返回ICMP 错误类型3且代码为1,2,3,9,10或13的数据包,则说明端口被过滤且存在状态防火墙。

#! /usr/bin/python

import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

dst_ip = "10.0.0.1"

src_port = RandShort()

dst_port=80

ack_flag_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="A"),timeout=10)

if (str(type(ack_flag_scan_resp))=="type 'NoneType'"):

print "Stateful firewall presentn(Filtered)"

elif(ack_flag_scan_resp.haslayer(TCP)):

if(ack_flag_scan_resp.getlayer(TCP).flags == 0x4):

print "No firewalln(Unfiltered)"

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 "Stateful firewall presentn(Filtered)"

TCP窗口扫描

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

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

代码:

#! /usr/bin/python

import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

dst_ip = "10.0.0.1"

src_port = RandShort()

dst_port=80

window_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port,flags="A"),timeout=10)

if (str(type(window_scan_resp))=="type 'NoneType'"):

print "No response"

elif(window_scan_resp.haslayer(TCP)):

if(window_scan_resp.getlayer(TCP).window == 0):

print "Closed"

elif(window_scan_resp.getlayer(TCP).window 0):

print "Open"

UDP扫描

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

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

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

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

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

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

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

代码:

#! /usr/bin/python

import logging

logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

from scapy.all import *

dst_ip = "10.0.0.1"

src_port = RandShort()

dst_port=53

dst_timeout=10

def udp_scan(dst_ip,dst_port,dst_timeout):

udp_scan_resp = sr1(IP(dst=dst_ip)/UDP(dport=dst_port),timeout=dst_timeout)

if (str(type(udp_scan_resp))=="type 'NoneType'"):

retrans = []

for count in range(0,3):

retrans.append(sr1(IP(dst=dst_ip)/UDP(dport=dst_port),timeout=dst_timeout))

for item in retrans:

if (str(type(item))!="type 'NoneType'"):

udp_scan(dst_ip,dst_port,dst_timeout)

return "Open|Filtered"

elif (udp_scan_resp.haslayer(UDP)):

return "Open"

elif(udp_scan_resp.haslayer(ICMP)):

if(int(udp_scan_resp.getlayer(ICMP).type)==3 and int(udp_scan_resp.getlayer(ICMP).code)==3):

return "Closed"

elif(int(udp_scan_resp.getlayer(ICMP).type)==3 and int(udp_scan_resp.getlayer(ICMP).code) in [1,2,9,10,13]):

return "Filtered"

print udp_scan(dst_ip,dst_port,dst_timeout)

下面解释下上述代码中的一些函数和变量:

RandShort():产生随机数

type():获取数据类型

sport:源端口号

dport:目标端口号

timeout:等待相应的时间

haslayer():查找指定层:TCP或UDP或ICMP

getlayer():获取指定层:TCP或UDP或ICMP

以上扫描的概念可以被用于“多端口扫描”,源码可以参考这里:

Scapy 是一个非常好用的工具,使用它可以非常简单的构建自己的数据包,还可以很轻易的处理数据包的发送和相应。

(译者注:上述所有代码均在Kali 2.0下测试通过,建议读者在Linux环境下测试代码,如想在Windows上测试,请参见 Scapy官方文档 配置好scapy环境)

Python 实现端口扫描

一、常见端口扫描的原理

0、秘密扫描

秘密扫描是一种不被审计工具所检测的扫描技术。

它通常用于在通过普通的防火墙或路由器的筛选(filtering)时隐藏自己。

秘密扫描能躲避IDS、防火墙、包过滤器和日志审计,从而获取目标端口的开放或关闭的信息。由于没有包含TCP 3次握手协议的任何部分,所以无法被记录下来,比半连接扫描更为隐蔽。

但是这种扫描的缺点是扫描结果的不可靠性会增加,而且扫描主机也需要自己构造IP包。现有的秘密扫描有TCP FIN扫描、TCP ACK扫描、NULL扫描、XMAS扫描和SYN/ACK扫描等。

1、Connect()扫描

此扫描试图与每一个TCP端口进行“三次握手”通信。如果能够成功建立接连,则证明端口开发,否则为关闭。准确度很高,但是最容易被防火墙和IDS检测到,并且在目标主机的日志中会记录大量的连接请求以及错误信息。

TCP connect端口扫描服务端与客户端建立连接成功(目标端口开放)的过程:

① Client端发送SYN;

② Server端返回SYN/ACK,表明端口开放;

③ Client端返回ACK,表明连接已建立;

④ Client端主动断开连接。

建立连接成功(目标端口开放)

TCP connect端口扫描服务端与客户端未建立连接成功(目标端口关闭)过程:

① Client端发送SYN;

② Server端返回RST/ACK,表明端口未开放。

优点:实现简单,对操作者的权限没有严格要求(有些类型的端口扫描需要操作者具有root权限),系统中的任何用户都有权力使用这个调用,而且如果想要得到从目标端口返回banners信息,也只能采用这一 *** 。

另一优点是扫描速度快。如果对每个目标端口以线性的方式,使用单独的connect()调用,可以通过同时打开多个套接字,从而加速扫描。

缺点:是会在目标主机的日志记录中留下痕迹,易被发现,并且数据包会被过滤掉。目标主机的logs文件会显示一连串的连接和连接出错的服务信息,并且能很快地使它关闭。

2、SYN扫描

扫描器向目标主机的一个端口发送请求连接的SYN包,扫描器在收到SYN/ACK后,不是发送的ACK应答而是发送RST包请求断开连接。这样,三次握手就没有完成,无法建立正常的TCP连接,因此,这次扫描就不会被记录到系统日志中。这种扫描技术一般不会在目标主机上留下扫描痕迹。但是,这种扫描需要有root权限。

·端口开放:(1)Client发送SYN;(2)Server端发送SYN/ACK;(3)Client发送RST断开(只需要前两步就可以判断端口开放)

·端口关闭:(1)Client发送SYN;(2)Server端回复RST(表示端口关闭)

优点:SYN扫描要比TCP Connect()扫描隐蔽一些,SYN仅仅需要发送初始的SYN数据包给目标主机,如果端口开放,则相应SYN-ACK数据包;如果关闭,则响应RST数据包;

3、NULL扫描

反向扫描—-原理是将一个没有设置任何标志位的数据包发送给TCP端口,在正常的通信中至少要设置一个标志位,根据FRC 793的要求,在端口关闭的情况下,若收到一个没有设置标志位的数据字段,那么主机应该舍弃这个分段,并发送一个RST数据包,否则不会响应发起扫描的客户端计算机。也就是说,如果TCP端口处于关闭则响应一个RST数据包,若处于开放则无相应。但是应该知道理由NULL扫描要求所有的主机都符合RFC 793规定,但是windows系统主机不遵从RFC 793标准,且只要收到没有设置任何标志位的数据包时,不管端口是处于开放还是关闭都响应一个RST数据包。但是基于Unix(*nix,如Linux)遵从RFC 793标准,所以可以用NULL扫描。 经过上面的分析,我们知道NULL可以辨别某台主机运行的操作系统是什么操作系统。

端口开放:Client发送Null,server没有响应

端口关闭:(1)Client发送NUll;(2)Server回复RST

说明:Null扫描和前面的TCP Connect()和SYN的判断条件正好相反。在前两种扫描中,有响应数据包的表示端口开放,但在NUll扫描中,收到响应数据包表示端口关闭。反向扫描比前两种隐蔽性高些,当精确度也相对低一些。

用途:判断是否为Windows系统还是Linux。

4、FIN扫描

与NULL有点类似,只是FIN为指示TCP会话结束,在FIN扫描中一个设置了FIN位的数据包被发送后,若响应RST数据包,则表示端口关闭,没有响应则表示开放。此类扫描同样不能准确判断windows系统上端口开 *** 况。

·端口开放:发送FIN,没有响应

·端口关闭:(1)发送FIN;(2)回复RST

5、ACK扫描

扫描主机向目标主机发送ACK数据包。根据返回的RST数据包有两种 *** 可以得到端口的信息。 *** 一是: 若返回的RST数据包的TTL值小于或等于64,则端口开放,反之端口关闭。

6、Xmas-Tree扫描

通过发送带有下列标志位的tcp数据包。

·URG:指示数据时紧急数据,应立即处理。

·PSH:强制将数据压入缓冲区。

·FIN:在结束TCP会话时使用。

正常情况下,三个标志位不能被同时设置,但在此种扫描中可以用来判断哪些端口关闭还是开放,与上面的反向扫描情况相同,依然不能判断windows平台上的端口。

·端口开放:发送URG/PSH/FIN,没有响应

·端口关闭:(1)发送URG/PSH/FIN,没有响应;(2)响应RST

XMAS扫描原理和NULL扫描的类似,将TCP数据包中的ACK、FIN、RST、SYN、URG、PSH标志位置1后发送给目标主机。在目标端口开放的情况下,目标主机将不返回任何信息。

7、Dump扫描

也被称为Idle扫描或反向扫描,在扫描主机时应用了第三方僵尸计算机扫描。由僵尸主机向目标主机发送SYN包。目标主机端口开发时回应SYN|ACK,关闭时返回RST,僵尸主机对SYN|ACK回应RST,对RST不做回应。从僵尸主机上进行扫描时,进行的是一个从本地计算机到僵尸主机的、连续的ping操作。查看僵尸主机返回的Echo响应的ID字段,能确定目标主机上哪些端口是开放的还是关闭的。

二、Python 代码实现

1、利用Python的Socket包中的connect *** ,直接对目标IP和端口进行连接并且尝试返回结果,而无需自己构建SYN包。

2、对IP端口进行多线程扫描,注意的是不同的电脑不同的CPU每次最多创建的线程是不一样的,如果创建过多可能会报错,需要根据自己电脑情况修改每次扫描的个数或者将seelp的时间加长都可以。

看完了吗?感觉动手操作一下把!

python学习网,免费的在线学习python平台,欢迎关注!

本文转自:

linux下用C写的一个端口扫描器,想得到扫描主机的操作系统类型

nmap 命令行

zenmap 图形化界面

一般能扫描出主机的操作系统版本

0条大神的评论

发表评论