Analisi dei metadati incorporati

In questo capitolo apprenderemo in dettaglio come investigare i metadati incorporati usando Python digital forensics.

introduzione

I metadati incorporati sono le informazioni sui dati archiviati nello stesso file che contiene l'oggetto descritto da quei dati. In altre parole, sono le informazioni su una risorsa digitale memorizzata nel file digitale stesso. È sempre associato al file e non può mai essere separato.

In caso di analisi forense digitale, non possiamo estrarre tutte le informazioni su un particolare file. Dall'altro lato, i metadati incorporati possono fornirci informazioni fondamentali per l'indagine. Ad esempio, i metadati di un file di testo possono contenere informazioni sull'autore, la sua lunghezza, la data scritta e persino un breve riassunto su quel documento. Un'immagine digitale può includere metadati come la lunghezza dell'immagine, la velocità dell'otturatore, ecc.

Artefatti contenenti attributi di metadati e loro estrazione

In questa sezione, apprenderemo vari artefatti contenenti attributi di metadati e il loro processo di estrazione utilizzando Python.

Audio e video

Questi sono i due artefatti molto comuni che hanno i metadati incorporati. Questi metadati possono essere estratti a scopo di indagine.

È possibile utilizzare il seguente script Python per estrarre attributi o metadati comuni da file audio o MP3 e un video o un file MP4.

Nota che per questo script, abbiamo bisogno di installare una libreria Python di terze parti chiamata mutagen che ci consente di estrarre metadati da file audio e video. Può essere installato con l'aiuto del seguente comando:

pip install mutagen

Alcune delle librerie utili che dobbiamo importare per questo script Python sono le seguenti:

from __future__ import print_function

import argparse
import json
import mutagen

Il gestore della riga di comando prenderà un argomento che rappresenta il percorso dei file MP3 o MP4. Quindi, useremomutagen.file() metodo per aprire un handle al file come segue:

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Python Metadata Extractor')
   parser.add_argument("AV_FILE", help="File to extract metadata from")
   args = parser.parse_args()
   av_file = mutagen.File(args.AV_FILE)
   file_ext = args.AV_FILE.rsplit('.', 1)[-1]
   
   if file_ext.lower() == 'mp3':
      handle_id3(av_file)
   elif file_ext.lower() == 'mp4':
      handle_mp4(av_file)

Ora, dobbiamo usare due maniglie, una per estrarre i dati da MP3 e una per estrarre i dati dal file MP4. Possiamo definire queste maniglie come segue:

def handle_id3(id3_file):
   id3_frames = {'TIT2': 'Title', 'TPE1': 'Artist', 'TALB': 'Album','TXXX':
      'Custom', 'TCON': 'Content Type', 'TDRL': 'Date released','COMM': 'Comments',
         'TDRC': 'Recording Date'}
   print("{:15} | {:15} | {:38} | {}".format("Frame", "Description","Text","Value"))
   print("-" * 85)
   
   for frames in id3_file.tags.values():
      frame_name = id3_frames.get(frames.FrameID, frames.FrameID)
      desc = getattr(frames, 'desc', "N/A")
      text = getattr(frames, 'text', ["N/A"])[0]
      value = getattr(frames, 'value', "N/A")
      
      if "date" in frame_name.lower():
         text = str(text)
      print("{:15} | {:15} | {:38} | {}".format(
         frame_name, desc, text, value))
def handle_mp4(mp4_file):
   cp_sym = u"\u00A9"
   qt_tag = {
      cp_sym + 'nam': 'Title', cp_sym + 'art': 'Artist',
      cp_sym + 'alb': 'Album', cp_sym + 'gen': 'Genre',
      'cpil': 'Compilation', cp_sym + 'day': 'Creation Date',
      'cnID': 'Apple Store Content ID', 'atID': 'Album Title ID',
      'plID': 'Playlist ID', 'geID': 'Genre ID', 'pcst': 'Podcast',
      'purl': 'Podcast URL', 'egid': 'Episode Global ID',
      'cmID': 'Camera ID', 'sfID': 'Apple Store Country',
      'desc': 'Description', 'ldes': 'Long Description'}
genre_ids = json.load(open('apple_genres.json'))

Ora, dobbiamo scorrere questo file MP4 come segue:

print("{:22} | {}".format('Name', 'Value'))
print("-" * 40)

for name, value in mp4_file.tags.items():
   tag_name = qt_tag.get(name, name)
   
   if isinstance(value, list):
      value = "; ".join([str(x) for x in value])
   if name == 'geID':
      value = "{}: {}".format(
      value, genre_ids[str(value)].replace("|", " - "))
   print("{:22} | {}".format(tag_name, value))

