Python数字网络取证-II

前一章讨论了使用Python进行网络取证的一些概念.在本章中,让我们更深入地了解使用Python的网络取证.

使用美丽的汤进行网页保存

万维网(WWW) )是一种独特的信息资源.然而,由于内容以惊人的速度丢失,其遗留风险很高.许多文化遗产和学术机构,非营利组织和私营企业已经探讨了所涉及的问题,并为网络存档的技术解决方案的开发做出了贡献.

网页保存或网络存档是从万维网收集数据的过程,确保将数据保存在档案中,并使其可供未来的研究人员,历史学家和公众使用.在继续进行网页保存之前,让我们讨论一下与网页保存相关的一些重要问题,如下所示 :

  • 改变网络资源 : 网络资源每天都在不断变化,这对网页保存来说是一个挑战.

  • 大量资源 : 与网页保存相关的另一个问题是要保留的大量资源.

  • 诚信 : 必须保护网页免受未经授权的修改,删除或删除,以保护其完整性.

  • 处理多媒体数据 : 在保留网页的同时,我们也需要处理多媒体数据,这些可能会导致问题.

  • 提供访问权限 :  ;除了保留之外,还需要解决提供对Web资源的访问和处理所有权问题的问题.

在本章中,我们将使用名为 Beautiful Soup 的Python库来保存网页.

什么是美味汤?

美丽Soup是一个Python库,用于从HTML和XML文件中提取数据.它可以与 urlib 一起使用,因为它需要输入(文档或url)来创建汤对象,因为它无法获取网页本身.您可以在 www.crummy.com/software/BeautifulSoup/bs4/doc/

请注意,在使用它之前,我们必须使用以下命令安装第三方库 :

 
 pip install bs4

接下来,使用Anaconda包管理器,我们可以按照以下方式安装Beautiful Soup;

 
 conda install -c anaconda beautifulsoup4

用于保存网页的Python脚本

使用名为Beautiful Soup的第三方库来保存网页的Python脚本在这里讨论 :

首先,按以下方式导入所需的库;

 
 from __future__ import print_function 
 import argparse 
 from bs4 import BeautifulSoup,SoupStrainer 
 from datetime imp ort datetime 
 import hashlib 
 import logging 
 import os 
 import ssl 
 import ur 
 from urllib.request import urlopen 
 import urllib.error 
 logger = logging.getLogger(__ name__)

请注意,此脚本将采用两个位置参数,一个是要保留的URL和其他是所需的输出目录,如下所示 :

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Web Page preservation')
   parser.add_argument("DOMAIN", help="Website Domain")
   parser.add_argument("OUTPUT_DIR", help="Preservation Output Directory")
   parser.add_argument("-l", help="Log file path",
   default=__file__[:-3] + ".log")
   args = parser.parse_args()

现在,通过指定文件和流处理程序进行循环设置脚本的日志记录,并记录采集过程,如图所示 :

logger.setLevel(logging.DEBUG)
msg_fmt = logging.Formatter("%(asctime)-15s %(funcName)-10s""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stderr)
strhndl.setFormatter(fmt=msg_fmt)
fhndl = logging.FileHandler(args.l, mode='a')
fhndl.setFormatter(fmt=msg_fmt)

logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting BS Preservation")
logger.debug("Supplied arguments: {}".format(sys.argv[1:]))
logger.debug("System " + sys.platform)
logger.debug("Version " + sys.version)

现在,让我们对所需的输出目录进行输入验证,如下所示 :

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)
main(args.DOMAIN, args.OUTPUT_DIR)

现在,我们将定义 main()函数,它将提取基本名称的网站通过删除实际名称之前的不必要元素以及输入URL上的附加验证,如下所示;

def main(website, output_dir):
   base_name = website.replace("https://", "").replace("http://", "").replace("www.", "")
   link_queue = set()
   
   if "http://" not in website and "https://" not in website:
      logger.error("Exiting preservation - invalid user input: {}".format(website))
      sys.exit(1)
   logger.info("Accessing {} webpage".format(website))
   context = ssl._create_unverified_context()

现在,我们需要使用urlopen()方法打开与URL的连接.让我们使用try-except块如下 :

