Artefatti importanti in Windows-II

Questo capitolo parla di alcuni artefatti più importanti in Windows e del loro metodo di estrazione usando Python.

Attività degli utenti

Windows con NTUSER.DATfile per memorizzare varie attività dell'utente. Ogni profilo utente sta avendo come hiveNTUSER.DAT, che memorizza le informazioni e le configurazioni relative a quell'utente in modo specifico. Quindi, è molto utile ai fini delle indagini da parte degli analisti forensi.

Il seguente script Python analizzerà alcune delle chiavi di NTUSER.DATper esplorare le azioni di un utente sul sistema. Prima di procedere ulteriormente, per lo script Python, dobbiamo installare moduli di terze parti, vale a direRegistry, pytsk3, pyewf e Jinja2. Possiamo usare pip per installarli.

Possiamo seguire i seguenti passaggi per estrarre le informazioni da NTUSER.DAT file -

  • Per prima cosa, cerca tutto NTUSER.DAT file nel sistema.

  • Quindi analizza il file WordWheelQuery, TypePath and RunMRU chiave per ciascuno NTUSER.DAT file.

  • Infine scriveremo questi artefatti, già elaborati, in un report HTML utilizzando Jinja2 fmodule.

Codice Python

Vediamo come utilizzare il codice Python per questo scopo -

Prima di tutto, dobbiamo importare i seguenti moduli 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

Fornisci ora un argomento per il gestore della riga di comando. Qui accetterà tre argomenti: il primo è il percorso del file di prova, il secondo è il tipo di file di prova e il terzo è il percorso di output desiderato per il report HTML, come mostrato di seguito -

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)

Ora, definiamo main() funzione per cercare tutto NTUSER.DAT file, come mostrato -

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'}
   }

Ora proveremo a trovare la chiave in NTUSER.DAT file e una volta trovato, definisci le funzioni di elaborazione dell'utente come mostrato di seguito -

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

Ora, passa l'oggetto del dizionario e il suo percorso a write_html() metodo come segue -

write_html(report, nt_rec)

Ora, definisci un metodo, che richiede pytsk gestire il file e leggerlo nella classe Registry tramite StringIO classe.

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)

Ora definiremo la funzione che analizzerà e gestirà WordWheelQuery chiave da NTUSER.DAT file come segue -

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

Ora definiremo la funzione che analizzerà e gestirà TypedPaths chiave da NTUSER.DAT file come segue -

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

Ora definiremo la funzione che analizzerà e gestirà RunMRU chiave da NTUSER.DAT file come segue -

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

Ora, la seguente funzione gestirà la creazione del report 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)

Finalmente possiamo scrivere un documento HTML per il report. Dopo aver eseguito lo script precedente, otterremo le informazioni dal file NTUSER.DAT in formato documento HTML.

LINK file

I file di collegamenti vengono creati quando un utente o il sistema operativo crea file di collegamento per i file utilizzati di frequente, a cui si fa doppio clic o a cui si accede dalle unità di sistema come l'archiviazione collegata. Questi tipi di file di collegamento sono chiamati file di collegamento. Accedendo a questi file di collegamento, un investigatore può trovare l'attività della finestra come l'ora e la posizione da cui si è avuto accesso a questi file.

Parliamo dello script Python che possiamo usare per ottenere le informazioni da questi file LINK di Windows.

Per lo script Python, installa moduli di terze parti, vale a dire pylnk, pytsk3, pyewf. Possiamo seguire i seguenti passaggi per estrarre le informazioni dalnk File

  • Innanzitutto, cerca lnk file all'interno del sistema.

  • Quindi, estrai le informazioni da quel file scorrendole.

  • Ora, finalmente, abbiamo bisogno di queste informazioni per un rapporto CSV.

Codice Python

Vediamo come utilizzare il codice Python per questo scopo -

Innanzitutto, importa le seguenti librerie Python:

