主动信息搜集

 

 

在内网中,好的信息搜集能力能够帮助开发者更快地拿到权限及达成目标。 内网里数据多种多样,需要根据需求寻找任何能对下一步渗透行动有所帮助的信 息。信息搜集能力是渗透过程中不可或缺的重要一步。下面将介绍各类搜集信息 的方法。

 

 

 

 

 

 

4.2.1    基于ICMP的主机发现

ICMP(Internet Control Message Protocol ,Internet报文协议)是TCP/IP的一种 子协议,位于OSI 7层网络模型中的网络层,其目的是用于在IP主机、路由器之间 传递控制消息。

1.ICMP工作流程

ICMP中提供了多种报文,这些报文又可分成两大类:“差错通知”和“信息查 询”。

(1)差错通知

当IP数据包在对方计算机处理过程中出现未知的发送错误时,ICMP会向发送 者传送错误事实以及错误原因等,如图4-1所示。

57965d84988048268c831a38a702986c.png

图4-1    差错通知

(2)信息查询

信息查询由一个请求和一个应答构成的。只需要向目标发送一个请求数据

包,如果收到了来自目标的回应,就可以判断目标是活跃主机,否则可以判断目 标是非活跃主机,如图4-2所示。

 

 

 

 

 

55e335b57d8e48278183f414fef878e2.png

图4-2    信息查询

2.ICMP主机探测过程

Ping命令是ICMP中较为常见的一种应用,经常使用这个命令来测试本地与目 标之间的连通性,发送一个ICMP请求消息给目标主机,若源主机收到目标主机的 应答响应消息,则表示目标可达,主机存在。例如,我们所在的主机IP地址为

192.168.124.134 ,而通信的目标IP地址为192.168.124.5 。如果要判断192.168.124.5 是否为活跃主机,只需要向其发送一个ICMP请求,如果192.168.124.5这台主机处 于活跃状态,那么它在收到这个请求之后,就会给出一个回应,如下所示:

702c234509474a8f83208b4c3474f19a.png

现在来编写一个利用ICMP实现探测活跃主机的代码程序。程序有很多种可实 现方式,此处我们借助Scapy库来完成。Scapy是Python中一个第三方库,在Scapy  库内部已经实现了大量的网络协议,例如TCP 、UDP 、IP 、ARP等,使用Scapy可 以灵活地编写各种网络工具。

首先我们对Scapy进行安装,命令如下:

 

 

 

 

 

 

 

安装过程如下所示:

77528eb411c149cfba9e6f0fd68e40c2.png

接下来,我们可以利用Scapy库函数中的ICMP实现探测主机存活,详细过程 如下所示。

1)导入程序代码所应用到的模块:scapy 、random 、optparse ,其中scapy用  于发送ping请求和接收目标主机的应答数据,random用于产生随机字段,optparse 用于生成命行参数形式。示例如下:

#!/usr/bin/python

#coding:utf-8

from scapy .all import *

from random import randin t

from optparse import OptionParser

2)对用户输入的参数进行接收和批量处理,并将处理后的IP地址传入Scan函 数。

def main() :

parser = OptionParser("Usage:%prog -i <target host> ")   # 输出帮助信息

parser .add_option( '-i ',type= 'string ',dest= 'IP ',help= 'specify target host ') # 获取IP地址参数

options,args = parser .parse_args()

print("Scan report for " + options .IP + "\n")

# 判断是单台主机还是多台主机

# IP中存在-,说明是要扫描多台主机

if '- ' in options .IP:

# 代码举例:192 .168 .1 .1-120

# 通过“ -”进行分隔,把192 .168 .1 .1和120分开

# 把192.168.1.1通过“,”进行分隔,取最后一个数作为range函数的start,然后把120+

1作为range函数的stop

# 这样循环遍历出需要扫描的IP地址

for i in range(in t(options .IP .split( '- ')[0] .split( ' . ')[3]), in t

(options .IP .split( '- ')[1]) + 1) :

Scan(

options .IP .split( ' . ')[0] + ' . ' + options .IP .split( ' . ')[1] + ' . ' + options .IP .split( ' . ')[2] + ' . ' + str(i))

time .sleep(0 .2)

 

 

 

 

 

else:

Scan(options .IP)

print("\nScan finished!. . .\n")

if __name__ == "__main__" :

try:

main()

except Keyboard Interrupt :

print("interrupted by user, killing all threads . . .")

 

3)Scan函数通过调用ICMP ,将构造好的请求包发送到目的地址,并根据目 的地址的应答数据判断目标主机是否存活。存活的IP地址会打印

出“xx.xx.xx.xx →Host is up” ,对于不存活的主机打印出“xx.xx.xx.xx →Host is down”:

icmp_id = randin t(1, 65535)

icmp_seq = randin t(1, 65535)

packet=IP(dst=ip,ttl=64,id=ip_id)/ICMP(id=icmp_id,seq=icmp_seq)/b '

rootkit '

result = sr1(packet, timeout=1, verbose=False)

if result :

for rcv in result :

scan_ip = rcv[IP] .src

print(scan_ip + '---> ' 'Host is up ')

else:

print(ip + '---> ' 'host is down ')

运行效果如下所示。

7fbde1b517e44e4480250bd139fa3552.png

此处,我们也可以在程序中导入Nmap库函数,实现探测主机存活工具的编  写。这里使用Nmap函数的-sn与-PE参数,-PE表示使用ICMP ,-sn表示只测试该主 机的状态,具体步骤如下所示。

1)导入程序代码所应用到的模块:nmap 、optparse 。nmap模块用于产生 ICMP的请求数据包,optparse用于生成命令行参数。

#!/usr/bin/python3

# -*- coding: utf-8 -*

 

 

 

 

 