Lo script sopra ci fornirà ulteriori informazioni sui file MP3 e MP4.

immagini

Le immagini possono contenere diversi tipi di metadati a seconda del formato del file. Tuttavia, la maggior parte delle immagini incorporano informazioni GPS. Possiamo estrarre queste informazioni GPS utilizzando librerie Python di terze parti. È possibile utilizzare il seguente script Python può essere utilizzato per fare lo stesso:

Innanzitutto, scarica la libreria python di terze parti denominata Python Imaging Library (PIL) come segue -

pip install pillow

Questo ci aiuterà a estrarre i metadati dalle immagini.

Possiamo anche scrivere i dettagli GPS incorporati nelle immagini in un file KML, ma per questo dobbiamo scaricare la libreria Python di terze parti denominata simplekml come segue -

pip install simplekml

In questo script, prima dobbiamo importare le seguenti librerie:

from __future__ import print_function
import argparse

from PIL import Image
from PIL.ExifTags import TAGS

import simplekml
import sys

Ora, il gestore della riga di comando accetterà un argomento posizionale che sostanzialmente rappresenta il percorso del file delle foto.

parser = argparse.ArgumentParser('Metadata from images')
parser.add_argument('PICTURE_FILE', help = "Path to picture")
args = parser.parse_args()

Ora, dobbiamo specificare gli URL che popoleranno le informazioni sulle coordinate. Gli URL sonogmaps e open_maps. Abbiamo anche bisogno di una funzione per convertire la coordinata della tupla gradi minuto secondo (DMS), fornita dalla libreria PIL, in decimale. Può essere fatto come segue:

gmaps = "https://www.google.com/maps?q={},{}"
open_maps = "http://www.openstreetmap.org/?mlat={}&mlon={}"

def process_coords(coord):
   coord_deg = 0
   
   for count, values in enumerate(coord):
      coord_deg += (float(values[0]) / values[1]) / 60**count
   return coord_deg

Ora useremo image.open() funzione per aprire il file come oggetto PIL.

img_file = Image.open(args.PICTURE_FILE)
exif_data = img_file._getexif()

if exif_data is None:
   print("No EXIF data found")
   sys.exit()
for name, value in exif_data.items():
   gps_tag = TAGS.get(name, name)
   if gps_tag is not 'GPSInfo':
      continue

Dopo aver trovato il file GPSInfo , memorizzeremo il riferimento GPS ed elaboreremo le coordinate con il process_coords() metodo.

lat_ref = value[1] == u'N'
lat = process_coords(value[2])

if not lat_ref:
   lat = lat * -1
lon_ref = value[3] == u'E'
lon = process_coords(value[4])

if not lon_ref:
   lon = lon * -1

Ora inizia kml oggetto da simplekml libreria come segue -

kml = simplekml.Kml()
kml.newpoint(name = args.PICTURE_FILE, coords = [(lon, lat)])
kml.save(args.PICTURE_FILE + ".kml")

Ora possiamo stampare le coordinate dalle informazioni elaborate come segue:

print("GPS Coordinates: {}, {}".format(lat, lon))
print("Google Maps URL: {}".format(gmaps.format(lat, lon)))
print("OpenStreetMap URL: {}".format(open_maps.format(lat, lon)))
print("KML File {} created".format(args.PICTURE_FILE + ".kml"))

Documenti PDF

I documenti PDF hanno un'ampia varietà di supporti, tra cui immagini, testo, moduli, ecc. Quando estraiamo metadati incorporati nei documenti PDF, possiamo ottenere i dati risultanti nel formato chiamato Extensible Metadata Platform (XMP). Possiamo estrarre i metadati con l'aiuto del seguente codice Python:

Innanzitutto, installa una libreria Python di terze parti denominata PyPDF2per leggere i metadati archiviati in formato XMP. Può essere installato come segue:

pip install PyPDF2

Ora importa le seguenti librerie per estrarre i metadati dai file PDF:

from __future__ import print_function
from argparse import ArgumentParser, FileType

import datetime
from PyPDF2 import PdfFileReader
import sys

Ora, il gestore della riga di comando accetterà un argomento posizionale che sostanzialmente rappresenta il percorso del file PDF.

parser = argparse.ArgumentParser('Metadata from PDF')
parser.add_argument('PDF_FILE', help='Path to PDF file',type=FileType('rb'))
args = parser.parse_args()

Ora possiamo usare getXmpMetadata() metodo per fornire un oggetto contenente i metadati disponibili come segue:

pdf_file = PdfFileReader(args.PDF_FILE)
xmpm = pdf_file.getXmpMetadata()

if xmpm is None:
   print("No XMP metadata found in document.")
   sys.exit()