from __future__ import print_function
from argparse import ArgumentParser

import csv
import StringIO

from utility.pytskutil import TSKUtil
import pylnk

Fornisci ora l'argomento per il gestore della riga di comando. Qui accetterà tre argomenti: il primo è il percorso del file delle prove, il secondo è il tipo di file delle prove e il terzo è il percorso di output desiderato per il report CSV, come mostrato di seguito -

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)

Ora, interpreta il file delle prove creando un oggetto di TSKUtil e scorrere il file system per trovare i file che terminano con lnk. Può essere fatto definendomain() funzionare come segue -

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'
   ]

Ora con l'aiuto del codice seguente, itereremo lnk file creando una funzione come segue:

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)

Ora dobbiamo definire due funzioni, una aprirà il file pytsk oggetto file e altro verranno utilizzati per scrivere report CSV come mostrato di seguito -

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)

Dopo aver eseguito lo script sopra, otterremo le informazioni dal file discovery lnk file in un rapporto CSV -

Precarica file

Ogni volta che un'applicazione viene eseguita per la prima volta da una posizione specifica, Windows crea prefetch files. Questi vengono utilizzati per accelerare il processo di avvio dell'applicazione. L'estensione per questi file è.PF e questi vengono memorizzati nel file ”\Root\Windows\Prefetch” cartella.

Gli esperti forensi digitali possono rivelare le prove dell'esecuzione del programma da una posizione specificata insieme ai dettagli dell'utente. I file di precaricamento sono artefatti utili per l'esaminatore perché la loro voce rimane anche dopo che il programma è stato eliminato o disinstallato.

Parliamo dello script Python che recupererà le informazioni dai file di precaricamento di Windows come indicato di seguito:

Per lo script Python, installa moduli di terze parti, vale a dire pylnk, pytsk3 e unicodecsv. Ricordiamo che abbiamo già lavorato con queste librerie negli script Python che abbiamo discusso nei capitoli precedenti.

Dobbiamo seguire i passaggi indicati di seguito per estrarre le informazioni da prefetch file -

  • Innanzitutto, cerca .pf file di estensione o file di precaricamento.

  • Ora, esegui la verifica della firma per eliminare i falsi positivi.

  • Successivamente, analizza il formato di file di precaricamento di Windows. Questo è diverso con la versione di Windows. Ad esempio, per Windows XP è 17, per Windows Vista e Windows 7 è 23, 26 per Windows 8.1 e 30 per Windows 10.

  • Infine, scriveremo il risultato analizzato in un file CSV.

Codice Python

Vediamo come utilizzare il codice Python per questo scopo -

Innanzitutto, importa le seguenti librerie 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

Fornisci ora un argomento per il gestore della riga di comando. Qui accetterà due argomenti, il primo sarebbe il percorso del file delle prove e il secondo sarebbe il tipo di file delle prove. Accetta anche un argomento opzionale per specificare il percorso per la scansione dei file di precaricamento -

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)

Ora, interpreta il file delle prove creando un oggetto di TSKUtil e scorrere il file system per trovare i file che terminano con .pf. Può essere fatto definendomain() funzionare come segue -

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)

Ora, definisci un metodo che eseguirà la convalida delle firme come mostrato di seguito:

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)

Ora inizia a elaborare i file di precaricamento di Windows. Qui stiamo prendendo l'esempio dei file di precaricamento di 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)

Ora, estrai i dati incorporati nei file precaricati utilizzando la struttura come segue:

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 ]

Poiché abbiamo fornito la versione di precaricamento per Windows XP, ma cosa succederebbe se incontrasse versioni di prefetch per altri Windows. Quindi deve visualizzare un messaggio di errore come segue:

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)

Ora, definisci il metodo per scrivere il risultato nel rapporto CSV come segue:

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)

Dopo aver eseguito lo script precedente, otterremo le informazioni dai file di precaricamento della versione di Windows XP in un foglio di calcolo.