import nmap

import optparse

 

2)利用optparse模块生成命令行参数化形式,对用户输入的参数进行接收和 批量处理,最后将处理后的IP地址传入NmapScan函数。

if __name__ == '__main__ ' :

parser = optparse .OptionParser( 'usage: python %prog -i ip \n\n '

'Example: python %prog -i 192 .168 .1 .1 [192.168.1.1-100]\n')

# 添加目标IP参数-i

parser.add_option('-i','--ip',dest='targetIP',default='192.168.1.1 ',type='string', help= 'target ip address ')

options,args = parser .parse_args()

# 判断是单台主机还是多台主机

# IP中存在“ -”,说明是要扫描多台主机

if '- ' in options .target IP:

# 代码举例:192 .168 .1 .1-120

# 通过 '- '进行分割,把192 .168 .1 .1和120进行分离

# 把192.168.1.1通过“,”进行分隔,取最后一个数作为range函数的start,然后把 120+1作为range函数的stop

# 这样循环遍历出需要扫描的IP地址

for i in range(in t(options .target IP .split( '- ')[0] .split( ' . ')[2]),in t (options .target IP .split( '- ')[1])+1) :

NmapScan(options .target IP .split( ' . ')[0] + ' . ' + options .target IP . split( ' . ')[1] + ' . ' + options .target IP .split( ' . ')[2] + ' . ' +  str(i))

else:

NmapScan(options .target IP)

3)NmapScan函数通过调用nm.scan() 函数,传入-sn-PE参数,发起ping扫 描,并打印出扫描后的结果。

def NmapScan(target IP) :

# 实例化PortScanner对象

nm = nmap .PortScanner()

try:

# hosts为目标IP地址,argusments为Nmap的扫描参数

# -sn:使用ping进行扫描

# -PE:使用ICMP的 echo请求包(-PP:使用timestamp请求包 -PM:netmask请求包) result = nm .scan(hosts=target IP, arguments= '-sn -PE ')

# 对结果进行切片,提取主机状态信息

state = result[ 'scan '][target IP][ 'status '][ 'state ']

print("[{}] is [{}]" .format(target IP, state))

except Exception  as e:

pass

运行效果如下所示。

1195a21a25f94abfaff0d8edb9245074.png

基于ICMP的探测主机存活是一种很常见的方法,无论是以太网还是互联网都

 

 

 

 

 

可以使用这种方法。但是该方法也存在一定的缺陷,就是当网络设备,例如路由 器、防火墙等对ICMP采取了屏蔽策略时,就会导致扫描结果不准确。

 

 

 

 

 

4.2.2    基于TCP 、UDP的主机发现

 

基于TCP 、UDP的主机发现属于四层主机发现,是一个位于传输层的协议。 可以用来探测远程主机存活、端口开放、服务类型以及系统类型等信息,相比于 三层主机发现更为可靠,用途更广。

TCP是一种面向连接的、可靠的传输通信协议,位于IP层之上,应用层之下 的中间层。每一次建立连接都基于三次握手通信,终止一个连接也需要经过四次 握手,建立完连接之后,才可以传输数据。当主动方发出SYN连接请求后,等待 对方回答TCP的三次握手SYN+ACK ,并最终对对方的SYN执行ACK确认。这种 建立连接的方法可以防止产生错误的连接,所以TCP是一个可靠的传输协议。

因此,我们可以利用TCP三次握手原理进行主机存活的探测。当向目标主机 直接发送ACK数据包时,如果目标主机存活,就会返回一个RST数据包以终止这 个不正常的TCP连接。也可以发送正常的SYN数据包,如果目标主机返回

SYN/ACK或者RST数据包,也可以证明目标主机为存活状态。其工作原理主要依 据目标主机响应数据包中flags字段,如果flags字段有值,则表示主机存活,该字 段通常包括SYN 、FIN 、ACK 、PSH 、RST 、URG六种类型。SYN表示建立连

接,FIN表示关闭连接,ACK表示应答,PSH表示包含DATA数据传输,RST表示 连接重置,URG表示紧急指针。

现在来编写一个利用TCP实现的活跃主机扫描程序,这个程序有很多种方式 可以实现,首先借助Scapy库来完成。在安装好Scapy的终端输入Scapy运行程序。 设定远程IP地址为39.xx.xx.238 ,flag标志为A表示给目标主机发送ACK应答数据  包,通过sr1() 函数将构造好的数据包发出。相关代码如下所示:

>>> ip=IP()

>>> tcp=TCP()

>>> r=(ip/tcp)

>>> r[IP] .dst="39 .xx .xx .238"

>>> r[TCP] .flags="A"

>>> a=sr1(r)

>>> a .display()

通过a.display() 函数查看目标主机的返回数据包信息,此时可以发现flags   标志位为R ,表示远程主机给源主机发送了一个REST 。由此可以验证远程目标主 机为存活状态。响应结果如下所示。

 

 

 

 

 

1)导入程序代码所应用到的模块:time 、optparse 、random和scapy 。time模  块主要用于产生延迟时间,optparse用于生成命行参数,random模块用于生成随机 的端口,scapy用于以TCP发送请求以及接收应答数据,例如:

 

import time

from optparse import OptionParser

from random import randin t

from scapy .all import *

2)利用optparse模块生成命令行参数化形式,对用户输入的参数进行接收和 批量处理,最后将处理后的IP地址传入Scan() 函数。

def main() :

usage = "Usage: %prog -i <ip address>"     # 输出帮助信息

parse = OptionParser(usage=usage)

parse .add_option("-i", '--ip ', type="string", dest="target IP", help=

"specify the IP address")          # 获取网段地址

