*本文仅用于技术讨论与研究,这里使用的技术仅用于学习教育目的,如果列出的技术用于其他任何目标,本站及作者概不负责。
编写这个工具的用意是为了提高工作效率,扫描内网高危端口,定期进行资产梳理,整合内网有哪些web资产,然后将这些数据以execl表格的形式进行统计。该篇文章将详细介绍如何使用python编写工具,以及python常见模块的使用。
该篇章目的是熟悉python编程,学习python的一些常见模块,在编写程序的过程中会有很多操作和方式方法,希望大家能学到东西。
环境准备:
python3 -m pip install python-nmap,openpyxl,request
第一节 python-nmap模块的使用
1、nmap模块方法及参数介绍
#1、导入 nmap模块 import nmap #2、加载nmap模块 np=nmap.PortScanner() #3、使用nmap模块 hosts 参数 指定IP, arguments 参数 可指定多个参数 如 -sV -iL -p 等等 res=np.scan(hosts='127.0.0.1',arguments='-p 80,81,8080 -sV') #也可以不使用hosts参数 通过nmap 的-iL 参数批量读取文本的ip #res=np.scan(arguments='-p http* -iL ip.txt') print(type(res)) print(res) #------------返回的内容为字典格式-----------------------
2、将打印的字典数据放到https://www.bejson.com/?src=xiaof 网站进行处理,可以直观的发现建值关系
3、遍历 字典数据,获取 ip、端口、状态、协议等信息 ,并将这些信息存储到字典中,方便后续调用,(注意这里np.scan没有使用hosts参数,直接使用nmap自带参数 -iL r.txt,需要创建一个 r.txt 文本文件 用来存储IP地址)
import nmap np=nmap.PortScanner() date={'numb':'','ip':'','port':'','protocol':'','state':''} res=np.scan(arguments='-p 21,22,80,8080,445 -iL r.txt') dir=res['scan'] for k in dir.keys(): #ip=(dir[k]['addresses']['ipv4']) for p in dir[k]['tcp']: numb+=1 date['numb']=numb date['ip']=str(dir[k]['addresses']['ipv4']) date['port']=str(p) date['state']=dir[k]['tcp'][p]['state'] date['protocol']=dir[k]['tcp'][p]['name'] print(date)
4、打印内容如下
第二节 openpyxl模块的使用
注:由于openpyxl 模块网上教程偏多,这里不详细介绍,可看注释一步一步调试,熟悉各个函数的功能
1、创建一个.xlsx文件并初始化内容
from openpyxl import * def init_excel(filename): #创建.xlsx表格,并初始化内容 wb=Workbook() filename=filename+".xlsx" ws=wb.create_sheet(index=0) #固定sheet为第一栏 head=['numb','ip','port','protocol','state'] for i in range(0,5): #ws.cell(1,n)表示第一行,第n列,与坐标相似,#第一列 的值 为 hand ws.cell(1,i+1).value=head[i] wb.save(filename) #保存文件,文件名为 port_Scan init_excel('port_Scan')
2、将上节处理好的数据 写入到 该表格中
def Save_Date(date,filename): #将数据存储到表格当中 filename=filename+".xlsx" wb_save=load_workbook(filename) #打开文件 ws_save=wb_save.worksheets[0] #固定sheet current_row=ws_save.max_row+1 #将坐标定位到第二行,(第一行是表头) current_col=1 for key in date: #将 第一节获取的date数据 进行遍历,写入到表格中 ws_save.cell(date['numb']+1,current_col).value=str(date[key]) current_col+=1 wb_save.save(filename) #保存文件
第三节 request模块的使用
功能:过滤 数据中 http、https服务,将ip与端口进行拼接,用request发起请求,记录title值,保存到excel文件中
1、处理json 数据,根据nmap 的数据规则 protocol:https 为https协议,其他 的http值均为http协议,可用此规则拼接url,放入request方法进行请求
for date in datas: if(date['protocol']=='https'): url="https://"+date['ip']+":"+date['port'] elif("http" in date['protocol']): url="http://"+date['ip']+":"+date['port'] if("http" in url): req(url) #掉用req函数
2、req 函数 也非常简单 就是传一个 url 参数 ,带入request方法 发起请求 ,当状态码为200的时候,将numb,url,title 保存到字典中,
注意:
我们知道https的站都是会有证书验证的,我们在使用Python3 requests发送HTTPS请求,已经关闭认证(verify=False)情况下,控制台会输出以下错误:
InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
这里我们加上**urllib3.disable_warnings()**这个是为了禁用requests发送HTTPS请求后的安全警告
def req(url): #对域名进行验证,返回状态码,title global numb_req heads = { #全局变量 请求头 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36', #模拟浏览器请求 'Connection':'close', 'Accept-Encoding':'gzip, deflate' } data={'numb':'','url':'','title':''} try: #try: 是用来捕获异常的,当请求超时 或者网络不能访问时会抛出异常信息,程序照常运行,提高代码的稳定性 urllib3.disable_warnings() response = requests.get(url=url,headers=heads,verify=False,timeout=10) #请求漏洞的url if response.status_code == 200: bs=BeautifulSoup(response.content,"html.parser") title=bs.find("title").text numb_req+=1 data['numb']=numb_req data['url']=url data['title']=title print("[+]"+url+"ttitle:"+title) return data else: print('[-]请求失败:t{}t{}'.format(url,response.status_code)) except Exception as e: print('[-]请求失败: {}t'.format(e,url))
第四节 多线程的使用
需要使用的库有:threading 、queue
多线程的学习可参考这篇文章:https://www.runoob.com/python3/python3-multithreading.html
这里使用 线程数可控的方式进行编写,首先 编写一个class ,run()方法 是线程启动后 默认调用的方法
class DoRun(threading.Thread): #自定义 多线程运行时使用的类 def __init__(self,queue): threading.Thread.__init__(self) self._queue=queue def run(self): while not self._queue.empty(): print("线程开始执行了") #req(self._queue.get()) #多线程要调用的方法
线程的启动和使用:
que=queue.Queue() threads=[thread_count] for date in datas: if(date['protocol']=='https'): url="https://"+date['ip']+":"+date['port'] elif("http" in date['protocol']): url="http://"+date['ip']+":"+date['port'] if("http" in url): req(url) que.put(url) #将url 加入到队列中, 在 DoRun 类 run()方法中使用self._queue.get()方法取出 for i in range(thread_count): threads.append(DoRun(que)) #使用多线程 默认调用 run()函数 for i in threads: i.start() #启动多线程 for i in threads: i.join() #等待线程结束
第五节 用户输入模块编写
1、这里使用的是optparse 这个模块,使用方法如下
from optparse import OptionParser #自定义输入参数 optParser = OptionParser() optParser.add_option('-t','--threads',type="int",help='线程数量,默认为50',default=50) #只列举了一个参数 (options , args) = optParser.parse_args() print(options.threads) #返回用户输入的线程数 ----------add_option()参数详解---------------------------------- -t: 简称,是指用户要输入的参数 --threads: 全称 type:参数的类型 help:使用 -h 显示的内容 default:默认值
参数介绍
-a --arguments 使用nmap模块时调用的参数 如 -p http* -iL r.txt 扫描r.txt文本的IP地址 获取http服务的数据 -T --Type 这里是要使用的模式,当Type =1 时,只扫描端口,当Type=2时,将扫描到的http服务进行下一步验证,获取title值 -t --thread_count 线程数量,当Type=2时 使用多线程 来请求http服务,获取title 默认 50
第六节 功能模块编写
该脚本主要是实现两个功能
1、扫描高危端口
2、梳理内网http资产
def run(): arguments,Type,thread_count=get_Input() #获取用户输入的参数 print("arguments={},Type={},thread_count={}".format(arguments,Type,thread_count)) np=nmap.PortScanner() res=np.scan(hosts='',arguments=arguments) #调用nmap模块 进行端口扫描 datas=get_datas(res) #将扫描到的数据进行整理,提取 filename=str(int(time.time())) #文件名为时间戳 if(Type==1): Save_Data(datas,filename) #当Type=1时,直接扫描端口 然后保存 elif(Type==2): get_title(datas,thread_count) #当Type=2时,先扫描http服务,然后使用request模块进行验证,获取title值,再保存
第七节 使用演示
扫描端口的使用方式 :python3 port_Scan.py -a "-p 0-65535 -iL r.txt" -T 1
http资产梳理的使用方式:python3 port_Scan.py -a "-p http* -iL r.txt" -T 2 -t 50 这里只扫描http服务,效率更高,准确率比全端口扫描要低,由于自己搭建的web服务较为简陋,没有title值,所以这里为[]
第八节 完整代码
import nmap from openpyxl import * #数据处理,将获取到的数据保存在excel文件中 import threading import queue import urllib3 import requests from optparse import OptionParser #自定义输入参数 import time import re numb_req=0 list=[] class DoRun(threading.Thread): #自定义 多线程运行时使用的类 def __init__(self,queue): threading.Thread.__init__(self) self._queue=queue def run(self): while not self._queue.empty(): date=req(self._queue.get()) #print(date) if(date): list.append(date) def init_excel(filename,sheetName): #创建.xlsx表格,并初始化内容 wb=Workbook() if(sheetName=="PortScan"): head=['numb','ip','port','protocol','state'] else: head=['numb','url','title'] ws=wb.create_sheet(sheetName,index=0) for i in range(0,len(head)): ws.cell(1,i+1).value=head[i] wb.save(filename) def Save_Data(datas,filename): #将数据存储到表格当中 filename=filename+".xlsx" init_excel(filename,"PortScan") wb_save=load_workbook(filename) ws_save=wb_save.worksheets[0] for data in datas: print(data) current_col=1 for key in data: ws_save.cell(data['numb']+1,current_col).value=str(data[key]) current_col+=1 wb_save.save(filename) def get_datas(res): #将 nmap 返回的数据 进行处理,返回 list[dir] dir=res['scan'] numb=0 datas=[] for k in dir.keys(): #ip=(dir[k]['addresses']['ipv4']) for p in dir[k]['tcp']: data={'numb':'','ip':'','port':'','protocol':'','state':''} numb+=1 data['numb']=numb data['ip']=str(dir[k]['addresses']['ipv4']) data['port']=str(p) data['state']=dir[k]['tcp'][p]['state'] data['protocol']=dir[k]['tcp'][p]['name'] #print("[+]data={}".format(data)) datas.append(data) return datas def get_title(datas,thread_count): #使用多线程 调用req ,获取datas(全局变量) que=queue.Queue() threads=[] for date in datas: url='' if(date['protocol']=='https'): url="https://"+date['ip']+":"+date['port'] elif("http" in date['protocol']): url="http://"+date['ip']+":"+date['port'] if(url!=''): que.put(url) for i in range(thread_count): threads.append(DoRun(que)) #使用多线程 默认调用 run()函数 for i in threads: i.start() #启动多线程 for i in threads: i.join() #等待线程结束 后将数据保存至文件 Save_title(list,str(int(time.time()))) def Save_title(datas,filename): #将获取的title 保存到execle 文件中 filename=filename+".xlsx" init_excel(filename,"title") wb_save=load_workbook(filename) ws_save=wb_save.worksheets[0] for data in datas: current_col=1 for key in data: ws_save.cell(data['numb']+1,current_col).value=str(data[key]) current_col+=1 wb_save.save(filename) def req(url): #对域名进行验证,返回状态码,title global numb_req heads = { #全局变量 请求头 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36', #模拟浏览器请求 'Connection':'close', 'Accept-Encoding':'gzip, deflate' } data={'numb':'','url':'','title':''} try: urllib3.disable_warnings() resp = requests.get(url=url,headers=heads,verify=False,timeout=10) #请求漏洞的url if resp.status_code == 200: title=re.findall("<title>(.*?)</title>",resp.text) numb_req+=1 data['numb']=numb_req data['url']=url data['title']=title #print("[+]"+url+"ttitle:"+title) print("[+]请求成功{}".format(data)) return data else: print('[-]请求失败') except Exception as e: print('[-]请求失败e:') def get_Input(): #获取用户输入的参数 ,返回 argument, optParser = OptionParser() optParser.add_option('-a','--arguments',action = 'store',type = "string",help='调用nmap模块 使用的参数',default="-iL r.txt") optParser.add_option("-T","--Type", action="store", type="int",help='当Type的值为1 时扫描端口, 当Type的值为2时,扫描http服务',default=1) optParser.add_option("-t","--thread_count", action="store", type="int",help='线程数量,默认为50',default=50) (options , args) = optParser.parse_args() return options.arguments,options.Type,options.thread_count def print_info(datas): for data in datas: print("[+]"+str(data['ip'])+" "+str(data['port']+" "+str(data['protocol'])+" is "+str(data['state']))) def run(): arguments,Type,thread_count=get_Input() #获取用户输入的参数 print("arguments={},Type={},thread_count={}".format(arguments,Type,thread_count)) np=nmap.PortScanner() res=np.scan(hosts='',arguments=arguments) #调用nmap模块 进行端口扫描 datas=get_datas(res) #将扫描到的数据进行整理,提取 filename=str(int(time.time())) #文件名为时间戳 if(Type==1): Save_Data(datas,filename) #当Type=1时,直接扫描端口 然后保存 elif(Type==2): get_title(datas,thread_count) #当Type=2时,先扫描http服务,然后使用request模块进行验证,获取title值,再保存 run()
本文作者:, 属于FreeBuf原创奖励计划,未经许可禁止转载