Possiamo usare custom_print() metodo per estrarre e stampare i valori rilevanti come titolo, creatore, collaboratore ecc. come segue:

custom_print("Title: {}", xmpm.dc_title)
custom_print("Creator(s): {}", xmpm.dc_creator)
custom_print("Contributors: {}", xmpm.dc_contributor)
custom_print("Subject: {}", xmpm.dc_subject)
custom_print("Description: {}", xmpm.dc_description)
custom_print("Created: {}", xmpm.xmp_createDate)
custom_print("Modified: {}", xmpm.xmp_modifyDate)
custom_print("Event Dates: {}", xmpm.dc_date)

Possiamo anche definire custom_print() metodo nel caso in cui il PDF venga creato utilizzando più software come segue:

def custom_print(fmt_str, value):
   if isinstance(value, list):
      print(fmt_str.format(", ".join(value)))
   elif isinstance(value, dict):
      fmt_value = [":".join((k, v)) for k, v in value.items()]
      print(fmt_str.format(", ".join(value)))
   elif isinstance(value, str) or isinstance(value, bool):
      print(fmt_str.format(value))
   elif isinstance(value, bytes):
      print(fmt_str.format(value.decode()))
   elif isinstance(value, datetime.datetime):
      print(fmt_str.format(value.isoformat()))
   elif value is None:
      print(fmt_str.format("N/A"))
   else:
      print("warn: unhandled type {} found".format(type(value)))

Possiamo anche estrarre qualsiasi altra proprietà personalizzata salvata dal software come segue:

if xmpm.custom_properties:
   print("Custom Properties:")
   
   for k, v in xmpm.custom_properties.items():
      print("\t{}: {}".format(k, v))

Lo script sopra leggerà il documento PDF e stamperà i metadati memorizzati in formato XMP comprese alcune proprietà personalizzate memorizzate dal software con l'aiuto di cui è stato creato quel PDF.

File eseguibili di Windows

A volte potremmo incontrare un file eseguibile sospetto o non autorizzato. Ma ai fini dell'indagine può essere utile a causa dei metadati incorporati. Possiamo ottenere le informazioni come la sua posizione, il suo scopo e altri attributi come il produttore, la data di compilazione, ecc. Con l'aiuto del seguente script Python possiamo ottenere la data di compilazione, i dati utili dalle intestazioni e i simboli importati ed esportati.

A tale scopo, installa prima la libreria Python di terze parti pefile. Può essere fatto come segue:

pip install pefile

Una volta installato correttamente, importa le seguenti librerie come segue:

from __future__ import print_function

import argparse
from datetime import datetime
from pefile import PE

Ora, il gestore della riga di comando accetterà un argomento posizionale che sostanzialmente rappresenta il percorso del file eseguibile. Puoi anche scegliere lo stile di output, se ti serve in modo dettagliato e dettagliato o in modo semplificato. Per questo è necessario fornire un argomento opzionale come mostrato di seguito:

parser = argparse.ArgumentParser('Metadata from executable file')
parser.add_argument("EXE_FILE", help = "Path to exe file")
parser.add_argument("-v", "--verbose", help = "Increase verbosity of output",
action = 'store_true', default = False)
args = parser.parse_args()

Ora, caricheremo il file eseguibile di input utilizzando la classe PE. Useremo anche il dump dei dati eseguibili in un oggetto dizionariodump_dict() metodo.

pe = PE(args.EXE_FILE)
ped = pe.dump_dict()

Possiamo estrarre i metadati dei file di base come la paternità incorporata, la versione e il tempo di compilazione utilizzando il codice mostrato di seguito -

file_info = {}
for structure in pe.FileInfo:
   if structure.Key == b'StringFileInfo':
      for s_table in structure.StringTable:
         for key, value in s_table.entries.items():
            if value is None or len(value) == 0:
               value = "Unknown"
            file_info[key] = value
print("File Information: ")
print("==================")

for k, v in file_info.items():
   if isinstance(k, bytes):
      k = k.decode()
   if isinstance(v, bytes):
      v = v.decode()
   print("{}: {}".format(k, v))
comp_time = ped['FILE_HEADER']['TimeDateStamp']['Value']
comp_time = comp_time.split("[")[-1].strip("]")
time_stamp, timezone = comp_time.rsplit(" ", 1)
comp_time = datetime.strptime(time_stamp, "%a %b %d %H:%M:%S %Y")
print("Compiled on {} {}".format(comp_time, timezone.strip()))

Possiamo estrarre i dati utili dalle intestazioni come segue:

