Artefactos importantes en Windows-II
Este capítulo habla sobre algunos artefactos más importantes en Windows y su método de extracción usando Python.
Actividades del usuario
Windows que tiene NTUSER.DATarchivo para almacenar diversas actividades del usuario. Cada perfil de usuario tiene colmena comoNTUSER.DAT, que almacena la información y configuraciones relacionadas específicamente con ese usuario. Por lo tanto, es muy útil para fines de investigación por parte de analistas forenses.
La siguiente secuencia de comandos de Python analizará algunas de las claves de NTUSER.DATpara explorar las acciones de un usuario en el sistema. Antes de continuar, para el script Python, necesitamos instalar módulos de terceros, a saberRegistry, pytsk3, pyewf y Jinja2. Podemos usar pip para instalarlos.
Podemos seguir los siguientes pasos para extraer información de NTUSER.DAT archivo -
Primero, busque todo NTUSER.DAT archivos en el sistema.
Luego analice el WordWheelQuery, TypePath and RunMRU clave para cada NTUSER.DAT archivo.
Por último, escribiremos estos artefactos, ya procesados, en un informe HTML utilizando Jinja2 fmodule.
Código Python
Veamos cómo usar el código Python para este propósito:
En primer lugar, necesitamos importar los siguientes módulos de Python:
from __future__ import print_function
from argparse import ArgumentParser
import os
import StringIO
import struct
from utility.pytskutil import TSKUtil
from Registry import Registry
import jinja2
Ahora, proporcione un argumento para el controlador de línea de comandos. Aquí aceptará tres argumentos: el primero es la ruta al archivo de evidencia, el segundo es el tipo de archivo de evidencia y el tercero es la ruta de salida deseada para el informe HTML, como se muestra a continuación.
if __name__ == '__main__':
parser = argparse.ArgumentParser('Information from user activities')
parser.add_argument('EVIDENCE_FILE',help = "Path to evidence file")
parser.add_argument('IMAGE_TYPE',help = "Evidence file format",choices = ('ewf', 'raw'))
parser.add_argument('REPORT',help = "Path to report file")
args = parser.parse_args()
main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.REPORT)
Ahora, definamos main() función para buscar todo NTUSER.DAT archivos, como se muestra -
def main(evidence, image_type, report):
tsk_util = TSKUtil(evidence, image_type)
tsk_ntuser_hives = tsk_util.recurse_files('ntuser.dat','/Users', 'equals')
nt_rec = {
'wordwheel': {'data': [], 'title': 'WordWheel Query'},
'typed_path': {'data': [], 'title': 'Typed Paths'},
'run_mru': {'data': [], 'title': 'Run MRU'}
}
Ahora, intentaremos encontrar la clave en NTUSER.DAT archivo y una vez que lo encuentre, defina las funciones de procesamiento del usuario como se muestra a continuación:
for ntuser in tsk_ntuser_hives:
uname = ntuser[1].split("/")
open_ntuser = open_file_as_reg(ntuser[2])
try:
explorer_key = open_ntuser.root().find_key("Software").find_key("Microsoft")
.find_key("Windows").find_key("CurrentVersion").find_key("Explorer")
except Registry.RegistryKeyNotFoundException:
continue
nt_rec['wordwheel']['data'] += parse_wordwheel(explorer_key, uname)
nt_rec['typed_path']['data'] += parse_typed_paths(explorer_key, uname)
nt_rec['run_mru']['data'] += parse_run_mru(explorer_key, uname)
nt_rec['wordwheel']['headers'] = \ nt_rec['wordwheel']['data'][0].keys()
nt_rec['typed_path']['headers'] = \ nt_rec['typed_path']['data'][0].keys()
nt_rec['run_mru']['headers'] = \ nt_rec['run_mru']['data'][0].keys()
Ahora, pase el objeto de diccionario y su ruta a write_html() método de la siguiente manera -
write_html(report, nt_rec)
Ahora, defina un método que requiera pytsk identificador de archivo y léalo en la clase Registry a través del StringIO clase.
def open_file_as_reg(reg_file):
file_size = reg_file.info.meta.size
file_content = reg_file.read_random(0, file_size)
file_like_obj = StringIO.StringIO(file_content)
return Registry.Registry(file_like_obj)
Ahora, definiremos la función que analizará y manejará WordWheelQuery clave de NTUSER.DAT archivo de la siguiente manera:
def parse_wordwheel(explorer_key, username):
try:
wwq = explorer_key.find_key("WordWheelQuery")
except Registry.RegistryKeyNotFoundException:
return []
mru_list = wwq.value("MRUListEx").value()
mru_order = []
for i in xrange(0, len(mru_list), 2):
order_val = struct.unpack('h', mru_list[i:i + 2])[0]
if order_val in mru_order and order_val in (0, -1):
break
else:
mru_order.append(order_val)
search_list = []
for count, val in enumerate(mru_order):
ts = "N/A"
if count == 0:
ts = wwq.timestamp()
search_list.append({
'timestamp': ts,
'username': username,
'order': count,
'value_name': str(val),
'search': wwq.value(str(val)).value().decode("UTF-16").strip("\x00")
})
return search_list
Ahora, definiremos la función que analizará y manejará TypedPaths clave de NTUSER.DAT archivo de la siguiente manera:
def parse_typed_paths(explorer_key, username):
try:
typed_paths = explorer_key.find_key("TypedPaths")
except Registry.RegistryKeyNotFoundException:
return []
typed_path_details = []
for val in typed_paths.values():
typed_path_details.append({
"username": username,
"value_name": val.name(),
"path": val.value()
})
return typed_path_details
Ahora, definiremos la función que analizará y manejará RunMRU clave de NTUSER.DAT archivo de la siguiente manera:
def parse_run_mru(explorer_key, username):
try:
run_mru = explorer_key.find_key("RunMRU")
except Registry.RegistryKeyNotFoundException:
return []
if len(run_mru.values()) == 0:
return []
mru_list = run_mru.value("MRUList").value()
mru_order = []
for i in mru_list:
mru_order.append(i)
mru_details = []
for count, val in enumerate(mru_order):
ts = "N/A"
if count == 0:
ts = run_mru.timestamp()
mru_details.append({
"username": username,
"timestamp": ts,
"order": count,
"value_name": val,
"run_statement": run_mru.value(val).value()
})
return mru_details
Ahora, la siguiente función manejará la creación del informe HTML:
def write_html(outfile, data_dict):
cwd = os.path.dirname(os.path.abspath(__file__))
env = jinja2.Environment(loader=jinja2.FileSystemLoader(cwd))
template = env.get_template("user_activity.html")
rendering = template.render(nt_data=data_dict)
with open(outfile, 'w') as open_outfile:
open_outfile.write(rendering)
Por fin podemos escribir un documento HTML para un informe. Después de ejecutar el script anterior, obtendremos la información del archivo NTUSER.DAT en formato de documento HTML.
LINK archivos
Los archivos de accesos directos se crean cuando un usuario o el sistema operativo crea archivos de acceso directo para los archivos que se utilizan con frecuencia, se hace doble clic o se accede a ellos desde las unidades del sistema, como el almacenamiento adjunto. Estos tipos de archivos de acceso directo se denominan archivos de enlace. Al acceder a estos archivos de enlace, un investigador puede encontrar la actividad de la ventana, como la hora y la ubicación desde donde se ha accedido a estos archivos.
Analicemos el script de Python que podemos usar para obtener la información de estos archivos LINK de Windows.
Para el script Python, instale módulos de terceros, a saber pylnk, pytsk3, pyewf. Podemos seguir los siguientes pasos para extraer información delnk archivos
Primero, busque lnk archivos dentro del sistema.
Luego, extraiga la información de ese archivo iterando a través de ellos.
Ahora, por fin necesitamos esta información para un informe CSV.
Código Python
Veamos cómo usar el código Python para este propósito:
Primero, importe las siguientes bibliotecas de Python:
from __future__ import print_function
from argparse import ArgumentParser
import csv
import StringIO
from utility.pytskutil import TSKUtil
import pylnk
Ahora, proporcione el argumento para el controlador de línea de comandos. Aquí aceptará tres argumentos: el primero es la ruta al archivo de evidencia, el segundo es el tipo de archivo de evidencia y el tercero es la ruta de salida deseada para el informe CSV, como se muestra a continuación.
if __name__ == '__main__':
parser = argparse.ArgumentParser('Parsing LNK files')
parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
parser.add_argument('IMAGE_TYPE', help = "Evidence file format",choices = ('ewf', 'raw'))
parser.add_argument('CSV_REPORT', help = "Path to CSV report")
args = parser.parse_args()
main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.CSV_REPORT)
Ahora, interprete el archivo de evidencia creando un objeto de TSKUtil e iterar a través del sistema de archivos para encontrar archivos que terminen con lnk. Se puede hacer definiendomain() funciona de la siguiente manera:
def main(evidence, image_type, report):
tsk_util = TSKUtil(evidence, image_type)
lnk_files = tsk_util.recurse_files("lnk", path="/", logic="endswith")
if lnk_files is None:
print("No lnk files found")
exit(0)
columns = [
'command_line_arguments', 'description', 'drive_serial_number',
'drive_type', 'file_access_time', 'file_attribute_flags',
'file_creation_time', 'file_modification_time', 'file_size',
'environmental_variables_location', 'volume_label',
'machine_identifier', 'local_path', 'network_path',
'relative_path', 'working_directory'
]
Ahora, con la ayuda del siguiente código, iteraremos a través de lnk archivos creando una función de la siguiente manera:
parsed_lnks = []
for entry in lnk_files:
lnk = open_file_as_lnk(entry[2])
lnk_data = {'lnk_path': entry[1], 'lnk_name': entry[0]}
for col in columns:
lnk_data[col] = getattr(lnk, col, "N/A")
lnk.close()
parsed_lnks.append(lnk_data)
write_csv(report, columns + ['lnk_path', 'lnk_name'], parsed_lnks)
Ahora necesitamos definir dos funciones, una abrirá el pytsk El objeto de archivo y otros se utilizarán para escribir el informe CSV como se muestra a continuación:
def open_file_as_lnk(lnk_file):
file_size = lnk_file.info.meta.size
file_content = lnk_file.read_random(0, file_size)
file_like_obj = StringIO.StringIO(file_content)
lnk = pylnk.file()
lnk.open_file_object(file_like_obj)
return lnk
def write_csv(outfile, fieldnames, data):
with open(outfile, 'wb') as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
Después de ejecutar el script anterior, obtendremos la información del descubrimiento lnk archivos en un informe CSV -
Precargar archivos
Siempre que una aplicación se ejecuta por primera vez desde una ubicación específica, Windows crea prefetch files. Se utilizan para acelerar el proceso de inicio de la aplicación. La extensión de estos archivos es.PF y estos se almacenan en el ”\Root\Windows\Prefetch” carpeta.
Los expertos forenses digitales pueden revelar la evidencia de la ejecución del programa desde una ubicación específica junto con los detalles del usuario. Los archivos de captación previa son artefactos útiles para el examinador porque su entrada permanece incluso después de que el programa se haya eliminado o desinstalado.
Analicemos el script de Python que obtendrá información de los archivos de recuperación previa de Windows como se indica a continuación:
Para el script Python, instale módulos de terceros, a saber pylnk, pytsk3 y unicodecsv. Recuerde que ya hemos trabajado con estas bibliotecas en los scripts de Python que hemos comentado en los capítulos anteriores.
Tenemos que seguir los pasos que se indican a continuación para extraer información de prefetch archivos -
Primero, busque .pf archivos de extensión o los archivos de captación previa.
Ahora, realice la verificación de la firma para eliminar falsos positivos.
A continuación, analice el formato de archivo de captación previa de Windows. Esto difiere con la versión de Windows. Por ejemplo, para Windows XP es 17, para Windows Vista y Windows 7 es 23, 26 para Windows 8.1 y 30 para Windows 10.
Por último, escribiremos el resultado analizado en un archivo CSV.
Código Python
Veamos cómo usar el código Python para este propósito:
Primero, importe las siguientes bibliotecas de Python:
from __future__ import print_function
import argparse
from datetime import datetime, timedelta
import os
import pytsk3
import pyewf
import struct
import sys
import unicodecsv as csv
from utility.pytskutil import TSKUtil
Ahora, proporcione un argumento para el controlador de línea de comandos. Aquí aceptará dos argumentos, el primero sería la ruta al archivo de pruebas y el segundo sería el tipo de archivo de pruebas. También acepta un argumento opcional para especificar la ruta para buscar archivos de captación previa:
if __name__ == "__main__":
parser = argparse.ArgumentParser('Parsing Prefetch files')
parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
parser.add_argument("OUTPUT_CSV", help = "Path to write output csv")
parser.add_argument("-d", help = "Prefetch directory to scan",default = "/WINDOWS/PREFETCH")
args = parser.parse_args()
if os.path.exists(args.EVIDENCE_FILE) and \
os.path.isfile(args.EVIDENCE_FILE):
main(args.EVIDENCE_FILE, args.TYPE, args.OUTPUT_CSV, args.d)
else:
print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
sys.exit(1)
Ahora, interprete el archivo de evidencia creando un objeto de TSKUtil e iterar a través del sistema de archivos para encontrar archivos que terminen con .pf. Se puede hacer definiendomain() funciona de la siguiente manera:
def main(evidence, image_type, output_csv, path):
tsk_util = TSKUtil(evidence, image_type)
prefetch_dir = tsk_util.query_directory(path)
prefetch_files = None
if prefetch_dir is not None:
prefetch_files = tsk_util.recurse_files(".pf", path=path, logic="endswith")
if prefetch_files is None:
print("[-] No .pf files found")
sys.exit(2)
print("[+] Identified {} potential prefetch files".format(len(prefetch_files)))
prefetch_data = []
for hit in prefetch_files:
prefetch_file = hit[2]
pf_version = check_signature(prefetch_file)
Ahora, defina un método que hará la validación de firmas como se muestra a continuación:
def check_signature(prefetch_file):
version, signature = struct.unpack("^<2i", prefetch_file.read_random(0, 8))
if signature == 1094927187:
return version
else:
return None
if pf_version is None:
continue
pf_name = hit[0]
if pf_version == 17:
parsed_data = parse_pf_17(prefetch_file, pf_name)
parsed_data.append(os.path.join(path, hit[1].lstrip("//")))
prefetch_data.append(parsed_data)
Ahora, comience a procesar archivos de recuperación previa de Windows. Aquí tomamos el ejemplo de los archivos de captación previa de Windows XP:
def parse_pf_17(prefetch_file, pf_name):
create = convert_unix(prefetch_file.info.meta.crtime)
modify = convert_unix(prefetch_file.info.meta.mtime)
def convert_unix(ts):
if int(ts) == 0:
return ""
return datetime.utcfromtimestamp(ts)
def convert_filetime(ts):
if int(ts) == 0:
return ""
return datetime(1601, 1, 1) + timedelta(microseconds=ts / 10)
Ahora, extraiga los datos incrustados dentro de los archivos precargados usando la estructura de la siguiente manera:
pf_size, name, vol_info, vol_entries, vol_size, filetime, \
count = struct.unpack("<i60s32x3iq16xi",prefetch_file.read_random(12, 136))
name = name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0]
vol_name_offset, vol_name_length, vol_create, \
vol_serial = struct.unpack("<2iqi",prefetch_file.read_random(vol_info, 20))
vol_serial = hex(vol_serial).lstrip("0x")
vol_serial = vol_serial[:4] + "-" + vol_serial[4:]
vol_name = struct.unpack(
"<{}s".format(2 * vol_name_length),
prefetch_file.read_random(vol_info + vol_name_offset,vol_name_length * 2))[0]
vol_name = vol_name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0]
return [
pf_name, name, pf_size, create,
modify, convert_filetime(filetime), count, vol_name,
convert_filetime(vol_create), vol_serial ]
Como hemos proporcionado la versión de búsqueda previa para Windows XP, pero ¿qué pasa si encuentra versiones de recuperación previa para otros Windows? Entonces debe mostrar un mensaje de error de la siguiente manera:
elif pf_version == 23:
print("[-] Windows Vista / 7 PF file {} -- unsupported".format(pf_name))
continue
elif pf_version == 26:
print("[-] Windows 8 PF file {} -- unsupported".format(pf_name))
continue
elif pf_version == 30:
print("[-] Windows 10 PF file {} -- unsupported".format(pf_name))
continue
else:
print("[-] Signature mismatch - Name: {}\nPath: {}".format(hit[0], hit[1]))
continue
write_output(prefetch_data, output_csv)
Ahora, defina el método para escribir el resultado en el informe CSV de la siguiente manera:
def write_output(data, output_csv):
print("[+] Writing csv report")
with open(output_csv, "wb") as outfile:
writer = csv.writer(outfile)
writer.writerow([
"File Name", "Prefetch Name", "File Size (bytes)",
"File Create Date (UTC)", "File Modify Date (UTC)",
"Prefetch Last Execution Date (UTC)",
"Prefetch Execution Count", "Volume", "Volume Create Date",
"Volume Serial", "File Path" ])
writer.writerows(data)
Después de ejecutar el script anterior, obtendremos la información de los archivos de recuperación previa de la versión de Windows XP en una hoja de cálculo.