Artefatos importantes no Windows-II

Este capítulo fala sobre alguns artefatos mais importantes no Windows e seu método de extração usando Python.

Atividades do usuário

Tendo Windows NTUSER.DATarquivo para armazenar várias atividades do usuário. Cada perfil de usuário tem uma colmeiaNTUSER.DAT, que armazena as informações e configurações relacionadas especificamente a esse usuário. Portanto, é altamente útil para fins de investigação por analistas forenses.

O seguinte script Python irá analisar algumas das chaves de NTUSER.DATpara explorar as ações de um usuário no sistema. Antes de prosseguir, para o script Python, precisamos instalar módulos de terceiros, a saberRegistry, pytsk3, pyewf e Jinja2. Podemos usar pip para instalá-los.

Podemos seguir as seguintes etapas para extrair informações de NTUSER.DAT arquivo -

  • Primeiro, pesquise por todos NTUSER.DAT arquivos no sistema.

  • Em seguida, analise o WordWheelQuery, TypePath and RunMRU chave para cada NTUSER.DAT Arquivo.

  • Por fim, escreveremos esses artefatos, já processados, em um relatório HTML usando Jinja2 fmodule.

Código Python

Vamos ver como usar o código Python para esse propósito -

Em primeiro lugar, precisamos importar os seguintes módulos 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

Agora, forneça o argumento para o manipulador de linha de comando. Aqui, ele aceitará três argumentos - o primeiro é o caminho para o arquivo de evidência, o segundo é o tipo de arquivo de evidência e o terceiro é o caminho de saída desejado para o relatório HTML, conforme mostrado abaixo -

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)

Agora, vamos definir main() função para pesquisar todos NTUSER.DAT arquivos, como mostrado -

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

Agora, vamos tentar encontrar a chave em NTUSER.DAT arquivo e depois de encontrá-lo, defina as funções de processamento do usuário conforme mostrado abaixo -

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

Agora, passe o objeto de dicionário e seu caminho para write_html() método da seguinte forma -

write_html(report, nt_rec)

Agora, defina um método, que leva pytsk identificador de arquivo e lê-lo na classe Registry através do 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)

Agora, vamos definir a função que irá analisar e tratar WordWheelQuery chave de NTUSER.DAT arquivo da seguinte forma -

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

Agora, vamos definir a função que irá analisar e tratar TypedPaths chave de NTUSER.DAT arquivo da seguinte forma -

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

Agora, vamos definir a função que irá analisar e tratar RunMRU chave de NTUSER.DAT arquivo da seguinte forma -

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

Agora, a seguinte função tratará da criação do relatório 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 fim, podemos escrever um documento HTML para relatório. Após executar o script acima, obteremos as informações do arquivo NTUSER.DAT em formato de documento HTML.

Arquivos LINK

Os arquivos de atalho são criados quando um usuário ou o sistema operacional cria arquivos de atalho para os arquivos usados ​​com frequência, clicados duas vezes ou acessados ​​a partir de unidades do sistema, como armazenamento anexado. Esses tipos de arquivos de atalho são chamados de arquivos de link. Ao acessar esses arquivos de link, um investigador pode encontrar a atividade da janela, como a hora e o local de onde esses arquivos foram acessados.

Vamos discutir o script Python que podemos usar para obter as informações desses arquivos LINK do Windows.

Para o script Python, instale módulos de terceiros, nomeadamente pylnk, pytsk3, pyewf. Podemos seguir as seguintes etapas para extrair informações delnk arquivos

  • Primeiro, procure lnk arquivos dentro do sistema.

  • Em seguida, extraia as informações desse arquivo iterando por meio deles.

  • Agora, finalmente, precisamos dessas informações para um relatório CSV.

Código Python

Vamos ver como usar o código Python para esse propósito -

Primeiro, importe as seguintes bibliotecas Python -

from __future__ import print_function
from argparse import ArgumentParser

import csv
import StringIO

