利用Python进行Web渗透测试(八):改进暴力探测器的显示结果和新功能

本篇将会涉及:

  • 改善探测器的结果显示
  • 为探测器添加新的特征
  • 测试探测器的新特征

改善探测器的结果显示

在上一篇我们使用Python编写了一个暴力探测器,用来探测web站点上可能存在利用漏洞的目录和路径。

在这个0.1的版本中,我们只是将网址和其响应状态码打印到终端中,命令行中黑底白字的显示,似乎很不美观也不容易找出重点信息。

有什么办法可以改善一下呢?
我们可以借助于一个模块——termcolor,一个用于将输出进行颜色格式化的工具。

我们来看看termcolor的一个官方示例:

import sys  from termcolor import colored, cprint    text = colored('Hello, World!', 'red', attrs=['reverse', 'blink'])  print(text)  cprint('Hello, World!', 'green', 'on_red')    print_red_on_cyan = lambda x: cprint(x, 'red', 'on_cyan')  print_red_on_cyan('Hello, World!')  print_red_on_cyan('Hello, Universe!')    for i in range(10):      cprint(i, 'magenta', end=' ')    cprint("Attention!", 'red', attrs=['bold'], file=sys.stderr)  

运行代码,会在终端输出带有颜色的字符:

从上面的示例我们可以知道,使用termcolor的colored()方法,可以将普通字符格式化为带有颜色的字符,cprint()方法则可以直接打印出特定颜色的字符。

termcolor自带了八种颜色供我们使用,分别是:

  • grey:灰色
  • red:红色
  • green:绿色
  • yellow:黄色
  • blue:蓝色
  • magenta:品红色
  • cyan:青色
  • white:白色

在探测器中,我们使用colored()方法对状态码进行颜色格式化。

除了添加颜色的显示外,在结果中仅仅显示URL的状态码似乎有点单调,我们再添加几个结果显示:

  • 网页文本的行数;
  • 网页文本的字符数;
  • 网页中的词数;
  • 网页内容的哈希值;
# 统计网页行数  lines = r.text.count("\n")  # 统计网页字符数  charts = len(r._content)  # 统计网页词数  words = len(re.findall(r"\S+",r.text))  # 哈希值  hashs = str(hashlib.md5(r.content).hexdigest())  

接下来,我们将颜色格式化和新的结果显示添加到request_performer()类中:

class request_performer(Thread):      def __init__(self,word,url):          Thread.__init__(self)          try:              self.word = word.split("\n")[0]              self.urly = url.replace('FUZZ',self.word)              self.url = self.urly          except Exception as e:              print(e)        def run(self):          try:              r = requests.get(self.url)              # 统计网页行数              lines = str(r.text.count("\n"))              # 统计网页字符数              charts = str(len(r._content))              # 统计网页词数              words = str(len(re.findall(r"\S+", r.text)))              # 哈希值              hashs = str(hashlib.md5(r.content).hexdigest())              # 状态码              scode = r.status_code              if '200' <= scode < '300':                  print(colored(scode,'green') + "\t" + charts + "  \t" + lines + "   \t" + words+" \t"+hashs+"\t"+self.url)              elif '400' <= scode < '500':                  print(colored(scode,'red') + "\t" + charts + "  \t" + lines + "   \t" + words+" \t"+hashs+"\t"+self.url)              elif '300' <= scode < '400':                  print(colored(scode,'blue') + "\t" + charts + " \t" + lines + "   \t" + words+" \t"+hashs+"\t"+self.url)              else:                  print(colored(scode,'yellow') + "\t" + charts + "   \t" + lines + "   \t" + words+" \t"+hashs+"\t"+self.url)              i[0] = i[0] -1          except Exception as e:              print(e)  

我们设置状态码在大于等于200小于300为绿色,大于等于400小于500为红色,大于等于300小于400为蓝色。

在终端中运行命令:

python3 brutediscovery.py -w http://www.scruffybank.com/FUZZ -t 5 -f common.txt  

可以看到显示出来的结果清晰明了很多了:

添加新的功能选项

在上面的示例中,我们将探测器的显示结果优化了一下,使其更加美观。但是,还有一个问题——404页面的处理。