try:
   index = urlopen(website, context=context).read().decode("utf-8")
except urllib.error.HTTPError as e:
   logger.error("Exiting preservation - unable to access page: {}".format(website))
   sys.exit(2)
logger.debug("Successfully accessed {}".format(website))

下一个代码行包括三个函数,如下所述 :

  • write_output()来编写第一个网页到输出目录

  • find_links()用于识别此网页上链接的功能

  • recurse_pages()函数迭代并发现网页上的所有链接.

write_output(website, index, output_dir)
link_queue = find_links(base_name, index, link_queue)
logger.info("Found {} initial links on webpage".format(len(link_queue)))
recurse_pages(website, link_queue, context, output_dir)
logger.info("Completed preservation of {}".format(website))

现在,让我们将 write_output()方法定义如下 :

def write_output(name, data, output_dir, counter=0):
   name = name.replace("http://", "").replace("https://", "").rstrip("//")
   directory = os.path.join(output_dir, os.path.dirname(name))
   
   if not os.path.exists(directory) and os.path.dirname(name) != "":
      os.makedirs(directory)

我们需要记录一些有关网页的详细信息,然后我们使用 hash_data()方法记录数据的哈希值,如下所示 :

logger.debug("Writing {} to {}".format(name, output_dir)) logger.debug("Data Hash: {}".format(hash_data(data)))
path = os.path.join(output_dir, name)
path = path + "_" + str(counter)
with open(path, "w") as outfile:
   outfile.write(data)
logger.debug("Output File Hash: {}".format(hash_file(path)))

现在,在其帮助下定义 hash_data()方法我们读取 UTF-8 编码数据,然后生成 SHA-256 哈希,如下所示 :

def hash_data(data):
   sha256 = hashlib.sha256()
   sha256.update(data.encode("utf-8"))
   return sha256.hexdigest()
def hash_file(file):
   sha256 = hashlib.sha256()
   with open(file, "rb") as in_file:
      sha256.update(in_file.read())
return sha256.hexdigest()

现在,让我们创建一个 Beautifulsoup 对象. find_links()方法下的网页数据如下 :

def find_links(website, page, queue):
   for link in BeautifulSoup(page, "html.parser",parse_only = SoupStrainer("a", href = True)):
      if website in link.get("href"):
         if not os.path.basename(link.get("href")).startswith("#"):
            queue.add(link.get("href"))
   return queue

现在,我们需要通过提供网站URL,当前链接队列,未验证的SSL上下文和输出目录的输入来定义 recurse_pages()方法,如下所示;

def recurse_pages(website, queue, context, output_dir):
   processed = []
   counter = 0
   
   while True:
      counter += 1
      if len(processed) == len(queue):
         break
      for link in queue.copy(): if link in processed:
         continue
	   processed.append(link)
      try:
      page = urlopen(link,      context=context).read().decode("utf-8")
      except urllib.error.HTTPError as e:
         msg = "Error accessing webpage: {}".format(link)
         logger.error(msg)
         continue

现在,写下每个的输出通过传递链接名称,页面数据,输出目录和计数器在文件中访问的网页,如下所示 :

write_output(link, page, output_dir, counter)
queue = find_links(website, page, queue)
logger.info("Identified {} links throughout website".format(
   len(queue)))

现在,当我们通过提供网站的URL,输出目录和日志文件的路径来运行此脚本时,我们将获得有关该Web的详细信息可用于将来使用的页面.

病毒狩猎

你有没有想过法医分析师,安全研究人员和事件受访者如何理解有用的软件和恶意软件之间的区别?答案在于问题本身,因为没有研究恶意软件,黑客迅速产生,研究人员和专家很难说出有用的软件和恶意软件之间的区别.在本节中,我们将讨论 VirusShare ,这是完成此任务的工具.

了解VirusShare

VirusShare是最大的私人拥有的恶意软件样本集合,为安全研究人员,事件响应者和法医分析师提供实时恶意代码的样本.它包含超过3000万个样本.

VirusShare的好处是免费提供的恶意软件哈希列表.任何人都可以使用这些哈希值来创建一个非常全面的哈希集,并使用它来识别潜在的恶意文件.但在使用VirusShare之前,我们建议您访问 https://virusshare.com 更多详细信息.