from utility.pytskutil import TSKUtil
import pylnk

Agora, forneça o argumento para o manipulador de linha de comando. Aqui, ele aceitará três argumentos - o primeiro é o caminho para o arquivo de evidência, o segundo é o tipo de arquivo de evidência e o terceiro é o caminho de saída desejado para o relatório CSV, conforme mostrado abaixo -

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)

Agora, interprete o arquivo de evidência criando um objeto de TSKUtil e iterar através do sistema de arquivos para encontrar arquivos que terminem com lnk. Isso pode ser feito definindomain() funcionar da seguinte forma -

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

Agora, com a ajuda do código a seguir, iremos iterar através lnk arquivos criando uma função da seguinte maneira -

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)

Agora precisamos definir duas funções, uma abrirá o pytsk objeto de arquivo e outro serão usados ​​para escrever o relatório CSV conforme mostrado abaixo -

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)

Depois de executar o script acima, obteremos as informações do lnk arquivos em um relatório CSV -

Pré-busca de arquivos

Sempre que um aplicativo é executado pela primeira vez em um local específico, o Windows cria prefetch files. Eles são usados ​​para acelerar o processo de inicialização do aplicativo. A extensão para esses arquivos é.PF e estes são armazenados no ”\Root\Windows\Prefetch” pasta.

Os especialistas forenses digitais podem revelar a evidência da execução do programa a partir de um local especificado, juntamente com os detalhes do usuário. Os arquivos de pré-busca são artefatos úteis para o examinador porque sua entrada permanece mesmo depois que o programa foi excluído ou desinstalado.

Vamos discutir o script Python que irá buscar informações dos arquivos de pré-busca do Windows conforme fornecido abaixo -

Para o script Python, instale módulos de terceiros, nomeadamente pylnk, pytsk3 e unicodecsv. Lembre-se de que já trabalhamos com essas bibliotecas nos scripts Python que discutimos nos capítulos anteriores.

Temos que seguir as etapas fornecidas abaixo para extrair informações de prefetch arquivos -

  • Primeiro, procure .pf arquivos de extensão ou os arquivos de pré-busca.

  • Agora, execute a verificação de assinatura para eliminar falsos positivos.

  • Em seguida, analise o formato de arquivo de pré-busca do Windows. Isso difere da versão do Windows. Por exemplo, para Windows XP é 17, para Windows Vista e Windows 7 é 23, 26 para Windows 8.1 e 30 para Windows 10.

  • Por último, escreveremos o resultado analisado em um arquivo CSV.

Código Python

Vamos ver como usar o código Python para esse propósito -

Primeiro, importe as seguintes bibliotecas 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

Agora, forneça um argumento para o manipulador de linha de comando. Aqui ele aceitará dois argumentos, o primeiro seria o caminho para o arquivo de evidência e o segundo seria o tipo de arquivo de evidência. Ele também aceita um argumento opcional para especificar o caminho para verificar os arquivos de pré-busca -

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)

Agora, interprete o arquivo de evidência criando um objeto de TSKUtil e iterar através do sistema de arquivos para encontrar arquivos que terminem com .pf. Isso pode ser feito definindomain() funcionar da seguinte forma -

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)

Agora, defina um método que fará a validação das assinaturas conforme mostrado abaixo -

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)

Agora, comece a processar os arquivos de pré-busca do Windows. Aqui estamos tomando o exemplo de arquivos de pré-busca do 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)

Agora, extraia os dados incorporados aos arquivos pré-buscados usando struct da seguinte maneira -

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 fornecemos a versão de pré-busca para o Windows XP, mas e se ele encontrar versões de pré-busca para outros Windows. Em seguida, ele deve exibir uma mensagem de erro da seguinte forma -

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)

Agora, defina o método para escrever o resultado no relatório CSV da seguinte maneira -

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)

Após executar o script acima, obteremos as informações dos arquivos de pré-busca da versão do Windows XP em uma planilha.