options, args = parse .parse_args()          #实例化用户输入的参数

if '- ' in options .target IP:

# 代码举例:192 .168 .1 .1-120

# 通过“ -”进行分隔,把192 .168 .1 .1和120进行分离

# 把192.168.1.1通过“,”进行分隔,取最后一个数作为range函数的start,然后把 120+1作为range函数的stop

# 这样循环遍历出需要扫描的IP地址

 

 

 

 

 

for i in range(in t(options .target IP .split( '- ')[0] .split( ' . ')[3]), in t (options .target IP .split( '- ')[1]) + 1) :

Scan(options .target IP .split( ' . ')[0] + ' . ' + options .target IP .split  ( ' . ')[1] + ' . ' + options .target IP .split( ' . ')[2] + ' . ' + str(i))

else:

Scan(options .target IP)

if __name__ == '__main__ ' :

main()

 

3)Scan() 函数,通过调用TCP将构造好的请求包发送到目的地址,并根据 目的地址的响应数据包中flags字段值判断主机是否存活。若flags字段为R ,其整

型数值为4时表示接收到了目标主机的REST , 目标主机为存活状态,打印 出“xx.xx.xx.xx Host is up” ,否则为不存活主机,打印出“xx.xx.xx.xx Host is down”。

def Scan(ip) :

try:

dport = random .randin t(1, 65535)          #随机目的端口

packet = IP(dst=ip)/TCP(flags="A",dport=dport)     #构造标志位为ACK的数据包

response = sr1(packet,timeout=1 .0, verbose=0)

if response:

if in t(response[TCP] .flags) == 4:     #判断响应包中是否存在RST标志位

time .sleep(0 .5)

print(ip + ' ' + "is up")

else:

print(ip + ' ' + "is down")

else:

print(ip + ' ' + "is down")

except :

pass

运行效果如下所示。

a1bead67eae34ae595367a67676e8698.png

同时,可以打开Wireshark软件进行流量监听,根据抓到的数据流量可以分 析,源主机向目标主机发送ACK请求,当主机存活时, 目标主机会发送一个

REST的应答数据包,效果如图4-3所示。

 

fe7c399f7f9a41daa77a878ce16747f8.png

 

 

 

图4-3    向目标主机发送ACK请求时的监听效果

UDP(User Datagram Protocol ,用户数据报协议)是一种利用IP提供面向无  连接的网络通信服务。UDP会把应用程序发来的数据,在收到的一刻立即原样发 送到网络上。即使在网络传输过程中出现丢包、顺序错乱等情况时,UDP也不会 负责重新发送以及纠错。当向目标发送一个UDP数据包之后, 目标是不会发回任 何UDP数据包的。不过,如果目标主机处于活跃状态,但是目标端口是关闭状态 时,会返回一个ICMP数据包,这个数据包的含义为unreachable 。如果目标主机不 处于活跃状态,这时是收不到任何响应数据的。利用UDP原理可以实现探测存活 主机。

现在来编写一个利用UDP实现的活跃主机的扫描程序,首先借助Scapy库来完 成。在安装好Scapy的终端输入Scapy运行程序。设定远程IP地址为39.xx.xx.238,

端口dport可为任意值,此处将dport设为7345 ,通过sr1() 函数将构造好的数据包 发出。相关代码如下所示:

>>> udp=UDP()

>>> r = (ip/udp)

>>> r[IP] .dst="192 .168 .19 .141"

>>> r[UDP] .dport=734

>>> a=sr1(r)

如果目标主机处于存活状态,则会接收到目标主机的应答信息,可通过

a.display() 函数查看数据包信息。可以查看到返回的信息中存在ICMP的应答信 息,“code=port-unreachable”表示目标端口不可达。由此可以验证远程目标主机为 存活状态。若目标主机不为存活状态,则不会收到目标主机的响应数据包。响应 结果如下所示。

 

 

 

 

 

根据以上UDP发现存活主机的原理,我们可以编写相应的Python工具进行实 现,具体过程如下所示:

1)导入程序代码所应用到的模块:time 、optparse 、random和scapy 。time模  块主要用于产生延迟时间,optparse模块用于生成命令行参数,random模块用于生 成随机的端口,scapy模块用于以UDP发送请求以及接收应答数据。

 

#!/usr/bin/python

import time

from optparse import OptionParser

from random import randin t

from scapy .all import *

2)利用optparse模块生成命令行参数化形式,对用户输入的参数进行接收和 批量处理,最后将处理后的IP地址传入Scan() 函数。

def main() :

usage = "Usage: %prog -i <ip address>"     # 输出帮助信息

parse = OptionParser(usage=usage)

parse .add_option("-i", '--ip ', type="string", dest="target IP", help=

"specify the IP address")          # 获取网段地址

options, args = parse .parse_args()          #实例化用户输入的参数

if '- ' in options .target IP:

# 代码举例:192 .168 .1 .1-120

# 通过“ -”进行分隔,把192 .168 .1 .1和120进行分离

# 把192.168.1.1通过“,”进行分隔,取最后一个数作为range函数的start,然后把 120+1作为range函数的stop

# 这样循环遍历出需要扫描的IP地址

for i in range(in t(options .target IP .split( '- ')[0] .split( ' . ')[3]), in t (options .target IP .split( '- ')[1]) + 1) :

Scan(options .target IP .split( ' . ')[0] + ' . ' + options .target IP .split  ( ' . ')[1] + ' . ' + options .target IP .split( ' . ')[2] + ' . ' + str(i))

 

 

 

 

 

else:

Scan(options .target IP)

 

if __name__ == '__main__ ' :

main()

 

3)Scan() 函数,通过调用UDP将构造好的请求包发送到目的地址,并根据 是否接收到目标主机的响应数据包判断主机的存活状态。若接收到响应数据包,

