Autopsy  4.12.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.Content;
54 import org.sleuthkit.datamodel.TskCoreException;
55 import org.xml.sax.SAXException;
56 
61 final class ExtractSafari extends Extract {
62 
63  private final IngestServices services = IngestServices.getInstance();
64 
65  // visit_time uses an epoch of Jan 1, 2001 thus the addition of 978307200
66  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
67 
68  private static final String HISTORY_FILE_NAME = "History.db"; //NON-NLS
69  private static final String BOOKMARK_FILE_NAME = "Bookmarks.plist"; //NON-NLS
70  private static final String DOWNLOAD_FILE_NAME = "Downloads.plist"; //NON-NLS
71  private static final String COOKIE_FILE_NAME = "Cookies.binarycookies"; //NON-NLS
72  private static final String COOKIE_FOLDER = "Cookies";
73  private static final String SAFARI_FOLDER = "Safari";
74 
75  private static final String HEAD_URL = "url"; //NON-NLS
76  private static final String HEAD_TITLE = "title"; //NON-NLS
77  private static final String HEAD_TIME = "time"; //NON-NLS
78 
79  private static final String PLIST_KEY_CHILDREN = "Children"; //NON-NLS
80  private static final String PLIST_KEY_URL = "URLString"; //NON-NLS
81  private static final String PLIST_KEY_URI = "URIDictionary"; //NON-NLS
82  private static final String PLIST_KEY_TITLE = "title"; //NON-NLS
83  private static final String PLIST_KEY_DOWNLOAD_URL = "DownloadEntryURL"; //NON-NLS
84  private static final String PLIST_KEY_DOWNLOAD_DATE = "DownloadEntryDateAddedKey"; //NON-NLS
85  private static final String PLIST_KEY_DOWNLOAD_PATH = "DownloadEntryPath"; //NON-NLS
86  private static final String PLIST_KEY_DOWNLOAD_HISTORY = "DownloadHistory"; //NON-NLS
87 
88  private static final Logger LOG = Logger.getLogger(ExtractSafari.class.getName());
89 
90  @Messages({
91  "ExtractSafari_Module_Name=Safari",
92  "ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files.",
93  "ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files",
94  "ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files",
95  "Progress_Message_Safari_History=Safari History",
96  "Progress_Message_Safari_Bookmarks=Safari Bookmarks",
97  "Progress_Message_Safari_Cookies=Safari Cookies",
98  "Progress_Message_Safari_Downloads=Safari Downloads",
99  })
100 
105  ExtractSafari() {
106 
107  }
108 
109  @Override
110  protected String getName() {
111  return Bundle.ExtractSafari_Module_Name();
112  }
113 
114  @Override
115  void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
116  setFoundData(false);
117 
118  progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
119  try {
120  processHistoryDB(dataSource, context);
121 
122  } catch (IOException | TskCoreException ex) {
123  this.addErrorMessage(Bundle.ExtractSafari_Error_Getting_History());
124  LOG.log(Level.SEVERE, "Exception thrown while processing history file.", ex); //NON-NLS
125  }
126 
127  progressBar.progress(Bundle.Progress_Message_Safari_Bookmarks());
128  try {
129  processBookmarkPList(dataSource, context);
130  } catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
131  this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
132  LOG.log(Level.SEVERE, "Exception thrown while parsing Safari Bookmarks file.", ex); //NON-NLS
133  }
134 
135  progressBar.progress(Bundle.Progress_Message_Safari_Downloads());
136  try {
137  processDownloadsPList(dataSource, context);
138  } catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) {
139  this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark());
140  LOG.log(Level.SEVERE, "Exception thrown while parsing Safari Download.plist file.", ex); //NON-NLS
141  }
142 
143  progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
144  try {
145  processBinaryCookieFile(dataSource, context);
146  } catch (TskCoreException ex) {
147  this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Cookies());
148  LOG.log(Level.SEVERE, "Exception thrown while processing Safari cookies file.", ex); //NON-NLS
149  }
150  }
151 
159  private void processHistoryDB(Content dataSource, IngestJobContext context) throws TskCoreException, IOException {
160  FileManager fileManager = getCurrentCase().getServices().getFileManager();
161 
162  List<AbstractFile> historyFiles = fileManager.findFiles(dataSource, HISTORY_FILE_NAME, SAFARI_FOLDER);
163 
164  if (historyFiles == null || historyFiles.isEmpty()) {
165  return;
166  }
167 
168  setFoundData(true);
169 
170  for (AbstractFile historyFile : historyFiles) {
171  if (context.dataSourceIngestIsCancelled()) {
172  break;
173  }
174 
175  getHistory(context, historyFile);
176  }
177  }
178 
190  private void processBookmarkPList(Content dataSource, IngestJobContext context) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
191  FileManager fileManager = getCurrentCase().getServices().getFileManager();
192 
193  List<AbstractFile> files = fileManager.findFiles(dataSource, BOOKMARK_FILE_NAME, SAFARI_FOLDER);
194 
195  if (files == null || files.isEmpty()) {
196  return;
197  }
198 
199  setFoundData(true);
200 
201  for (AbstractFile file : files) {
202  if (context.dataSourceIngestIsCancelled()) {
203  break;
204  }
205 
206  getBookmarks(context, file);
207  }
208  }
209 
222  private void processDownloadsPList(Content dataSource, IngestJobContext context) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
223  FileManager fileManager = getCurrentCase().getServices().getFileManager();
224 
225  List<AbstractFile> files = fileManager.findFiles(dataSource, DOWNLOAD_FILE_NAME, SAFARI_FOLDER);
226 
227  if (files == null || files.isEmpty()) {
228  return;
229  }
230 
231  setFoundData(true);
232 
233  for (AbstractFile file : files) {
234  if (context.dataSourceIngestIsCancelled()) {
235  break;
236  }
237 
238  getDownloads(dataSource, context, file);
239  }
240  }
241 
249  private void processBinaryCookieFile(Content dataSource, IngestJobContext context) throws TskCoreException {
250  FileManager fileManager = getCurrentCase().getServices().getFileManager();
251 
252  List<AbstractFile> files = fileManager.findFiles(dataSource, COOKIE_FILE_NAME, COOKIE_FOLDER);
253 
254  if (files == null || files.isEmpty()) {
255  return;
256  }
257 
258  setFoundData(true);
259 
260  for (AbstractFile file : files) {
261  if (context.dataSourceIngestIsCancelled()) {
262  break;
263  }
264  try {
265  getCookies(context, file);
266  } catch (IOException ex) {
267  LOG.log(Level.WARNING, String.format("Failed to get cookies from file %s", Paths.get(file.getUniquePath(), file.getName()).toString()), ex);
268  }
269  }
270  }
271 
280  private void getHistory(IngestJobContext context, AbstractFile historyFile) throws TskCoreException, IOException {
281  if (historyFile.getSize() == 0) {
282  return;
283  }
284 
285  File tempHistoryFile = createTemporaryFile(context, historyFile);
286 
287  try {
288  ContentUtils.writeToFile(historyFile, tempHistoryFile, context::dataSourceIngestIsCancelled);
289  } catch (IOException ex) {
290  throw new IOException("Error writingToFile: " + historyFile, ex); //NON-NLS
291  }
292 
293  try {
294  postArtifacts(getHistoryArtifacts(historyFile, tempHistoryFile.toPath(), context));
295  } finally {
296  tempHistoryFile.delete();
297  }
298  }
299 
313  private void getBookmarks(IngestJobContext context, AbstractFile file) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
314  if (file.getSize() == 0) {
315  return;
316  }
317 
318  File tempFile = createTemporaryFile(context, file);
319 
320  try {
321  postArtifacts(getBookmarkArtifacts(file, tempFile, context));
322  } finally {
323  tempFile.delete();
324  }
325 
326  }
327 
341  private void getDownloads(Content dataSource, IngestJobContext context, AbstractFile file) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
342  if (file.getSize() == 0) {
343  return;
344  }
345 
346  File tempFile = createTemporaryFile(context, file);
347 
348  try {
349  postArtifacts(getDownloadArtifacts(dataSource, file, tempFile));
350 
351  } finally {
352  if (tempFile != null) {
353  tempFile.delete();
354  }
355  }
356 
357  }
358 
368  private void getCookies(IngestJobContext context, AbstractFile file) throws TskCoreException, IOException {
369  if (file.getSize() == 0) {
370  return;
371  }
372 
373  File tempFile = null;
374 
375  try {
376  tempFile = createTemporaryFile(context, file);
377 
378  postArtifacts(getCookieArtifacts(file, tempFile, context));
379 
380  } finally {
381  if (tempFile != null) {
382  tempFile.delete();
383  }
384  }
385  }
386 
397  private Collection<BlackboardArtifact> getHistoryArtifacts(AbstractFile origFile, Path tempFilePath, IngestJobContext context) throws TskCoreException {
398  List<HashMap<String, Object>> historyList = this.dbConnect(tempFilePath.toString(), HISTORY_QUERY);
399 
400  if (historyList == null || historyList.isEmpty()) {
401  return null;
402  }
403 
404  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
405  for (HashMap<String, Object> row : historyList) {
406  if (context.dataSourceIngestIsCancelled()) {
407  return bbartifacts;
408  }
409 
410  String url = row.get(HEAD_URL).toString();
411  String title = row.get(HEAD_TITLE).toString();
412  Long time = (Double.valueOf(row.get(HEAD_TIME).toString())).longValue();
413 
414  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY);
415  bbart.addAttributes(createHistoryAttribute(url, time, null, title,
416  this.getName(), NetworkUtils.extractDomain(url), null));
417  bbartifacts.add(bbart);
418  }
419 
420  return bbartifacts;
421  }
422 
436  private Collection<BlackboardArtifact> getBookmarkArtifacts(AbstractFile origFile, File tempFile, IngestJobContext context) throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
437  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
438 
439  try {
440  NSDictionary root = (NSDictionary) PropertyListParser.parse(tempFile);
441 
442  parseBookmarkDictionary(bbartifacts, origFile, root, context);
443  } catch (PropertyListFormatException ex) {
444  PropertyListFormatException plfe = new PropertyListFormatException(origFile.getName() + ": " + ex.getMessage());
445  plfe.setStackTrace(ex.getStackTrace());
446  throw plfe;
447  } catch (ParseException ex) {
448  ParseException pe = new ParseException(origFile.getName() + ": " + ex.getMessage(), ex.getErrorOffset());
449  pe.setStackTrace(ex.getStackTrace());
450  throw pe;
451  } catch (ParserConfigurationException ex) {
452  ParserConfigurationException pce = new ParserConfigurationException(origFile.getName() + ": " + ex.getMessage());
453  pce.setStackTrace(ex.getStackTrace());
454  throw pce;
455  } catch (SAXException ex) {
456  SAXException se = new SAXException(origFile.getName() + ": " + ex.getMessage());
457  se.setStackTrace(ex.getStackTrace());
458  throw se;
459  }
460 
461  return bbartifacts;
462  }
463 
477  private Collection<BlackboardArtifact> getDownloadArtifacts(Content dataSource, AbstractFile origFile, File tempFile)throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
478  Collection<BlackboardArtifact> bbartifacts = null;
479 
480  try {
481  while(true){
482  NSDictionary root = (NSDictionary)PropertyListParser.parse(tempFile);
483 
484  if(root == null)
485  break;
486 
487  NSArray nsArray = (NSArray)root.get(PLIST_KEY_DOWNLOAD_HISTORY);
488 
489  if(nsArray == null)
490  break;
491 
492  NSObject[] objectArray = nsArray.getArray();
493  bbartifacts = new ArrayList<>();
494 
495  for(NSObject obj: objectArray){
496  if(obj instanceof NSDictionary){
497  bbartifacts.addAll(parseDownloadDictionary(dataSource, origFile, (NSDictionary)obj));
498  }
499  }
500  break;
501  }
502 
503  } catch (PropertyListFormatException ex) {
504  PropertyListFormatException plfe = new PropertyListFormatException(origFile.getName() + ": " + ex.getMessage());
505  plfe.setStackTrace(ex.getStackTrace());
506  throw plfe;
507  } catch (ParseException ex) {
508  ParseException pe = new ParseException(origFile.getName() + ": " + ex.getMessage(), ex.getErrorOffset());
509  pe.setStackTrace(ex.getStackTrace());
510  throw pe;
511  } catch (ParserConfigurationException ex) {
512  ParserConfigurationException pce = new ParserConfigurationException(origFile.getName() + ": " + ex.getMessage());
513  pce.setStackTrace(ex.getStackTrace());
514  throw pce;
515  } catch (SAXException ex) {
516  SAXException se = new SAXException(origFile.getName() + ": " + ex.getMessage());
517  se.setStackTrace(ex.getStackTrace());
518  throw se;
519  }
520 
521  return bbartifacts;
522  }
523 
534  private Collection<BlackboardArtifact> getCookieArtifacts(AbstractFile origFile, File tempFile, IngestJobContext context) throws TskCoreException, IOException {
535  Collection<BlackboardArtifact> bbartifacts = null;
536  BinaryCookieReader reader = BinaryCookieReader.initalizeReader(tempFile);
537 
538  if (reader != null) {
539  bbartifacts = new ArrayList<>();
540 
541  Iterator<Cookie> iter = reader.iterator();
542  while (iter.hasNext()) {
543  if (context.dataSourceIngestIsCancelled()) {
544  return bbartifacts;
545  }
546 
547  Cookie cookie = iter.next();
548 
549  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE);
550  bbart.addAttributes(createCookieAttributes(cookie.getURL(), cookie.getCreationDate(), cookie.getName(), cookie.getValue(), this.getName(), NetworkUtils.extractDomain(cookie.getURL())));
551  bbartifacts.add(bbart);
552  }
553  }
554 
555  return bbartifacts;
556  }
557 
567  private void parseBookmarkDictionary(Collection<BlackboardArtifact> bbartifacts, AbstractFile origFile, NSDictionary root, IngestJobContext context) throws TskCoreException {
568 
569  if (context.dataSourceIngestIsCancelled()) {
570  return;
571  }
572 
573  if (root.containsKey(PLIST_KEY_CHILDREN)) {
574  NSArray children = (NSArray) root.objectForKey(PLIST_KEY_CHILDREN);
575 
576  if (children != null) {
577  for (NSObject obj : children.getArray()) {
578  parseBookmarkDictionary(bbartifacts, origFile, (NSDictionary) obj, context);
579  }
580  }
581  } else if (root.containsKey(PLIST_KEY_URL)) {
582  String url = null;
583  String title = null;
584 
585  NSString nsstr = (NSString) root.objectForKey(PLIST_KEY_URL);
586  if (nsstr != null) {
587  url = nsstr.toString();
588  }
589 
590  NSDictionary dic = (NSDictionary) root.get(PLIST_KEY_URI);
591 
592  nsstr = (NSString) root.objectForKey(PLIST_KEY_TITLE);
593 
594  if (nsstr != null) {
595  title = ((NSString) dic.get(PLIST_KEY_TITLE)).toString();
596  }
597 
598  if (url != null || title != null) {
599  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK);
600  bbart.addAttributes(createBookmarkAttributes(url, title, null, getName(), NetworkUtils.extractDomain(url)));
601  bbartifacts.add(bbart);
602  }
603  }
604  }
605 
615  private Collection<BlackboardArtifact> parseDownloadDictionary(Content dataSource, AbstractFile origFile, NSDictionary entry) throws TskCoreException {
616  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
617  String url = null;
618  String path = null;
619  Long time = null;
620  Long pathID = null;
621 
622  FileManager fileManager = getCurrentCase().getServices().getFileManager();
623 
624  NSString nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_URL);
625  if (nsstring != null) {
626  url = nsstring.toString();
627  }
628 
629  nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_PATH);
630  if (nsstring != null) {
631  path = nsstring.toString();
632  pathID = Util.findID(dataSource, path);
633  }
634 
635  NSDate date = (NSDate) entry.get(PLIST_KEY_DOWNLOAD_DATE);
636  if (date != null) {
637  time = date.getDate().getTime();
638  }
639 
640  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD);
641  bbart.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName()));
642  bbartifacts.add(bbart);
643 
644  // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it.
645  for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) {
646  BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
647  downloadSourceArt.addAttributes(createDownloadSourceAttributes(url));
648  bbartifacts.add(downloadSourceArt);
649  break;
650  }
651 
652  return bbartifacts;
653  }
654 }

Copyright © 2012-2018 Basis Technology. Generated on: Wed Sep 18 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.