选择扫描目标的语法如下:
对目标的端口进行扫描:
对目标端口状态进行扫描:
对目标操作系统和运行服务进行扫描:
ARP的中文名字是“地址解析协议”,主要用在以太网中。需要明确的是,所有的主机在互联网中通信的时候使用的是IP地址,而在以太网中通信时使用的却是硬件地址(MAC地址)。
现在来编写一个利用ARP实现的活跃主机扫描程序,这个程序有很多方式可以实现,首先借助Scapy库来完成。其核心思想就是要产生一个ARP请求,首先产看Scapy库中ARP类型数据包中需要的参数。
这里面的大多数参数都有默认值,其中,hwsrc和psrc分别是源硬件地址和源IP地址。这两个地址不用设置,发送的时候会自动填写本机的地址。唯一需要设置的是目的地址pdst,将这个地址设置为目标即可。
另外,因为发送的是广播数据包,所以需要在Ether层进行设置,首先查看一下Ether的格式
这一层只有三个参数:dst是目的硬件地址,src是源硬件地址,这里面src会自动设置为本机地址。所以只需要将dst设置为ff:ff:ff:ff:ff:ff即可将数据包发到网络中的各个主机上。
下面构造一个扫描192.168.26.100的ARP请求数据包并将其发送出去
ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.26.100"),timeout=2)
对这个请求的回应进行监听,如果得到回应,那么证明目标在线,并打印输出这个主机的硬件地址。
ans,unans=srp(Et.summary(lambda(s,r):r.sprintf("%Ether.src% %ARP.psrc%"))
#lambda:匿名函数,括号里面我们要传递的参数,冒号后面的数据相当于return返回一个结果
ICMP位于TCP/IP协议族中的网络层,它的目的是用于在IP主机、路由器之间传递控制消息。
ICMP中提供了多种报文,这些报文又可以分成两大类:差错报文和查询报文。其中,查询报文都是一个由请求和一个应答构成的。
ICMP查询报文有4中,分别是相应请求或应答、时间戳请求或应答、地址掩码请求或应答、路由器询问或应答。Ping命令就是相应请求或应答的一种应用,我们经常会使用这个命令来测试本地和目标之间的连通性。
例如我们所在主机IP为192.168.1.1,而通信的目标地址为192.168.1.2,如果要判断192.168.1.2是否为活跃主机,就需要向其发送一个ICMP请求,这个请求的格式如下:
IP层内容:源IP:192.168.1.1,目标IP:192.168.1.2
ICMP层内容:Type:8(表示请求)/0(表示应答)
编写一个利用ICMP实现的活跃主机扫描程序,借助Scapy库来完成。
其核心思想就是产生一个ICMP请求,首先查看Scapy库中ICMP类型数据包中需要的参数。
这一层和地址有关的参数有两个:dst是目的IP地址,src是源IP地址。
src会自动设置为本机地址。所以只需要将dst设置为“192.168.26.100”即可将数据包发到目标主机上。接下来构造一个扫描192.168.26.100的ICMP请求数据包并将其发送出去。
ans,unans=sr(IP(dst="192.168.26.100")/ICMP())
对这个请求的回应进行监听,如果得到了回应,那么证明目标在线,并打印输出这个主机的IP地址。
ans.summary(lambda (s,r) : r.sprintf("%IP.src% is alive"))
TCP是一个位于传输层的协议。它是一中面向连接的、可靠的、基于字节流的传输层通信协议。
TCP的特点是使用三次握手协议建立连接。当主动放发出SYN连接请求后,等待对方的回答TCP的三次握手SYN+ACK,并对最终对对方的SYN执行ACK确认。这种建立连接的方法可以防止产生错误的连接。
如果检测到一台主机的某个端口有回应,也一样可以判断这台主机是活跃的。如果一台主机处于活跃状态,那么它的端口即使是关闭的,在收到请求时,也会给出一个回应,只不过并不是一个“SYN+ACK”数据包,而是一个拒绝连接的“RST”数据包。
编写一个利用TCP实现的活跃主机扫描程序,这个程序有很多种方式可以实现,首先借助Scapy库来完成。
首先查看Scapy库中TCP类型数据包中需要的参数
这里的大多数参数都不需要设置,需要考虑的是sport、dport和flags。
sport是源端口,dport是目的端口,flags是标志位,可能的值包括SYN(建立连接)、FIN(关闭连接)、ACK(响应)、PSH(DATA数据传输)、RST(连接重置)。此处将flags设置为“S”,也就是SYN。另外TCP并没有目标地址和源地址,需要在IP层进行设置。
ans,unans=sr(IP(dst="192.168.26.100")/ITCP(dport=80,flags="S"))
ans.summary(lambda(s,r):r.sprintf("%IP.src% is alive"))
UDP全称是用户数据报协议,在网络中它与TCP一样用于处理数据包,它是一种无连接的协议。在OSI模型中位于第4层——传输层。
但基于UDP的活跃主机发现技术和TCP不同,UDP没有三次握手。
当向目标发送一个UDP数据包之后,目标是不会发回任何UDP数据包的。不过,如果目标主机是处于活跃状态的,但是目标端口是关闭的时候,可以返回一个ICMP数据包,这个数据包的含义为“unreachable”,如果目标主机不处于活跃状态,这时是收不到任何回应的。
构造一个发往192.168.26.100的6677端口的UDP数据包并将其发送出去。
ans,unans=sr(IP(dst="192.168.26.100")/UDP(dport-6777))
对于这个请求的回应进行监听,如果得到了回应,当然这个回应是ICMP类型的,就证明目标在线,并打印输出这个主机的IP地址
ans.summary(lambda(s,r):r.sprintf("%IP.src% is alive"))
TCP全开扫描:如果目标端口是开放的,那么在接到主机端口发出的SYN请求之后,就会返回一个SYN+ACK回应,表示愿意接受这次连接的请求,然后主机端口再回应一个ACK,这样就成功地和目标端口建立了一个TCP连接。
如果端口是关闭的,那么在接到主机端口发出的SYN请求之后,就会返回一个RST回应,表示不接受这次连接的请求,这样就中断了这次TCP连接。
目标端口不开放还有另外一种情况,就是当主机端口发出SYN请求之后,没有收到任何的回应。多种原因都可能造成这种情况,例如,目标主机处于非活跃状态,这时当然无法进行回应,不过这也可以认为端口是关闭的。另外一些网络安全设备也会屏蔽掉对某些端口的SYN请求,这时也会出现无法进行回应的情况。
利用Scapy模块构造一个TCP,SYN数据包。
packet = IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="S")
然后使用sr1函数将这个数据包发送出去
resp=sr1(packet,timeout=10)
要根据收到应答包来判断目标端口的状态,这时会出现三种情况:
resp.getlayer(TCP).flags==0x12 #12就是SYN+ACK
来判断回应数据包是否为“SYN+ACK”。如果结果为真,表示目标接受TCP请求,需要继续发送一个ACK数据包过去,完成三次握手:IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="AR")
resp.getlayer(TCP).flags==0x14
import sys
from scapy.all import *
if len(sys.argv)!=3:
print('Usage:PortScan\n eg:PortScan 192.168.1.1 80' )
sys.exit(1)
dst_ip=sys.argv[1]
src_port=RandShort()#产生随机端口
dst_port=int(sys.argv[2])
packet=IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='S')
resp=sr1(packet,timeout=10)
if(str(type(resp))=="" ):
print("The port %s Closed" % dst_port)
elif(resp.haslayer(TCP)):
if(resp.getlayer(TCP).flags==0x12):
send_rst=sr(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="A",timeout=10)
print("The port %s is Open" % dst_port)
elif(resp.getlayer(TCP).flags==0x14):
print("The port %s is Closed" % dst_port)
注释:if len(sys.argv)!=3——len:表示长度 判断sys.argv有多少个元素 (sys.argv是一个列表[“文本文件路径,“192.168.1.1”,“80”]) ,当我们运行py文件时,在列表中上传了一个文件路径信息,所以是三个参数。
基于TCP全开的端口扫描技术还有一些不完善的地方。例如,这次连接可能会被目标主机的日志记录下来,而且最为重要的是建立TCP连接三次握手中的最后一次是没用的,在目标返回一个SYN+ACK类型的数据包之后,已经达到了探测的目的,最后发送的ACK类型数据包是不必要的,所以可以考虑去除这一步。
扫描思想: 如果目标端口是开放的,那么在接到主机端口发出的SYN请求之后,就会返回一个SYN+ACK回应,表示愿意接受这次连接的请求,然后主机端口不再回应一个ACK,而是发送一个RST表示中断这个连接。这样实际上并没有建立好完整的TCP连接,所以称为半开连接。如果端口关闭,半开扫描和全开扫描没有区别。
半开扫描实例中,考虑一种更为复杂的情况,那么是目标端口的filtered状态。这种状态往往是由包过滤机制造成的,过滤可能来自专业的防火墙设备、路由器规则或者主机上的软件防火墙。这种情况下会让扫描工作变得很难,因为这些端口几乎不提供任何信息。不过有时候它们也会相应ICMP错误消息,但更多的时候包过滤机制不做出任何相应。
将没有收到回应数据包的端口归于“filtered”状态。
当TCP连接的数据包被屏蔽时,一般会返回如下所示的集中ICMP错误消息。
类型(TYPE) | 代码(CODE) | 意义 |
---|---|---|
3 | 1 | 主机不可达 |
3 | 2 | 协议不可达 |
3 | 3 | 端口不可达 |
3 | 9 | 目的网络被强制禁止 |
3 | 10 | 目的主机被强制禁止 |
3 | 13 | 由于过滤,通信被强制禁止 |
如果收到这集中类型的ICMP数据包,也将目标端口的状态归类到“filtered”。
所以在编程时,要考虑如下两种情况
if(str(type(stealth_scan_resp))=="" ):#表示没有收到任何的回应数据包
if(int(resp.getlayer(ICMP).type)==3 and int(resp.getlayer(ICMP).code) in[1,2,3,9,10,13]):#表示收到的是ICMP类型数据包。
import sys
from scapy.all import *
if len(sys.argv)!=3:
print('Usage:PortScan\n eg:PortScan 192.168.1.1 80' )
sys.exit(1)
dst_ip=sys.argv[1]
src_port=RandShort()#产生随机端口
dst_port=int(sys.argv[2])
packet=IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags='S')
resp=sr1(packet,timeout=10)
if(str(type(resp))=="" ):
print("The port %s Closed" % dst_port)
elif(resp.haslayer(TCP)):
if(resp.getlayer(TCP).flags==0x12):
send_rst=sr(IP(dst=dst_ip)/TCP(sport=src_port,dport=dst_port,flags="R",timeout=10)
print("The port %s is Open" % dst_port)
elif(resp.getlayer(TCP).flags==0x14):
print("The port %s is Closed" % dst_port)
elif(resp.haslayer(ICMP)):
if(int(resp.getlayer(ICMP).type)==3 and int(resp.getlayer(ICMP).code) in[1,2,3,9,10,13]):
print("The port %s is Filtered" % dst_port)
平时所使用的软件除了操作系统之外,还有大量的其他应用程序。有些个人开发的应用软件大都存在着漏洞,它们更是网络安全的重灾区。
如果目标使用这些软件对外提供网络服务,攻击者就有可能利用这个网络服务的漏洞入侵。
因此,在对目标进行渗透测试时,要尽量地检测出目标系统运行的各种服务。对于入侵者来说,发现这些运行在目标上的服务,就可以利用这些软件上的漏洞入侵目标;对于网络安全的维护者来说,也可以提前发现系统的漏洞,从而预防这些入侵行为。
服务扫描思路:
常见的服务都会运行在指定的端口上。例如FTP服务上总会运行在21号端口上,而HTTP服务总会运行在80端口上。因为这些端口都是公知端口,所以只需要知道目标上哪个端口是开放的,就可以猜测出目标上运行着什么服务。
不过这样做有两个明显缺点:
解决方法:
首先引入需要的Socket库:import socket
然后初始化一个TCP类型的Socket:s=socket.socket()
使用这个Socket去连接127.0.0.1的21端口(测试本机):s.connect(("127.0.0.1",21))
连接成功之后,向目标发送任意的一段数据:s.send('1111111')
目标会将自己的banner作为应答信息返回:banner=s.recv(1024)
关闭这个连接:s.close()
打印输出得到bannner:print('Banner:{}'.format(banner))
代码如下
import sys
import socket
if len(sys.argv)!=3:
print('Usage:PortScan\n eg:PortScan 192.168.1.1 80' )
sys.exit(1)
target=sys.argv[1]
port=int(sys.argv[2])
s=socket.socket()
res=s.connect((target,port))
s.send('111111')
service=s.recv(1024)
s.close()
print("Port in {}".format(port)+"Service:{}".format(service))
执行
扫描过程很简单,核心语句变成了nm.scan(target,port,“-sV”),关键是对扫描结果的处理。
import sys
import nmap
if len(sys.argv)!=3:
print('Usage:PortScan\n eg:PortScan 192.168.1.1 80' )
sys.exit(1)
target=sys.argv[1]
port=sys.argv[2]
nm=nmap.PortScanner()
nm.scan(target,port,"-sV")
for host in nm.all_hosts():
for proto in nm[host].all_protocols():
lport=nm[host][proto].keys()
lport.sort()
for port in lport:
print("port : %s\t product : %s" % (port,nm[host][proto][port]['product']))
很多著名的工具都提供了远程对操作系统进行检测的功能。这一点用在入侵上就可以称为黑客的工具,而用在网络管理上就可以进行资产管理和操作系统补丁管理。
但是并没有一种工具可以提供绝对准确的远程操作系统信息。几乎所有的工具都使用了一种“猜”的方法。当然这不是凭空猜测。
目前远程对操作系统继续宁检测的方法一般可以分成以下两类:
p0f就是一款典型的被动式扫描工具。p0f可以自动地捕获网络中通信的数据包,并对其进行分析,使用的方法很简单,可以在命令行中直接输入“p0f”
然后,打开浏览器访问如:web服务器,就会产生如下结果
采用向目标发送数据包的方式来检测,但是这需要设计一系列的探针式数据包,并将各种操作系统的反应保存为一个数据库。
使用nmap库文件来编写一个主动式扫描程序,首先还是命令行中来实现这个程序
首先导入Nmap库:import nmap
然后床架你要给PortScanner对象出来:nm=nmap.PortScanner()
对目标主机进行扫描,扫描的参数为“-O”:nm.scan('192.168.0.104','-o')
扫描结果如下:
这个扫描的结果看起来有点乱,可以使用osmatch
osmatch是一个字典类型,用来存储操作系统的匹配信息,它包括’accuracy’ ‘line’ ‘osclass’三个键,而’osclass‘中包含关键信息,它本身也是一个字典类型,其中包含’accuracy’(匹配度)、‘cpe’(通用平台枚举)、‘osfamily’(系统类别)、‘osgen’(第几代操作系统)、‘type’(设备类型)、‘vendor’(生产厂家)6个键。
下面是一个使用Nmap库编写完整程序
import nmap
import sys
if len(sys.argv)!=2:
print('Usage:OSsacn \n eg: OSscan 192.168.1.1' )
sys.exit(1)
target=sys.argv[1]
nm=nmap.PortScanner()
nm.scan(target,arguments="-O")
if 'osmatch' in nm[target]:
for osmatch in nm[target]['osmatch']:
print('OsMatch.name:{}'.format(osmatch['name']))
print('OsMatch.accuracy:{}'.format(osmatch['accuracy']))#匹配度
print('OsMatch.line:{}'.format(osmatch['line']))
print('')
if 'osclass' in osmatch:
for osclass in osmatch['osclass']:
print('OsMatch.type:{}'.format(osmatch['type']))
print('OsMatch.vendor:{}'.format(osmatch['vendor']))
print('OsMatch.osfamily:{}'.format(osmatch['osfamily']))
print('OsMatch.osgen:{}'.format(osmatch['osgen']))
print('OsMatch.accuracy:{}'.format(osmatch['accuracy']))
print('')