Python数字网络取证-II
上一章使用 Python 处理了网络取证的一些概念。在本章中,让我们更深入地了解使用 Python 进行网络取证。
美汤保网页
万维网 (WWW) 是一种独特的信息资源。然而,由于内容以惊人的速度丢失,它的遗产处于高风险之中。许多文化遗产和学术机构、非营利组织和私营企业已经探索了所涉及的问题,并为开发网络归档技术解决方案做出了贡献。
网页保存或网络存档是从万维网收集数据的过程,确保数据保存在档案中,并可供未来的研究人员、历史学家和公众使用。在进一步讨论网页保存之前,让我们讨论一些与网页保存相关的重要问题,如下所示:
-
网络资源的变化 : 网页资源每天都在变化,这对网页保存来说是一个挑战。
-
海量资源 : 与网页保存相关的另一个问题是要保存的资源量很大。
-
正直 : 必须保护网页免受未经授权的修改、删除或删除,以保护其完整性。
-
处理多媒体数据 : 在保存网页的同时,我们还需要处理多媒体数据,这些可能会导致问题。
-
提供访问 : 除了保存之外,还需要解决提供网络资源的访问权和处理所有权问题。
在本章中,我们将使用名为的 Python 库 美丽的汤 用于网页保存。
什么是美汤?
Beautiful 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 import datetime import hashlib import logging import os import ssl import sys 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 输出 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: link_queue = set() if "http://“不在网站和”https: 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(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))
现在,让我们定义 写输出() 方法如下:
def write_output(name, data, output_dir, counter=0): name = name.replace("http:// ", "").replace("https: directory = os.path.join(output_dir, os.path.dirname(name)) if not os.path.exists(directory) and os.path.dirname(name) != "": os.makedirs(directory)
我们需要记录有关网页的一些详细信息,然后我们通过使用记录数据的哈希值 哈希数据() 方法如下:
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("输出 File Hash: {}".format(hash_file(path)))
现在,定义 哈希数据() 借助我们阅读的方法 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()
现在,让我们创建一个 美汤 对象出网页数据下 查找链接() 方法如下:
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 上下文和输出目录的输入,如下所示:
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、输出目录和日志文件的路径来运行此脚本时,我们将获得有关该网页的详细信息,以供将来使用。
病毒狩猎
你有没有想过法医分析师、安全研究人员和事件受访者如何理解有用软件和恶意软件之间的区别?答案在于问题本身,因为如果不研究由黑客快速生成的恶意软件,研究人员和专家就很难区分有用的软件和恶意软件。在本节中,让我们讨论一下 病毒共享 ,完成此任务的工具。
了解 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 = "输出 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)
现在我们需要定义 main() 功能与 **kwargs 作为参数,因为这将创建一个字典,我们可以参考支持提供的关键参数,如下所示:
def main(hashset, **kwargs): url = "https:// virusshare.com/hashes.4n6" print("[+] Identifying hash set range from {}".format(url)) context = ssl._create_unverified_context()
现在,我们需要使用打开 VirusShare 哈希页面 urllib.request.urlopen() 方法。我们将使用 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")) 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
现在,我们将使用 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)) try: hashes = urlopen(url_hash, context=context).read().decode("utf-8") hashes_list = hashes.split("\n") except urllib.error.HTTPError as e: print("[-] Error accessing webpage for hash list {}"" - continuing..".format(x)) continue
成功执行上述步骤后,我们将以a+模式打开哈希集文本文件以追加到文本文件的底部。
with open(hashset, "a+") as hashfile: for line in hashes_list: if not line.startswith("#") and line != "": hashes_downloaded += 1 hashfile.write(line + '\n') print("[+] Finished downloading {} hashes into {}".format( hashes_downloaded, hashset))
运行上述脚本后,你将获得包含文本格式的 MD5 哈希值的最新哈希列表。