proto字段整型数据为1时,则代表目标主机为存活状态,打印出“xx.xx.xx.xx Host is up” ,否则为不存活主机,打印出“xx.xx.xx.xx Host is down”。

def Scan(ip) :

try:

dport = random .randin t(1, 65535)

packet = IP(dst=ip)/UDP(dport=dport)

response = sr1(packet,timeout=1 .0, verbose=0)

response:

if in t(response[IP] .proto) == 1:

time .sleep(0 .5)

print(ip + ' ' + "is up")

else:

print(ip + ' ' + "is down")

else:

print(ip + ' ' + "is down")

except :

pass

运行效果如下所示。

c08803611c0c41a9977f5a27f0c89ed5.png

同时,可以打开Wireshark软件进行流量监听,根据抓到的数据流量可以分 析,源主机向目标主机发送UDP数据包,当主机存活时, 目标主机会发送一

个“Destination unreachableb(port unreachable )” 的应答数据包,效果如图4-4所 示。

 

 

 

 

 

0a9c2ba0abc24e7dac3da9c03d2d6d4d.png

图4-4    向目标主机发送UDP数据包时的监听效果

对于TCP 、UDP主机发现,同样可以借助Nmap库来实现。这里需要用到

Nmap的-sT和-PU两个参数。详细的代码过程这里不再赘述,读者可在4.2.1节的基 础上进行修改,所需修改代码部分如下所示:

 

result = nm .scan(hosts=target IP, arguments= '-sT ')

TCP主机发现的测试命令及效果如下:

2a083cbe87a448ceb7e07bfc8341fd4f.png

result = nm .scan(hosts=target IP, arguments= '-PU ')

UDP主机发现的测试效果如下:

21332f3874664a1191e1935ab9181a20.png

 

 

 

 

 

 

4.2.3    基于ARP的主机发现

 

ARP协议(地址解析协议)属于数据链路层的协议,主要负责根据网络层地 址(IP)来获取数据链路层地址(MAC)。

以太网协议规定,同一局域网中的一台主机要和另一台主机进行直接通信, 必须知道目标主机的MAC地址。而在TCP/IP中,网络层只关注目标主机的IP地  址。这就导致在以太网中使用IP协议时,数据链路层的以太网协议接收到的网络 层IP协议提供的数据中,只包含目的主机的IP地址。于是需要ARP协议来完成IP  地址到MAC地址的转换。假设我们当前的以太网结构如图4-5所示。

f3eb2802119e4e40b0d95d59ecaf6064.png

图4-5    以太网结构

在上述以太网结构中,假设PC1想与PC3通信,步骤如下。

1)PC1知道PC3的IP地址为192.168.1.3,然后PC1会检查自己的APR缓存表中 该IP是否有对应的MAC地址。

2)如果有,则进行通信。如果没有,PC1就会使用以太网广播包来给网络上 的每一台主机发送ARP请求,询问192.168.1.3的MAC地址。ARP请求中同时也包 含了PC1的IP地址和MAC地址。以太网内的所有主机都会接收到ARP请求,并检 查是否与自己的IP地址匹配。如果不匹配,则丢弃该ARP请求。

3)PC3确定ARP请求中的IP地址与自己的IP地址匹配,则将ARP请求中PC1 的IP地址和MAC地址添加到本地ARP缓存中。

 

 

 

 

4)PC3将自己的MAC地址发送给PC1。

5)PC1收到PC3的ARP响应时,将PC3的IP地址和MAC地址都更新到本地 ARP缓存表中。

本地ARP缓存表是有生存周期的,生存周期结束后,将再次重复上面的过 程。

当目标主机与我们处于同一以太网的时候,利用ARP进行主机发现是一个最  好的选择。因为这种扫描方式快且精准。现在我们借助Scapy来编写ARP主机发现 脚本,通过脚本对以太网内的每个主机都进行ARP请求。若主机存活,则会响应  我们的ARP请求,否则不会响应。因为ARP涉及网络层和数据链路层,所以需要  使用Scapy中的Ether和ARP 。Scapy中的ARP参数如下所示:

fc1c7b1b185a4c20a0c738c84579a98b.png

Scapy中的Ether参数如下所示:

8d9919dcdd444945a7ae2f1b02cf4bf2.png

这里介绍一下脚本中所使用的参数。Ether中src表示源MAC地址,dst表示目 的MAC地址。ARP中op代表消息类型,1为ARP请求,2为ARP响应,hwsrc和psrc 表示源MAC地址和源IP地址,pdst表示目的IP地址。接下来我们编写ARP主机发  现脚本。

1)写入脚本信息,导入相关模块:

U

#!/usr/bin/python3

# -*- coding: utf-8 -*-

import

import

import

from scapy .all import *

 

 

 

 

 

2)编写本机IP地址和MAC地址获取函数,通过正则表达式来进行获取:

 

# 取IP地址和MAC地址函数

def HostAddress(iface) :

# os .popen执行后返回执行结果

ipData = os .popen( 'ifconfig '+iface)

# 对ipData进行类型转换,再用正则进行匹配

dataLine = ipData .readlines()

# re .search利用正则匹配返回第一个成功匹配的结果,存在结果则为true

# 取MAC地址

if re .search( '\w\w:\w\w:\w\w:\w\w:\w\w:\w\w ',str(dataLine)) :

# 取出匹配的结果

MAC = re .search( '\w\w:\w\w:\w\w:\w\w:\w\w:\w\w ',str(dataLine)) .group(0) # 取IP地址

if re .search(r '((2[0-4]\d|25[0-5]|[01]?\d\d?)\ .){3}(2[0-4]\d|25[0-5]|[01]?

\d\d?) ',str(dataLine

)) :

