Python Digital Network Forensics-II
El capítulo anterior abordó algunos de los conceptos de análisis forense de redes utilizando Python. En este capítulo, comprendamos el análisis forense de redes usando Python a un nivel más profundo.
Preservación de la página web con Beautiful Soup
La World Wide Web (WWW) es un recurso de información único. Sin embargo, su legado corre un alto riesgo debido a la pérdida de contenido a un ritmo alarmante. Varias instituciones académicas y de patrimonio cultural, organizaciones sin fines de lucro y empresas privadas han explorado los problemas involucrados y han contribuido al desarrollo de soluciones técnicas para el archivo web.
La preservación de la página web o el archivo web es el proceso de recopilar los datos de la World Wide Web, garantizar que los datos se conserven en un archivo y ponerlos a disposición de futuros investigadores, historiadores y el público. Antes de continuar con la preservación de la página web, analicemos algunos temas importantes relacionados con la preservación de la página web como se indica a continuación:
Change in Web Resources - Los recursos web cambian todos los días, lo que supone un desafío para la preservación de la página web.
Large Quantity of Resources - Otro tema relacionado con la preservación de la página web es la gran cantidad de recursos que se desea preservar.
Integrity - Las páginas web deben estar protegidas de modificaciones, supresión o eliminación no autorizadas para proteger su integridad.
Dealing with multimedia data - Al preservar las páginas web, también debemos tratar con datos multimedia, y estos pueden causar problemas al hacerlo.
Providing access - Además de preservar, también debe resolverse el problema de proporcionar acceso a los recursos web y tratar los problemas de propiedad.
En este capítulo, usaremos la biblioteca de Python llamada Beautiful Soup para la conservación de la página web.
¿Qué es Beautiful Soup?
Beautiful Soup es una biblioteca de Python para extraer datos de archivos HTML y XML. Se puede utilizar conurlibporque necesita una entrada (documento o URL) para crear un objeto de sopa, ya que no puede recuperar la página web en sí. Puede obtener información detallada sobre esto en www.crummy.com/software/BeautifulSoup/bs4/doc/
Tenga en cuenta que antes de usarlo, debemos instalar una biblioteca de terceros usando el siguiente comando:
pip install bs4
A continuación, utilizando el administrador de paquetes Anaconda, podemos instalar Beautiful Soup de la siguiente manera:
conda install -c anaconda beautifulsoup4
Secuencia de comandos de Python para conservar páginas web
El script de Python para preservar páginas web mediante el uso de una biblioteca de terceros llamada Beautiful Soup se analiza aquí:
Primero, importe las bibliotecas necesarias de la siguiente manera:
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__)
Tenga en cuenta que este script tomará dos argumentos posicionales, uno es la URL que se debe conservar y el otro es el directorio de salida deseado, como se muestra a continuación:
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()
Ahora, configure el registro para el script especificando un archivo y un controlador de flujo para estar en bucle y documente el proceso de adquisición como se muestra:
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)
Ahora, hagamos la validación de entrada en el directorio de salida deseado de la siguiente manera:
if not os.path.exists(args.OUTPUT_DIR):
os.makedirs(args.OUTPUT_DIR)
main(args.DOMAIN, args.OUTPUT_DIR)
Ahora, definiremos el main() función que extraerá el nombre base del sitio web eliminando los elementos innecesarios antes del nombre real junto con una validación adicional en la URL de entrada de la siguiente manera:
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()
Ahora, necesitamos abrir una conexión con la URL usando el método urlopen (). Usemos el bloque try-except de la siguiente manera:
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))
Las siguientes líneas de código incluyen tres funciones como se explica a continuación:
write_output() para escribir la primera página web en el directorio de salida
find_links() función para identificar los enlaces en esta página web
recurse_pages() función para recorrer y descubrir todos los enlaces en la página 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))
Ahora, definamos write_output() método de la siguiente manera -
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)
Necesitamos registrar algunos detalles sobre la página web y luego registramos el hash de los datos usando hash_data() método de la siguiente manera -
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)))
Ahora, define hash_data() método con la ayuda del cual leemos el UTF-8 datos codificados y luego generar el SHA-256 hash de la siguiente manera:
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()
Ahora, creemos un Beautifulsoup objeto fuera de los datos de la página web bajo find_links() método de la siguiente manera -
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
Ahora, necesitamos definir recurse_pages() método proporcionándole las entradas de la URL del sitio web, la cola de enlaces actual, el contexto SSL no verificado y el directorio de salida de la siguiente manera:
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
Ahora, escriba la salida de cada página web a la que se accede en un archivo pasando el nombre del enlace, los datos de la página, el directorio de salida y el contador de la siguiente manera:
write_output(link, page, output_dir, counter)
queue = find_links(website, page, queue)
logger.info("Identified {} links throughout website".format(
len(queue)))
Ahora, cuando ejecutamos este script proporcionando la URL del sitio web, el directorio de salida y una ruta al archivo de registro, obtendremos los detalles sobre esa página web que se puede utilizar para uso futuro.
Caza de virus
¿Alguna vez se ha preguntado cómo los analistas forenses, los investigadores de seguridad y los encuestados de incidentes pueden comprender la diferencia entre software útil y malware? La respuesta está en la pregunta en sí, porque sin estudiar el malware, generado rápidamente por los piratas informáticos, es bastante imposible para los investigadores y especialistas distinguir entre software útil y malware. En esta sección, hablemos sobreVirusShare, una herramienta para realizar esta tarea.
Comprensión de VirusShare
VirusShare es la colección privada más grande de muestras de malware para proporcionar a los investigadores de seguridad, personal de respuesta a incidentes y analistas forenses las muestras de código malicioso activo. Contiene más de 30 millones de muestras.
El beneficio de VirusShare es la lista de hashes de malware que está disponible gratuitamente. Cualquiera puede usar estos hash para crear un conjunto de hash muy completo y usarlo para identificar archivos potencialmente maliciosos. Pero antes de usar VirusShare, le sugerimos que visitehttps://virusshare.com para más detalles.
Creación de una lista hash delimitada por líneas nuevas desde VirusShare con Python
Varias herramientas forenses como X-way y EnCase pueden utilizar una lista hash de VirusShare. En el script que se describe a continuación, automatizaremos la descarga de listas de hash de VirusShare para crear una lista de hash delimitada por líneas nuevas.
Para este script, necesitamos una biblioteca Python de terceros. tqdm que se puede descargar de la siguiente manera:
pip install tqdm
Tenga en cuenta que en este script, primero leeremos la página de hash de VirusShare e identificaremos dinámicamente la lista de hash más reciente. Luego inicializaremos la barra de progreso y descargaremos la lista hash en el rango deseado.
Primero, importe las siguientes bibliotecas:
from __future__ import print_function
import argparse
import os
import ssl
import sys
import tqdm
from urllib.request import urlopen
import urllib.error
Este script tomará un argumento posicional, que sería la ruta deseada para el conjunto de hash:
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()
Ahora, realizaremos la validación de entrada estándar de la siguiente manera:
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)
Ahora necesitamos definir main() funcionar con **kwargs como argumento porque esto creará un diccionario al que podemos hacer referencia para admitir los argumentos clave suministrados como se muestra a continuación:
def main(hashset, **kwargs):
url = "https://virusshare.com/hashes.4n6"
print("[+] Identifying hash set range from {}".format(url))
context = ssl._create_unverified_context()
Ahora, debemos abrir la página de hashes de VirusShare usando urlib.request.urlopen()método. Usaremos el bloque try-except de la siguiente manera:
try:
index = urlopen(url, context = context).read().decode("utf-8")
except urllib.error.HTTPError as e:
print("[-] Error accessing webpage - exiting..")
sys.exit(1)
Ahora, identifique la última lista hash de las páginas descargadas. Puede hacer esto buscando la última instancia del HTMLhrefetiqueta a la lista hash de VirusShare. Se puede hacer con las siguientes líneas de código:
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
Ahora usaremos tqdm.trange() método para crear un bucle y una barra de progreso de la siguiente manera:
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
Después de realizar los pasos anteriores con éxito, abriremos el archivo de texto del conjunto de hash en un modo + para agregarlo al final del archivo de texto.
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))
Después de ejecutar el script anterior, obtendrá la última lista de hash que contiene los valores de hash MD5 en formato de texto.