使用Python从VirusShare创建换行符分隔哈希列表

来自VirusShare的哈希列表可以被各种取证工具(如X)使用-ways和EnCase.在下面讨论的脚本中,我们将自动从VirusShare下载哈希列表以创建换行符分隔的哈希列表.

对于此脚本,我们需要第三方Python库 tqdm 可以按以下方式下载 :

 
 pip install tqdm

请注意,在此脚本中,首先我们将读取VirusShare哈希页面并动态识别最新的哈希列表.然后我们将初始化进度条并下载所需范围内的哈希列表.

首先,导入以下库 :

from __future__ import print_function

import argparse
import os
import ssl
import sys
import tqdm

from urllib.request import urlopen
import urllib.error

此脚本将采用一个位置参数,这将是所需的路径哈希集 :

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Hash set from VirusShare')
   parser.add_argument("OUTPUT_HASH", help = "Output Hashset")
   parser.add_argument("--start", type = int, help = "Optional starting location")
   args = parser.parse_args()

现在,我们将执行标准输入验证,如下所示;

directory = os.path.dirname(args.OUTPUT_HASH)
if not os.path.exists(directory):
   os.makedirs(directory)
if args.start:
   main(args.OUTPUT_HASH, start=args.start)
else:
   main(args.OUTPUT_HASH)

现在我们需要用 ** kwargs 作为参数定义 main()函数,因为这将创建一个字典,我们可以参考支持提供的关键参数,如下所示 :

def main(hashset, **kwargs):
   url = "https://virusshare.com/hashes.4n6"
   print("[+] Identifying hash set range from {}".format(url))
   context = ssl._create_unverified_context()

现在,我们需要使用 urlib.request.urlopen()方法打开VirusShare哈希页面.我们将使用try-except块如下 :

try:
   index = urlopen(url, context = context).read().decode("utf-8")
except urllib.error.HTTPError as e:
   print("[-] Error accessing webpage - exiting..")
   sys.exit(1)

现在,从下载的页面中识别最新的哈希列表.您可以通过查找HTML href 标记的最后一个实例来访问VirusShare哈希列表.可以使用以下代码行和减号来完成;

 
 tag = index.rfind(r'a href ="hashes/VirusShare_") 
 stop = int(index [tag + 27:tag + 27 + 5] .lstrip("0"))
如果"start"不在kwa< rgs:
 start = 0 
 else:
 start = kwargs ["start"] 
如果开始< 0或开始>停止:
 print("[ - ]提供的start参数必须大于或等于""为零但小于最新的哈希列表,""当前:{}".format(stop))
 sys.exit(2)
 print( "[+]从哈希列表{}创建哈希集到{}".格式(开始,停止))
 hashes_downloaded = 0

现在,我们将使用 tqdm.trange()方法创建一个循环和进度条,如下所示;

 
 for x in tqdm.trange(start,stop + 1,unit_scale = True,desc ="Progress"):
 url_hash ="https://virusshare.com/hashes/VirusShare_"{}.md5" .format(str(x).zfill(5))
t ry:
 hashes = urlopen(url_hash,context = context).read().decode("utf-8")
 hashes_list = hashes.split("\ n")
除外urllib.error.HTTPError as e:
 print("[ - ]访问哈希列表的网页时出错{}"" - 继续..".format(x))
继续

成功执行上述步骤后,我们将以+模式打开哈希集文本文件以附加到文本文件的底部.

tag = index.rfind(r'a href = "hashes/VirusShare_')
stop = int(index[tag + 27: tag + 27 + 5].lstrip("0"))

if "start" not in kwa<rgs:
   start = 0
else:
   start = kwargs["start"]

if start < 0 or start > stop:
   print("[-] Supplied start argument must be greater than or equal ""to zero but less than the latest hash list, ""currently: {}".format(stop))
sys.exit(2)
print("[+] Creating a hashset from hash lists {} to {}".format(start, stop))
hashes_downloaded = 0

运行上面的脚本后,您将获得包含文本格式的MD5哈希值的最新哈希列表.