IP = re .search(r '((2[0-4]\d|25[0-5]|[01]?\d\d?)\ .){3}(2[0-4]\d|25[0-5]| [01]?\d\d?) ',str(dataLine)) .group(0)

# 将IP和MAC通过元组的形式返回

addressInfo = (IP,MAC)

return addressInfo

3)编写ARP探测函数,根据本机的IP地址和MAC地址信息, 自动生成目标 进行探测并把结果写入文件:

# ARP扫描函数

def ArpScan(iface= 'eth0 ') :

# 通过HostAddres返回的元组取出MAC地址

mac = HostAddress(iface)[1]

# 取出本机IP地址

ip = HostAddress(iface)[0]

# 对本机IP地址进行分隔并作为依据元素,用于生成需要扫描的IP地址

ipSplit = ip .split( ' . ')

# 需要扫描的IP地址列表

ipList = []

# 根据本机IP生成IP扫描范围

for i in range(1,255) :

ipItem = ipSplit[0] + ' . ' + ipSplit[1] + ' . ' + ipSplit[2] + ' . ' + str(i) ipList.append(ipItem)

'''

发送ARP包

因为要用到OSI的二层和三层,所以要写成Ether/ARP。

因为最底层用到了二层,所以要用srp()发包

'''

result=srp(Ether(src=mac,dst= 'FF:FF:FF:FF:FF:FF ')/ARP(op=1,hwsrc=mac,hwdst= '00:00:00:00:00:00 ',pds t=ipList),iface=iface,timeout=2,verbose=False)

# 读取result中的应答包和应答包内容

resultAns = result[0] .res

# 存活主机列表

liveHost = []

# number 为接收到应答包的总数

number = len(resultAns)

print("=====================")

print("ARP 探测结果")

print("本机IP地址 :"  + ip)

print("本机MAC地址 :" + mac)

print("=====================")

for x in range(number) :

IP = resultAns[x][1][1] .fields[ 'psrc ']

MAC = resultAns[x][1][1] .fields[ 'hwsrc ']

liveHost.append([IP,MAC])

print("IP:" + IP + "\n\n" + "MAC:" + MAC  )

print("=====================")

# 把存活主机IP写入文件

 

 

 

 

 

resultFile = open("result","w")

for i in range(len(liveHost)) :

resultFile .write(liveHost[i][0] + "\n")

resultFile .close()

 

4)编写main 函数,利用optparse模块生成命令行参数化形式:

if __name__ == '__main__ ' :

parser = optparse .OptionParser( 'usage: python %prog -i interfaces \n\n ' 'Example: python %prog -i e th0\n ')

# 添加网卡参数 -i

parser .add_option( '-i ', '--iface ',dest= 'iface ',default= 'eth0 ',type= 'string ', help= 'interfaces name ')

(options, args) = parser .parse_args()

ArpScan(options .iface)

这样我们的ARP主机发现脚本功能就完成了。脚本测试结果下所示:

eae3e12ee7a74b6dbfeb4aacda9d77cf.png

8c13e38a70234322a396b921f2b1e3fb.png

查看结果文件如下所示:

 

 

639562086b7c48d7a6ebceb2d39bb4ae.png

 

提示:普通用户运行时需要进行sudo ,否则会出现Operation not permitted提 醒!

下面介绍通过Nmap库来实现ARP主机发现,这里需要用到Nmap的-PR参数。 详细的过程此处不再赘述,读者可在4.2.1节的基础上进行修改,所需修改的代码 部分如下所示:

U

result = nm .scan(hosts=target IP, arguments= '-PR ')

ARP主机发现,测试效果如下所示:

1e89823d7f13453eb1b47018bbf61468.png

 

 

 

 

 

 

4.2.4    端口探测

 

端口是设备与外界通信交流的接口。如果把服务器看作一栋房子,那么端口 就是可以进出这栋房子的门。真正的房子只有一个或几个门,但是服务器可以至 多有65 536个门。不同的端口(门)可以指向不同的服务(房间)。

例如,我们经常浏览网页时涉及的WWW服务用的是80号端口,上传或下载 文件时的FTP服务用的是21号端口,远程桌面用的是3389号端口。

所以入侵者想要获取到房子(服务器)的控制权,势必要先从一个门进入一 个房间,再通过这个房间控制整个房子。那么服务器开了几个端口,端口后面的 服务是什么,这些都是十分重要的信息,可以为入侵者制定详细的入侵计划提供 依据。因此在信息搜集阶段,端口开放情况的扫描就显得尤为重要。

下面将通过Python的Socket模块来编写一个简便的多线程端口扫描工具。

1)导入脚本信息以及相关的模块:

 

#!/usr/bin/python3

# -*- coding:utf-8 -*-

import import import import import

sys

socket

optparse

threading

queue

2)编写一个端口扫描类,继承threading.Thread 。这个类需要传递3个参数, 分别是目标IP 、端口队列、超时时间。通过这个类创建多个子线程来加快扫描进 度:

# 端口扫描类,继承threading .Thread

class PortScaner(threading .Thread) :

# 需要传入端口队列、 目标IP,探测超时时间

def __init__(self, portqueue, ip, timeout=3) :

threading .Thread.__init__(self)

self._portqueue = portqueue

self._ip = ip

self._timeout = timeout

def run(self) :

while True:

# 判断端口队列是否为空

if self._portqueue .empty() :

# 端口队列为空,说明已经扫描完毕,跳出循环

break

# 从端口队列中取出端口,超时时间为1s

port = self._portqueue .get(timeout=0 .5)

try:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s .settimeout(self._timeout)

 

 

 

 

 

result_code = s .connect_ex((self._ip, port))