for section in ped['PE Sections']:
   print("Section '{}' at {}: {}/{} {}".format(
      section['Name']['Value'], hex(section['VirtualAddress']['Value']),
      section['Misc_VirtualSize']['Value'],
      section['SizeOfRawData']['Value'], section['MD5'])
   )

Ora, estrai l'elenco delle importazioni e delle esportazioni dai file eseguibili come mostrato di seguito:

if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
   print("\nImports: ")
   print("=========")
   
   for dir_entry in pe.DIRECTORY_ENTRY_IMPORT:
      dll = dir_entry.dll
      
      if not args.verbose:
         print(dll.decode(), end=", ")
         continue
      name_list = []
      
      for impts in dir_entry.imports:
         if getattr(impts, "name", b"Unknown") is None:
            name = b"Unknown"
         else:
            name = getattr(impts, "name", b"Unknown")
			name_list.append([name.decode(), hex(impts.address)])
      name_fmt = ["{} ({})".format(x[0], x[1]) for x in name_list]
      print('- {}: {}'.format(dll.decode(), ", ".join(name_fmt)))
   if not args.verbose:
      print()

Ora stampa exports, names e addresses utilizzando il codice come mostrato di seguito -

if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
   print("\nExports: ")
   print("=========")
   
   for sym in pe.DIRECTORY_ENTRY_EXPORT.symbols:
      print('- {}: {}'.format(sym.name.decode(), hex(sym.address)))

Lo script precedente estrarrà i metadati di base, le informazioni dalle intestazioni dai file eseguibili di Windows.

Metadati del documento di Office

La maggior parte del lavoro sul computer viene svolto in tre applicazioni di MS Office: Word, PowerPoint ed Excel. Questi file possiedono enormi metadati, che possono esporre informazioni interessanti sulla loro paternità e storia.

Si noti che i metadati dal formato 2007 di word (.docx), excel (.xlsx) e powerpoint (.pptx) sono archiviati in un file XML. Possiamo elaborare questi file XML in Python con l'aiuto del seguente script Python mostrato di seguito:

Innanzitutto, importa le librerie richieste come mostrato di seguito:

from __future__ import print_function
from argparse import ArgumentParser
from datetime import datetime as dt
from xml.etree import ElementTree as etree

import zipfile
parser = argparse.ArgumentParser('Office Document Metadata’)
parser.add_argument("Office_File", help="Path to office file to read")
args = parser.parse_args()

Ora controlla se il file è un file ZIP. Altrimenti, solleva un errore. Ora apri il file ed estrai gli elementi chiave per l'elaborazione utilizzando il seguente codice:

zipfile.is_zipfile(args.Office_File)
zfile = zipfile.ZipFile(args.Office_File)
core_xml = etree.fromstring(zfile.read('docProps/core.xml'))
app_xml = etree.fromstring(zfile.read('docProps/app.xml'))

Ora, crea un dizionario per avviare l'estrazione dei metadati -

core_mapping = {
   'title': 'Title',
   'subject': 'Subject',
   'creator': 'Author(s)',
   'keywords': 'Keywords',
   'description': 'Description',
   'lastModifiedBy': 'Last Modified By',
   'modified': 'Modified Date',
   'created': 'Created Date',
   'category': 'Category',
   'contentStatus': 'Status',
   'revision': 'Revision'
}

Uso iterchildren() metodo per accedere a ciascuno dei tag all'interno del file XML -

for element in core_xml.getchildren():
   for key, title in core_mapping.items():
      if key in element.tag:
         if 'date' in title.lower():
            text = dt.strptime(element.text, "%Y-%m-%dT%H:%M:%SZ")
         else:
            text = element.text
         print("{}: {}".format(title, text))

Allo stesso modo, fallo per il file app.xml che contiene informazioni statistiche sui contenuti del documento -

app_mapping = {
   'TotalTime': 'Edit Time (minutes)',
   'Pages': 'Page Count',
   'Words': 'Word Count',
   'Characters': 'Character Count',
   'Lines': 'Line Count',
   'Paragraphs': 'Paragraph Count',
   'Company': 'Company',
   'HyperlinkBase': 'Hyperlink Base',
   'Slides': 'Slide count',
   'Notes': 'Note Count',
   'HiddenSlides': 'Hidden Slide Count',
}
for element in app_xml.getchildren():
   for key, title in app_mapping.items():
      if key in element.tag:
         if 'date' in title.lower():
            text = dt.strptime(element.text, "%Y-%m-%dT%H:%M:%SZ")
         else:
            text = element.text
         print("{}: {}".format(title, text))

Ora, dopo aver eseguito lo script precedente, possiamo ottenere i diversi dettagli sul particolare documento. Tieni presente che possiamo applicare questo script solo a documenti di Office 2007 o versioni successive.