Python 디지털 네트워크 포렌식 II

이전 장에서는 Python을 사용하는 네트워크 포렌식의 일부 개념을 다루었습니다. 이 장에서는 Python을 사용하는 네트워크 포렌식을 더 심층적으로 이해하겠습니다.

아름다운 수프로 웹 페이지 보존

월드 와이드 웹 (WWW)은 고유 한 정보 자원입니다. 그러나 그 유산은 놀라운 속도로 콘텐츠 손실로 인해 높은 위험에 처해 있습니다. 많은 문화 유산 및 학술 기관, 비영리 단체 및 민간 기업이 관련 문제를 탐색하고 웹 아카이빙을위한 기술 솔루션 개발에 기여했습니다.

웹 페이지 보존 또는 웹 보관은 World Wide Web에서 데이터를 수집하여 데이터가 아카이브에 보존되도록하고 미래의 연구자, 역사가 및 대중이 사용할 수 있도록하는 프로세스입니다. 웹 페이지 보존에 대해 더 진행하기 전에 다음과 같이 웹 페이지 보존과 관련된 몇 가지 중요한 문제에 대해 논의하겠습니다.

  • Change in Web Resources − 웹 리소스는 매일 계속 변경되며 이는 웹 페이지 보존에 대한 도전입니다.

  • Large Quantity of Resources − 웹 페이지 보존과 관련된 또 다른 문제는 보존해야 할 자원의 양이 많다는 것입니다.

  • Integrity − 웹 페이지는 무결성을 보호하기 위해 무단 수정, 삭제 또는 제거로부터 보호되어야합니다.

  • Dealing with multimedia data − 웹 페이지를 보존하는 동안 멀티미디어 데이터도 처리해야하는데 이로 인해 문제가 발생할 수 있습니다.

  • Providing access − 보존 외에도 웹 리소스에 대한 액세스를 제공하고 소유권 문제를 처리하는 문제도 해결되어야합니다.

이 장에서는 다음과 같은 Python 라이브러리를 사용합니다. Beautiful Soup 웹 페이지 보존을 위해.

뷰티플 스프 란?

Beautiful Soup은 HTML 및 XML 파일에서 데이터를 가져 오기위한 Python 라이브러리입니다. 함께 사용할 수 있습니다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 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

이제 정의해야합니다. recurse_pages() 다음과 같이 웹 사이트 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 이해

VirusShare는 보안 연구원, 사고 대응 자 및 포렌식 분석가에게 라이브 악성 코드 샘플을 제공하는 개인 소유의 가장 큰 맬웨어 샘플 모음입니다. 3 천만 개 이상의 샘플을 포함합니다.

VirusShare의 이점은 무료로 사용할 수있는 맬웨어 해시 목록입니다. 누구나 이러한 해시를 사용하여 매우 포괄적 인 해시 세트를 만들고이를 사용하여 잠재적 인 악성 파일을 식별 할 수 있습니다. 그러나 VirusShare를 사용하기 전에 다음을 방문하는 것이 좋습니다.https://virusshare.com 상세 사항은.

Python을 사용하여 VirusShare에서 줄 바꿈으로 구분 된 해시 목록 만들기

VirusShare의 해시 목록은 X-way 및 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)

이제 정의해야합니다. 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 해시 페이지를 열어야합니다. urlib.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의 마지막 인스턴스를 찾아이를 수행 할 수 있습니다.hrefVirusShare 해시 목록에 태그를 추가합니다. 다음 코드 라인으로 수행 할 수 있습니다.

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 해시 값이 포함 된 최신 해시 목록이 표시됩니다.