# sys .stdout.write("[%d]Scan\n" % port)

# 若端口开放,则会返回0

if result_code == 0:

sys .stdout.write("[%d] OPEN\n" % port)

except Exception as e:

print(e)

finally:

s .close()

 

3)编写一个函数,根据用户的参数来指定目标IP 、端口队列的生成以及子线 程的生成,同时能支持单个端口的扫描和范围端口的扫描:

def StartScan(targetip, port, threadNum) :

# 端口列表

port List = []

portNumb = port

# 判断是单个端口还是范围端口

if '- ' in port :

for i in range(in t(port.split( '- ')[0]), in t(port.split( '- ')[1])+1) : port List.append(i)

else:

port List.append(in t(port))

# 目标IP地址

ip = targetip

# 线程列表

threads = []

# 线程数量

threadNumber = threadNum

# 端口队列

portQueue = queue .Queue()

# 生成端口,加入端口队列

for port in port List :

portQueue .put(port)

for t in range(threadNumber) :

threads .append(PortScaner(portQueue, ip, timeout=3))

# 启动线程

for thread in threads:

thread.start()

# 阻塞线程

for thread in threads:

thread.join()

4)编写主函数来制定参数的规则:

if __name__ == '__main__ ' :

parser = optparse .OptionParser( 'Example: python %prog -i 127 .0 .0 .1 -p

80 \n      python %prog -i 127 .0 .0 .1 -p 1-100\n ')

# 目标IP参数-i

parser .add_option( '-i ', '--ip ', dest= 'target IP ',default= '127 .0 .0 .1 ', type= 'string ',help= 'target IP ')

# 添加端口参数-p

parser .add_option( '-p ', '--port ', dest= 'port ', default= '80 ', type= 'string ', help= 'scann port ')

# 线程数量参数-t

parser .add_option( '-t ', '--thread ', dest= 'threadNum ', default=100, type= 'in t ', help= 'scann thread number ')

(options, args) = parser .parse_args()

StartScan(options .target IP, options .port, options .threadNum)

这里打开了一个CentOS7的服务器作为目标,IP地址为192.168.61.62 ,服务器

 

 

 

开放了22 、80 、3306号端口,然后利用编写好的程序脚本对服务器进行端口扫 描,扫描结果如下所示:

760be2721a534575b6dabb4a9194cbcf.png

再对服务器进行范围端口的扫描,如下所示:

54583ff4529b46ba99c76b7b5afb6d26.png

对于开放端口探测,同样也可以借助Nmap库来实现,这里需要用到Nmap的- p参数。详细的代码此处不再赘述,读者可在4.2.1节的基础上进行修改。所需修  改的代码部分如下所示:

U

result = nm .scan(hosts=target IP, arguments= '-p '+str(targetPort))

测试效果如下:

dbb30b6867b9419ea7dd8dba8ee52570.png

 

 

 

 

 

 

4.2.5    服务识别

 

在渗透测试的过程中,服务识别是一个很重要的环节。如果能识别出目标主 机的服务、版本等信息,对于渗透测试将有重要帮助。对于入侵者来说,发现这 些运行在目标上的服务,就可以利用这些软件上的漏洞入侵目标;对于网络安全 的维护者来说,也可以提前发现系统的漏洞,从而预防这些入侵行为。

很多扫描工具都采用了一种十分简单的方式,就是根据端口判断服务类型,

因为通常常见的服务都会运行在固定的端口上(见表4-1~表4-7),例如,FTP服 务总会运行在21号端口上,HTTP服务运行在80号端口上。但是利用该方式进行

服务识别存在明显的缺陷,很多人会将服务运行在其他端口上,例如,将本来运  行在23号端口上的Telnet运行在22号端口上,这样就会误以为这是一个SSH服务, 进而增加不必要的工作量。由于很多软件在连接之后都会提供一个表明自身信息  的banner ,在这里我们可以根据获取的banner信息对运行的服务类型进行判断,进 而可以确定开放端口对应的服务类型及版本号。

表4-1    文件共享服务端口

2c74c093c2064dc2931bd2deb35d5ea0.png

表4-2    远程连接服务端口

b4d453ed0977436dacf3634e87d6d079.png

表4-3    Web应用服务端口

 

 

 

 

 

597cc44fc97c46869b5a416fd74c51d2.png

表4-4    数据库服务端口

20b65ea8410446d4855110e81b6914e7.png

表4-5    邮件服务端口

2988816cb13c4f1f8dfa5e194661e391.png

表4-6    网络常见协议端口5eba65ee58f34dc89eec752d258fc7a8.png

表4-7    特殊服务端口

 

676d568ff3904308863b045143ced007.png

 

 

 

因此,可以向目标开放的端口发送探针数据包,根据目标主机返回的banner   信息与存储总结的banner信息进行比对,进而确定运行的服务类型。著名的Nmap 扫描工具就是采用了这种方法,它包含一个十分强大的banner数据库,而且这个  库仍在不断完善中。接下来按照上面介绍的思路来编写对目标服务进行扫描的程 序。

1)导入程序代码所应用到的模块:time 、optparse 、socket和re 。time模块主 要用于产生延迟时间,optparse模块用于生成命行参数,socket模块用于产生TCP 请求,re模块为正则表达式模块,与指纹信息进行有效匹配,进而确定服务类

型。SIGNS为指纹库,用于对目标

from optparse import OptionParser

import

import

import

