Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractSafari.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2019 Basis Technology Corp.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.recentactivity;
20 
21 import com.dd.plist.NSArray;
22 import com.dd.plist.NSDate;
23 import com.dd.plist.NSDictionary;
24 import com.dd.plist.NSObject;
25 import com.dd.plist.NSString;
26 import com.dd.plist.PropertyListFormatException;
27 import com.dd.plist.PropertyListParser;
28 import java.io.File;
29 import java.io.IOException;
30 import java.nio.file.Path;
31 import java.nio.file.Paths;
32 import java.text.ParseException;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.logging.Level;
39 import javax.xml.parsers.ParserConfigurationException;
40 import org.apache.commons.io.FilenameUtils;
41 import org.openide.util.NbBundle.Messages;
51 import org.sleuthkit.datamodel.AbstractFile;
52 import org.sleuthkit.datamodel.BlackboardArtifact;
53 import org.sleuthkit.datamodel.BlackboardAttribute;
54 import org.sleuthkit.datamodel.Content;
55 import org.sleuthkit.datamodel.TskCoreException;
56 import org.xml.sax.SAXException;
57 
62 final class ExtractSafari extends Extract {
63 
64  private final IngestServices services = IngestServices.getInstance();
65 
66  // visit_time uses an epoch of Jan 1, 2001 thus the addition of 978307200
67  private static final String HISTORY_QUERY = "SELECT url, title, visit_time + 978307200 as time FROM 'history_items' JOIN history_visits ON history_item = history_items.id;"; //NON-NLS
68 
69  private static final String HISTORY_FILE_NAME = "History.db"; //NON-NLS
70  private static final String BOOKMARK_FILE_NAME = "Bookmarks.plist"; //NON-NLS
71  private static final String DOWNLOAD_FILE_NAME = "Downloads.plist"; //NON-NLS
72  private static final String COOKIE_FILE_NAME = "Cookies.binarycookies"; //NON-NLS
73  private static final String COOKIE_FOLDER = "Cookies";
74  private static final String SAFARI_FOLDER = "Safari";
75 
76  private static final String HEAD_URL = "url"; //NON-NLS
77  private static final String HEAD_TITLE = "title"; //NON-NLS
78  private static final String HEAD_TIME = "time"; //NON-NLS
79 
80  private static final String PLIST_KEY_CHILDREN = "Children"; //NON-NLS
81  private static final String PLIST_KEY_URL = "URLString"; //NON-NLS
82  private static final String PLIST_KEY_URI = "URIDictionary"; //NON-NLS
83  private static final String PLIST_KEY_TITLE = "title"; //NON-NLS
84  private static final String PLIST_KEY_DOWNLOAD_URL = "DownloadEntryURL"; //NON-NLS
85  private static final String PLIST_KEY_DOWNLOAD_DATE = "DownloadEntryDateAddedKey"; //NON-NLS
86  private static final String PLIST_KEY_DOWNLOAD_PATH = "DownloadEntryPath"; //NON-NLS
87  private static final String PLIST_KEY_DOWNLOAD_HISTORY = "DownloadHistory"; //NON-NLS
88 
89  private static final Logger LOG = Logger.getLogger(ExtractSafari.class.getName());
90 
91  @Messages({
92  "ExtractSafari_Module_Name=Safari",
93  "ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files.",
94  "ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files",
95  "ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files",
96  "Progress_Message_Safari_History=Safari History",
97  "Progress_Message_Safari_Bookmarks=Safari Bookmarks",
98  "Progress_Message_Safari_Cookies=Safari Cookies",
99  "Progress_Message_Safari_Downloads=Safari Downloads",
100  })
101 
106  ExtractSafari() {
107 
108  }
109 
110  @Override
111  protected String getName() {
112  return Bundle.ExtractSafari_Module_Name();
113  }
114 
115  @Override
116  void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
117  setFoundData(false);
118 
119  progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
120  try {
121  processHistoryDB(dataSource, context);
122 
123  } catch (IOException | TskCoreException ex) {
124  this.addErrorMessage(Bundle.ExtractSafari_Error_Getting_History());
125  LOG.log(Level.SEVERE, "Exception thrown while processing history file.", ex); //NON-NLS
126  }
127 
128  progressBar.progress(Bundle.Progress_Message_Safari_Bookmarks());
129  try {
130  processBookmarkPList(dataSource, context);
131  } catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
132  this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
133  LOG.log(Level.SEVERE, "Exception thrown while parsing Safari Bookmarks file.", ex); //NON-NLS
134  }
135 
136  progressBar.progress(Bundle.Progress_Message_Safari_Downloads());
137  try {
138  processDownloadsPList(dataSource, context);
139  } catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
140  this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
141  LOG.log(Level.SEVERE, "Exception thrown while parsing Safari Download.plist file.", ex); //NON-NLS
142  }
143 
144  progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
145  try {
146  processBinaryCookieFile(dataSource, context);
147  } catch (TskCoreException ex) {
148  this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Cookies());
149  LOG.log(Level.SEVERE, "Exception thrown while processing Safari cookies file.", ex); //NON-NLS
150  }
151  }
152 
160  private void processHistoryDB(Content dataSource, IngestJobContext context) throws TskCoreException, IOException {
161  FileManager fileManager = getCurrentCase().getServices().getFileManager();
162 
163  List<AbstractFile> historyFiles = fileManager.findFiles(dataSource, HISTORY_FILE_NAME, SAFARI_FOLDER);
164 
165  if (historyFiles == null || historyFiles.isEmpty()) {
166  return;
167  }
168 
169  setFoundData(true);
170 
171  for (AbstractFile historyFile : historyFiles) {
172  if (context.dataSourceIngestIsCancelled()) {
173  break;
174  }
175 
176  getHistory(context, historyFile);
177  }
178  }
179 
191  private void processBookmarkPList(Content dataSource, IngestJobContext context) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
192  FileManager fileManager = getCurrentCase().getServices().getFileManager();
193 
194  List<AbstractFile> files = fileManager.findFiles(dataSource, BOOKMARK_FILE_NAME, SAFARI_FOLDER);
195 
196  if (files == null || files.isEmpty()) {
197  return;
198  }
199 
200  setFoundData(true);
201 
202  for (AbstractFile file : files) {
203  if (context.dataSourceIngestIsCancelled()) {
204  break;
205  }
206 
207  getBookmarks(context, file);
208  }
209  }
210 
223  private void processDownloadsPList(Content dataSource, IngestJobContext context) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
224  FileManager fileManager = getCurrentCase().getServices().getFileManager();
225 
226  List<AbstractFile> files = fileManager.findFiles(dataSource, DOWNLOAD_FILE_NAME, SAFARI_FOLDER);
227 
228  if (files == null || files.isEmpty()) {
229  return;
230  }
231 
232  setFoundData(true);
233 
234  for (AbstractFile file : files) {
235  if (context.dataSourceIngestIsCancelled()) {
236  break;
237  }
238 
239  getDownloads(dataSource, context, file);
240  }
241  }
242 
250  private void processBinaryCookieFile(Content dataSource, IngestJobContext context) throws TskCoreException {
251  FileManager fileManager = getCurrentCase().getServices().getFileManager();
252 
253  List<AbstractFile> files = fileManager.findFiles(dataSource, COOKIE_FILE_NAME, COOKIE_FOLDER);
254 
255  if (files == null || files.isEmpty()) {
256  return;
257  }
258 
259  setFoundData(true);
260 
261  for (AbstractFile file : files) {
262  if (context.dataSourceIngestIsCancelled()) {
263  break;
264  }
265  try {
266  getCookies(context, file);
267  } catch (IOException ex) {
268  LOG.log(Level.WARNING, String.format("Failed to get cookies from file %s", Paths.get(file.getUniquePath(), file.getName()).toString()), ex);
269  }
270  }
271  }
272 
281  private void getHistory(IngestJobContext context, AbstractFile historyFile) throws TskCoreException, IOException {
282  if (historyFile.getSize() == 0) {
283  return;
284  }
285 
286  File tempHistoryFile = createTemporaryFile(context, historyFile);
287 
288  try {
289  ContentUtils.writeToFile(historyFile, tempHistoryFile, context::dataSourceIngestIsCancelled);
290  } catch (IOException ex) {
291  throw new IOException("Error writingToFile: " + historyFile, ex); //NON-NLS
292  }
293 
294  try {
295  postArtifacts(getHistoryArtifacts(historyFile, tempHistoryFile.toPath(), context));
296  } finally {
297  tempHistoryFile.delete();
298  }
299  }
300 
314  private void getBookmarks(IngestJobContext context, AbstractFile file) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
315  if (file.getSize() == 0) {
316  return;
317  }
318 
319  File tempFile = createTemporaryFile(context, file);
320 
321  try {
322  postArtifacts(getBookmarkArtifacts(file, tempFile, context));
323  } finally {
324  tempFile.delete();
325  }
326 
327  }
328 
342  private void getDownloads(Content dataSource, IngestJobContext context, AbstractFile file) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
343  if (file.getSize() == 0) {
344  return;
345  }
346 
347  File tempFile = createTemporaryFile(context, file);
348 
349  try {
350  postArtifacts(getDownloadArtifacts(dataSource, file, tempFile));
351 
352  } finally {
353  if (tempFile != null) {
354  tempFile.delete();
355  }
356  }
357 
358  }
359 
369  private void getCookies(IngestJobContext context, AbstractFile file) throws TskCoreException, IOException {
370  if (file.getSize() == 0) {
371  return;
372  }
373 
374  File tempFile = null;
375 
376  try {
377  tempFile = createTemporaryFile(context, file);
378 
379  postArtifacts(getCookieArtifacts(file, tempFile, context));
380 
381  } finally {
382  if (tempFile != null) {
383  tempFile.delete();
384  }
385  }
386  }
387 
398  private Collection<BlackboardArtifact> getHistoryArtifacts(AbstractFile origFile, Path tempFilePath, IngestJobContext context) throws TskCoreException {
399  List<HashMap<String, Object>> historyList = this.dbConnect(tempFilePath.toString(), HISTORY_QUERY);
400 
401  if (historyList == null || historyList.isEmpty()) {
402  return null;
403  }
404 
405  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
406  for (HashMap<String, Object> row : historyList) {
407  if (context.dataSourceIngestIsCancelled()) {
408  return bbartifacts;
409  }
410 
411  String url = row.get(HEAD_URL).toString();
412  String title = row.get(HEAD_TITLE).toString();
413  Long time = (Double.valueOf(row.get(HEAD_TIME).toString())).longValue();
414 
415  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY);
416  bbart.addAttributes(createHistoryAttribute(url, time, null, title,
417  this.getName(), NetworkUtils.extractDomain(url), null));
418  bbartifacts.add(bbart);
419  }
420 
421  return bbartifacts;
422  }
423 
437  private Collection<BlackboardArtifact> getBookmarkArtifacts(AbstractFile origFile, File tempFile, IngestJobContext context) throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
438  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
439 
440  try {
441  NSDictionary root = (NSDictionary) PropertyListParser.parse(tempFile);
442 
443  parseBookmarkDictionary(bbartifacts, origFile, root, context);
444  } catch (PropertyListFormatException ex) {
445  PropertyListFormatException plfe = new PropertyListFormatException(origFile.getName() + ": " + ex.getMessage());
446  plfe.setStackTrace(ex.getStackTrace());
447  throw plfe;
448  } catch (ParseException ex) {
449  ParseException pe = new ParseException(origFile.getName() + ": " + ex.getMessage(), ex.getErrorOffset());
450  pe.setStackTrace(ex.getStackTrace());
451  throw pe;
452  } catch (ParserConfigurationException ex) {
453  ParserConfigurationException pce = new ParserConfigurationException(origFile.getName() + ": " + ex.getMessage());
454  pce.setStackTrace(ex.getStackTrace());
455  throw pce;
456  } catch (SAXException ex) {
457  SAXException se = new SAXException(origFile.getName() + ": " + ex.getMessage());
458  se.setStackTrace(ex.getStackTrace());
459  throw se;
460  }
461 
462  return bbartifacts;
463  }
464 
478  private Collection<BlackboardArtifact> getDownloadArtifacts(Content dataSource, AbstractFile origFile, File tempFile)throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
479  Collection<BlackboardArtifact> bbartifacts = null;
480 
481  try {
482  while(true){
483  NSDictionary root = (NSDictionary)PropertyListParser.parse(tempFile);
484 
485  if(root == null)
486  break;
487 
488  NSArray nsArray = (NSArray)root.get(PLIST_KEY_DOWNLOAD_HISTORY);
489 
490  if(nsArray == null)
491  break;
492 
493  NSObject[] objectArray = nsArray.getArray();
494  bbartifacts = new ArrayList<>();
495 
496  for(NSObject obj: objectArray){
497  if(obj instanceof NSDictionary){
498  bbartifacts.addAll(parseDownloadDictionary(dataSource, origFile, (NSDictionary)obj));
499  }
500  }
501  break;
502  }
503 
504  } catch (PropertyListFormatException ex) {
505  PropertyListFormatException plfe = new PropertyListFormatException(origFile.getName() + ": " + ex.getMessage());
506  plfe.setStackTrace(ex.getStackTrace());
507  throw plfe;
508  } catch (ParseException ex) {
509  ParseException pe = new ParseException(origFile.getName() + ": " + ex.getMessage(), ex.getErrorOffset());
510  pe.setStackTrace(ex.getStackTrace());
511  throw pe;
512  } catch (ParserConfigurationException ex) {
513  ParserConfigurationException pce = new ParserConfigurationException(origFile.getName() + ": " + ex.getMessage());
514  pce.setStackTrace(ex.getStackTrace());
515  throw pce;
516  } catch (SAXException ex) {
517  SAXException se = new SAXException(origFile.getName() + ": " + ex.getMessage());
518  se.setStackTrace(ex.getStackTrace());
519  throw se;
520  }
521 
522  return bbartifacts;
523  }
524 
535  private Collection<BlackboardArtifact> getCookieArtifacts(AbstractFile origFile, File tempFile, IngestJobContext context) throws TskCoreException, IOException {
536  Collection<BlackboardArtifact> bbartifacts = null;
537  BinaryCookieReader reader = BinaryCookieReader.initalizeReader(tempFile);
538 
539  if (reader != null) {
540  bbartifacts = new ArrayList<>();
541 
542  Iterator<Cookie> iter = reader.iterator();
543  while (iter.hasNext()) {
544  if (context.dataSourceIngestIsCancelled()) {
545  return bbartifacts;
546  }
547 
548  Cookie cookie = iter.next();
549 
550  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE);
551  bbart.addAttributes(createCookieAttributes(cookie.getURL(), cookie.getCreationDate(), cookie.getName(), cookie.getValue(), this.getName(), NetworkUtils.extractDomain(cookie.getURL())));
552  bbartifacts.add(bbart);
553  }
554  }
555 
556  return bbartifacts;
557  }
558 
568  private void parseBookmarkDictionary(Collection<BlackboardArtifact> bbartifacts, AbstractFile origFile, NSDictionary root, IngestJobContext context) throws TskCoreException {
569 
570  if (context.dataSourceIngestIsCancelled()) {
571  return;
572  }
573 
574  if (root.containsKey(PLIST_KEY_CHILDREN)) {
575  NSArray children = (NSArray) root.objectForKey(PLIST_KEY_CHILDREN);
576 
577  if (children != null) {
578  for (NSObject obj : children.getArray()) {
579  parseBookmarkDictionary(bbartifacts, origFile, (NSDictionary) obj, context);
580  }
581  }
582  } else if (root.containsKey(PLIST_KEY_URL)) {
583  String url = null;
584  String title = null;
585 
586  NSString nsstr = (NSString) root.objectForKey(PLIST_KEY_URL);
587  if (nsstr != null) {
588  url = nsstr.toString();
589  }
590 
591  NSDictionary dic = (NSDictionary) root.get(PLIST_KEY_URI);
592 
593  nsstr = (NSString) root.objectForKey(PLIST_KEY_TITLE);
594 
595  if (nsstr != null) {
596  title = ((NSString) dic.get(PLIST_KEY_TITLE)).toString();
597  }
598 
599  if (url != null || title != null) {
600  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK);
601  bbart.addAttributes(createBookmarkAttributes(url, title, null, getName(), NetworkUtils.extractDomain(url)));
602  bbartifacts.add(bbart);
603  }
604  }
605  }
606 
616  private Collection<BlackboardArtifact> parseDownloadDictionary(Content dataSource, AbstractFile origFile, NSDictionary entry) throws TskCoreException {
617  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
618  String url = null;
619  String path = null;
620  Long time = null;
621  Long pathID = null;
622 
623  FileManager fileManager = getCurrentCase().getServices().getFileManager();
624 
625  NSString nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_URL);
626  if (nsstring != null) {
627  url = nsstring.toString();
628  }
629 
630  nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_PATH);
631  if (nsstring != null) {
632  path = nsstring.toString();
633  pathID = Util.findID(dataSource, path);
634  }
635 
636  NSDate date = (NSDate) entry.get(PLIST_KEY_DOWNLOAD_DATE);
637  if (date != null) {
638  time = date.getDate().getTime();
639  }
640 
641  BlackboardArtifact webDownloadArtifact = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD);
642  webDownloadArtifact.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName()));
643  bbartifacts.add(webDownloadArtifact);
644 
645  // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
646  for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) {
647  BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
648  associatedObjectArtifact.addAttribute(
649  new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
650  RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
651  bbartifacts.add(associatedObjectArtifact);
652  break;
653  }
654 
655  return bbartifacts;
656  }
657 }

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.