Python-digital-forensics-network

提供:Dev Guides
移動先:案内検索

Pythonデジタルネットワークフォレンジック-II

前の章では、Pythonを使用したネットワークフォレンジックの概念のいくつかを扱いました。 この章では、Pythonを使用したネットワークフォレンジックをより深いレベルで理解しましょう。

美しいスープでWebページを保存

World Wide Web(WWW)は、独自の情報リソースです。 ただし、驚異的な速度でコンテンツが失われるため、そのレガシーはリスクが高くなります。 多くの文化遺産および学術機関、非営利団体、民間企業が、関連する問題を調査し、Webアーカイブの技術的ソリューションの開発に貢献してきました。

Webページの保存またはWebアーカイブは、World Wide Webからデータを収集し、データをアーカイブに保存し、将来の研究者、歴史家、一般の人々が利用できるようにするプロセスです。 ウェブページの保存にさらに進む前に、以下に示すように、ウェブページの保存に関連するいくつかの重要な問題を議論しましょう-

  • * Webリソースの変更*-Webリソースは日々変化し続けており、これはWebページの保存の課題です。
  • 大量のリソース-Webページの保存に関連する別の問題は、大量のリソースが保存されることです。
  • 整合性-Webページは、その完全性を保護するために、不正な修正、削除、または削除から保護する必要があります。
  • マルチメディアデータの取り扱い-Webページを保存している間、マルチメディアデータも処理する必要があり、これらを行うと問題が発生する可能性があります。
  • アクセスの提供-保存のほかに、Webリソースへのアクセスを提供し、所有権の問題に対処する問題も解決する必要があります。

この章では、Webページの保存に Beautiful Soup という名前のPythonライブラリを使用します。

美しいスープとは?

Beautiful Soupは、HTMLおよびXMLファイルからデータを引き出すためのPythonライブラリです。 Webページ自体を取得できないため、スープオブジェクトを作成するには入力(ドキュメントまたはURL)が必要なため、 urlib で使用できます。 これについて詳しくはhttps://www.crummy.com/software/BeautifulSoup/bs4/doc/.[www.crummy.com/software/BeautifulSoup/bs4/doc/]で学ぶことができます。

それを使用する前に、次のコマンドを使用してサードパーティのライブラリをインストールする必要があることに注意してください-

pip install bs4

次に、Anacondaパッケージマネージャーを使用して、Beautiful Soupを次のようにインストールできます-

conda install -c anaconda beautifulsoup4

Webページを保持するためのPythonスクリプト

Beautiful Soupと呼ばれるサードパーティのライブラリを使用してWebページを保存するための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__)

このスクリプトは2つの位置引数を取ることに注意してください。1つは保存されるURLであり、もう1つは以下に示すように望ましい出力ディレクトリです-

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)

ここで、次のように入力URLの追加の検証とともに実際の名前の前に不要な要素を削除することにより、Webサイトのベース名を抽出する* main()*関数を定義します-

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))

コードの次の行には、以下で説明する3つの関数が含まれます-

  • * write_output()*は、最初のWebページを出力ディレクトリに書き込みます
  • このWebページ上のリンクを識別する* find_links()*関数
  • * recurse_pages()*関数は、Webページ上のすべてのリンクを反復して検出します。
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)

私たちは、Webページに関するいくつかの詳細を記録する必要があり、その後、次のように* 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)))

今、私たちは UTF-8 エンコードされたデータを読み取り、次のようにその SHA-256 ハッシュを生成する助けを借りて* hash_data()*メソッドを定義します-

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()

さて、次のように* find_links()メソッドの下でWebページのデータから *Beautifulsoup オブジェクトを作成しましょう-

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

さて、次のようにリンク名、ページデータ、出力ディレクトリとカウンターを渡すことにより、ファイル内のアクセスされた各Webページの出力を書きます-

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

これで、WebサイトのURL、出力ディレクトリ、およびログファイルへのパスを指定してこのスクリプトを実行すると、将来の使用に使用できるWebページに関する詳細が取得されます。

ウイルスハンティング

法医学アナリスト、セキュリティ研究者、インシデント対応者が有用なソフトウェアとマルウェアの違いをどのように理解できるのか疑問に思ったことはありませんか? 答えは質問そのものにあります。なぜなら、ハッカーによって急速に生成されるマルウェアについて研究しなければ、研究者や専門家が有用なソフトウェアとマルウェアの違いを知ることはまったく不可能だからです。 このセクションでは、このタスクを実行するツールである VirusShare について説明します。

VirusShareを理解する

VirusShareは、セキュリティ研究者、インシデントレスポンダー、フォレンジックアナリストにライブの悪意のあるコードのサンプルを提供するマルウェアサンプルの最大規模の非公開コレクションです。 3,000万を超えるサンプルが含まれています。

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

このスクリプトは、ハッシュセットの目的のパスになる位置引数を1つ取ります-

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)

次に、ダウンロードしたページから最新のハッシュリストを特定します。 これを行うには、VirusShareハッシュリストへのHTML href タグの最後のインスタンスを検索します。 それは次のコード行で行うことができます-

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ハッシュ値を含む最新のハッシュリストが取得されます。