SIGNS = (

# 协议 | 版本 | 关键字

b 'FTP |FTP |^220 .*FTP ',

b 'MySQL |MySQL |mysql_native_password ',

b 'oracle-https |^220- ora ',

b 'Telnet|Telnet|Telnet ',

b 'Telnet|Telnet|^\r\n%connection closed by remote host!\x00$ ',

b 'VNC |VNC |^RFB ',

b 'IMAP |IMAP |^\* OK .*?IMAP ',

b 'POP |POP |^\+OK .*? ',

b 'SMTP |SMTP |^220 .*?SMTP ',

b 'Kangle |Kangle |HTTP .*kangle ',

b 'SMTP |SMTP |^554 SMTP ',

b 'SSH |SSH |^SSH- ',

b 'HTTPS |HTTPS |Location: https ',

b 'HTTP |HTTP |HTTP/1 .1 ',

b 'HTTP |HTTP |HTTP/1 .0 ',

)

 

 

 

 

 

 

def main() :

parser = OptionParser("Usage:%prog -i <target host> ")   # 输出帮助信息

parser .add_option( '-i ',type= 'string ',dest= 'IP ',help= 'specify target host ') # 获取IP地址参数

parser .add_option( '-p ', type= 'string ', dest= 'PORT ', help= 'specify target

host ')  # 获取IP地址参数

options,args = parser .parse_args()

ip = options .IP

port = options .PORT

print("Scan report for "+ip+"\n")

for line in port.split( ', ') :

request(ip,line)

time .sleep(0 .2)

print("\nScan finished!. . .\n")

if __name__ == "__main__" :

try:

main()

except Keyboard Interrupt :

print("interrupted by user, killing all threads . . .")

 

3)在request() 函数中,首先调用sock.connect() 函数探测目标主机端口  是否开放,如果端口开放,则利用sock.sendall() 函数将PROBE探针发送给目标 端口。sock.recv() 函数用于接收返回的指纹信息,并将指纹信息及端口发送到 regex() 函数。

def request(ip,port) :

response = ' '

PROBE = 'GET / HTTP/1 .0\r\n\r\n '

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.settimeout(10)

result = sock.connect_ex((ip, in t(port)))

if result == 0:

try:

sock.sendall(PROBE .encode())

response = sock.recv(256)

if response:

regex(response, port)

except(ConnectionResetError,socket.timeout) :

pass

else:

pass

sock.close()

4)利用re.search() 函数将返回的banner信息与SIGNS包含的指纹信息进行 正则匹配,并将匹配到的结果输出。如果没有在SIGNS中找到相匹配的信息,则 输出Unrecognized。

def regex(response, port) :

text = ""

if re .search(b '<title>502 Bad Gateway ', response) :

proto = {"Service failed to access !!"}

for pattern in SIGNS:

pattern = pattern .split(b ' | ')

if re .search(pattern[-1], response, re .IGNORECASE) :

proto = "["+port+"]" + " open " + pattern[1] .decode()

break

else:

proto = "["+port+"]" + " open " + "Unrecognized"

print(proto)

 

 

 

 

 

 

端口服务版本的识别实现起来也是比较困难的, 目前市面上能提供相关服务  的软件也非常多,而且每个软件也会出现多个版本。下面借助Nmap库来实现对主 机端口服务的探测,这里还需要用到Nmap的-sV参数。详细的代码此处就不再赘  述,读者可在4.2.1节的基础上进行修改,所需修改的代码部分如下所示:

(2) print("[{} :{}] : [{} :{}]" .format(targetPort, port_infor[ 'state '] , port_ infor[ 'name '], port_infor[ 'product '])

测试效果如下:

 

 

 

 

 

 

4.2.6    系统识别

 

识别出目标主机操作系统的类型和版本,可以大量减少不必要的测试成本, 缩小测试范围,更精确地针对目标进行渗透测试。

但是判断目标的操作系统并非一件简单的事情。因为现在的操作系统类型繁 多,仅Windows和Linux就有包含了许多衍生系统,同时,现今的防火墙、路由  器、智能设备等都有其自带的操作系统,所以需要精确判断目标操作系统的类型 并非易事。 目前主要通过“指纹识别” 的方式来对目标的操作系统来进行猜测。检 测的方法一般分为两种:主动式探测和被动式探测。

(1)主动式探测:向目标主机发送一段特定的数据包,根据目标主机对数  据包做出的回应进行分析,判断目标主机中可能的操作系统类型。与被动式探测 相比,主动式获取的结果更加精确,但也容易触发目标安全系统的警报。

(2)被动式探测:通过工具嗅探、记录、分析数据包流。根据数据包信息  来分析目标主机的操作系统。与主动式探测相比,被动式探测的结果虽然不如主 动式探测精确,但是不容易被目标主机安全系统察觉。

主机识别的技术原理:Windows操作系统与Linux操作系统的TCP/IP实现方式 并不相同,导致两种系统对特定格式的数据包会有不同的响应结果,包括响应数 据包的内容、响应时间等,形成了操作系统的指纹。通常情况下,可在对目标主 机进行ping操作后,依据其返回的TTL值对系统类型进行判断,Windows系统的  TTL起始值为128 ,Linux系统的TTL起始值为64 ,且每经过一跳路由,TTL值减  1。

Windows的TTL返回值如下:

5ea43ee87e6e49678cefb35c64d47ca5.png

Linux的TTL返回值如下:

 

 

 

 

 

95a4f4cd37734625bc8c344d73894ad0.png

根据按照目标主机返回的响应数据包中的TTL值来判断操作系统类型的原 理,可编写Python程序实现自动化,详细过程如下所示。

1)导入程序代码所应用的模块:optparse 、os和re 。optparse用于生成命行参 数;os用于执行系统命令;re为正则表达式模块,用于匹配返回的TTL值。

 

#!/usr/bin/python3 .7

#!coding:utf-8

from optparse import OptionParser

import os

import re