在字典文件小、字典条目少的情况下,即使我们的探测器探测出很多的404页面,在结果中也容易排除开来。但是如果字典的条目很多,探测出来的404页面也会很多,在命令行界面就会掩盖了成功的响应。我们需要将404页面从结果中剔除掉。

由404页面问题延伸开来,我们的功能的需求就是:通过命令行关键字参数指定,去除某个响应状态码的显示结果。

在request_performer()类中,新添加一个属性hidecode:

class request_performer(Thread):      def __init__(self,word,url,hidecode):          Thread.__init__(self)          try:              self.word = word.split("\n")[0]              self.urly = url.replace('FUZZ',self.word)              self.url = self.urly              self.hidecode = hidecode          except Exception as e:              print(e)  

然后在request_performer()类中排除响应状态码为hidecode的请求结果:

    def run(self):          try:              r = requests.get(self.url)              # 统计网页行数              lines = str(r.text.count("\n"))              # 统计网页字符数              charts = str(len(r.text))              # 统计网页词数              words = str(len(re.findall(r"\S+", r.text)))              # 哈希值              hashs = str(hashlib.md5(r.content).hexdigest())              # 状态码              scode = str(r.status_code)              if scode != str(self.hidecode):                  if '200' <= scode < '300':                      print(colored(scode,'green') + "\t" + charts + "  \t" + lines + "   \t" + words+" \t"+hashs+"\t"+self.url)                  elif '400' <= scode < '500':                      print(colored(scode,'red') + "\t" + charts + "  \t" + lines + "   \t" + words+" \t"+hashs+"\t"+self.url)                  elif '300' <= scode < '400':                      print(colored(scode,'blue') + "\t" + charts + " \t" + lines + "   \t" + words+" \t"+hashs+"\t"+self.url)                  else:                      print(colored(scode,'yellow') + "\t" + charts + "   \t" + lines + "   \t" + words+" \t"+hashs+"\t"+self.url)              i[0] = i[0] -1          except Exception as e:              print(e)  

如果响应状态码不等于指定剔除的状态码,就打印结果。

再在启动函数start()中设置新功能的命令行参数:

def start(argv):      banner()      if len(sys.argv) < 5:          usage()          sys.exit()      try:          opts,args = getopt.getopt(argv,"w:t:f:c:")      except getopt.GetoptError:          print("错误的参数")          sys.exit()      hidecode = 000      for opt,arg in opts:          if opt == '-w':              url = arg          elif opt == '-f':              dicts = arg          elif opt == '-t':              threads = arg          elif opt == '-c':              hidecode = arg        try:          f = open(dicts,'r')          words = f.readlines()      except:          print("打开文件错误:",dicts,"\n")          sys.exit()        launcher_thread(words,threads,url,hidecode)  

接着,完善 launcher_thread()函数:

def launcher_thread(names,th,url,hidecode):      global i      i = []      resultlist = []      print("==============================================")      print("状态码"+"\t"+"字符数"+"\t"+"行数"+"\t"+"词数"+"\t"+"MD5"+"\t"+"网址")      print("==============================================")      i.append(0)      while len(names):          try:              if i[0] < int(th):                  n = names.pop(0)                  i[0] = i[0]+1                  thread = request_performer(n,url,hidecode)                  thread.start()          except KeyboardInterrupt:              print("用户停止了程序运行。完成探测")              sys.exit()      return True  

所有的函数就修改完毕。

测试新功能

改好了代码,我们来测试一下新的功能。目标站点同样选用虚拟机环境中的靶机http://www.scruffybank.com/,common.txt作为字典文件。

因为我们在start()函数里面设置了hidecode的默认值为000,所以我们新增的-c选项不加也可以运行。

先看看不加-c选项的结果:

python3 bruteforce3.py -w http://www.scruffybank.com/FUZZ -t 5 -f common.txt  

和第二版的程序效果一致:

我们再加上-c选项排除404页面:

python bruteforce3.py -w http://www.scruffybank.com/FUZZ -t 5 -f common.txt -c 404  

所有状态码为404的页面都没有显示出来:

我们打开其中一个网址看看:

三个字母加一个换行符一共4个字符、1行文本、1个词数,与探测器返回的结果相同。

这样我们的探测器更新到第三版,功能也越来越丰富了。

 

下一篇,我们会继续为我们的渗透测试探测器增加功能。