2)利用optparse模块生成命令行参数化形式,对用户输入的参数进行接收和 批量处理,最后将处理后的IP地址传入ttl_scan() 函数。

def main() :

parser = OptionParser("Usage:%prog -i <target host> ")   # 输出帮助信息

parser .add_option( '-i ',type= 'string ',dest= 'IP ',help= 'specify target host ') # 获取IP地址参数

options,args = parser .parse_args()

ip = options .IP

ttl_scan(ip)

if __name__ == "__main__" :

main()

3)调用os.popen() 函数执行ping命令,并将返回的结果通过正则表达式识

别,提取出TTL值。当TTL值小于等于64时,操作系统为Linux类型,输 出“xx.xx.xx.xx is Linux/UNIX” ,否则输出“xx.xx.xx.xx is Windows”。

def ttl_scan(ip) :

ttlstrmatch = re .compile(r 'ttl=\d+ ')

ttlnummatch = re .compile(r '\d+ ')

result = os .popen("ping -c 1 "+ip)

res = result.read()

for line in res .splitlines() :

result = ttlstrmatch.findall(line)

if result :

ttl = ttlnummatch.findall(result[0])

if in t(ttl[0]) <= 64:  # 判断目标主机响应包中TTL值是否小于等于64

print("%s  is Linux/UNIX"%ip)  # TTL≤64时为Linux/UNIX系统 else:

print("%s is Windows"%ip)      # 反之为Windows系统

else:

pass

 

 

 

 

 

运行结果如下:

9937f9b7510d41d4b039231045803384.png

当然,这里也可以借助Nmap库来实现操作系统类型识别的功能,通过Nmap 的-O参数对目标主机操作进行系统识别,代码如下所示:

result = nm .scan(hosts=target IP, arguments= '-O ')

借助Nmap库我们可以很轻松地完成一个主动式系统探测工具,而且其判断的 结果在实际运用中也非常具有参考价值。

运行结果如下:

3c031f362e204a0593dd87d2b484f98e.png

 

 

 

 

 

 

4.2.7    敏感目录探测

 

资源发现属于信息搜集的一部分,善于发现隐藏的信息,如隐藏目录、隐藏 文件等,可提高渗透测试的全面细致性。本节将用Python实现敏感目录发现。在 渗透测试过程中,资源发现是极其重要的一环。具备好的资源发现能力能够令整 个工作事半功倍。

在渗透测试过程中进行目录扫描是很有必要的,例如,当发现开发过程中未 关闭或忘记关闭的页面,可能就会发现许多可以利用的信息。下面我们编写一个 基于字典的目录扫描脚本。

1)要进行网页目录扫描,需要进行网页访问,所以先导入requests模块备 用,然后等待用户输入url和字典:

 

import requests

headers = {

"User-Agent" : "Mozilla/5 .0 (Windows NT 6 .1; WOW64; rv:6 .0) Gecko/20100101 Firefox/6 .0" }

url = input("url : ")

txt = input( 'php .txt ')

2)当用户没有输入字典时,默认打开根目录的php.txt ,后将字典中的内容 放进队列中:

url_list = []

if txt == "" :

txt = "php .txt"

try:

with open(txt, 'r ') as f :

for a in f :

a = a .replace( '\n ', '')

url_list.append(a)

f.close()

except :

print("error!")

3)将队列中的内容拼接到url中组成需要验证的地址,通过返回值判断是否 存在此目录:

 

for li in url_list :

conn = "http://" + url +"/"+ li

try:

response = requests .get(conn,headers = headers)

print("%s  %s" % (conn, response))

except e:

print( '%s-------------%s ' %(conn, e .code))

 

 

 

 

 

 

至此,一个简单的目录扫描脚本就完成了。运行效果如下所示:

324f883f6e374f479f4fe0369880dc3e.png

 

  • 30
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在Kali渗透测试中,信息收集是非常重要的一步。信息收集又分为被动信息收集和主动信息收集。被动信息收集是指通过公开的信息来源,如搜索引擎、社交媒体、公司网站等来搜集目标的信息主动信息收集则是指使用特定的工具和技术来主动获取目标的信息。以下是Kali渗透测试中常用的信息收集步骤: 1. 开始被动信息收集:使用搜索引擎进行目标的虚拟足迹搜索,查找目标可能泄露的信息,如员工姓名、电子邮件地址、电话号码等。同时,还可以通过搜索引擎的高级搜索功能,对目标域名进行更详细的搜索和分析。 2. 分析目标的社交媒体:通过目标的社交媒***解目标,并有针对性地进行后续攻击。 3. 扫描目标的网络:使用网络扫描工具,如Nmap,对目标的IP地址进行扫描,查找目标主机的开放端口和服务。这些信息可以帮助渗透测试人员了解目标的网络基础设施。 4. 利用WHOIS查询:使用WHOIS查询工具,查询目标的域名注册信息,如注册人、注册商、联系方式等。这些信息可以帮助渗透测试人员了解目标的背景信息和网络架构。 5. 进行主动信息收集:使用渗透测试工具,如Metasploit、Nessus等,对目标进行主动扫描和漏洞评估。通过对目标系统的漏洞扫描和渗透测试,可以发现可能存在的安全漏洞,并进行后续的攻击和渗透。 综上所述,Kali渗透测试中的信息收集步骤包括被动信息收集和主动信息收集,通过搜索引擎、社交媒体分析、网络扫描、WHOIS查询和渗透测试工具等手段,可以收集到目标的关键信息,为后续的攻击和渗透提供支持。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [如何使用Kali进行信息收集?](https://blog.csdn.net/hack0919/article/details/130913774)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [渗透技术之各种信息收集的方式.docx](https://download.csdn.net/download/qq_40730911/12595950)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lyx-0607

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值