Autopsy  4.10.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.text.ParseException;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.logging.Level;
38 import javax.xml.parsers.ParserConfigurationException;
39 import org.apache.commons.io.FilenameUtils;
40 import org.openide.util.NbBundle.Messages;
50 import org.sleuthkit.datamodel.AbstractFile;
51 import org.sleuthkit.datamodel.BlackboardArtifact;
52 import org.sleuthkit.datamodel.BlackboardAttribute;
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: {0}", 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: {0}", 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: {0}", ex); //NON-NLS
141  }
142 
143  progressBar.progress(Bundle.Progress_Message_Safari_Cookies());
144  try {
145  processBinaryCookieFile(dataSource, context);
146  } catch (IOException | TskCoreException ex) {
147  this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Cookies());
148  LOG.log(Level.SEVERE, "Exception thrown while processing Safari cookies file: {0}", 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, IOException {
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  getCookies(context, file);
265  }
266  }
267 
276  private void getHistory(IngestJobContext context, AbstractFile historyFile) throws TskCoreException, IOException {
277  if (historyFile.getSize() == 0) {
278  return;
279  }
280 
281  File tempHistoryFile = createTemporaryFile(context, historyFile);
282 
283  try {
284  ContentUtils.writeToFile(historyFile, tempHistoryFile, context::dataSourceIngestIsCancelled);
285  } catch (IOException ex) {
286  throw new IOException("Error writingToFile: " + historyFile, ex); //NON-NLS
287  }
288 
289  try {
290  Collection<BlackboardArtifact> bbartifacts = getHistoryArtifacts(historyFile, tempHistoryFile.toPath());
291  if (!bbartifacts.isEmpty()) {
292  services.fireModuleDataEvent(new ModuleDataEvent(
293  RecentActivityExtracterModuleFactory.getModuleName(),
294  BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, bbartifacts));
295  }
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  Collection<BlackboardArtifact> bbartifacts = getBookmarkArtifacts(file, tempFile);
323  if (!bbartifacts.isEmpty()) {
324  services.fireModuleDataEvent(new ModuleDataEvent(
325  RecentActivityExtracterModuleFactory.getModuleName(),
326  BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bbartifacts));
327  }
328  } finally {
329  tempFile.delete();
330  }
331 
332  }
333 
347  private void getDownloads(Content dataSource, IngestJobContext context, AbstractFile file) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException {
348  if (file.getSize() == 0) {
349  return;
350  }
351 
352  File tempFile = createTemporaryFile(context, file);
353 
354  try {
355  Collection<BlackboardArtifact> bbartifacts = getDownloadArtifacts(dataSource, file, tempFile);
356  if (!bbartifacts.isEmpty()) {
357  services.fireModuleDataEvent(new ModuleDataEvent(
358  RecentActivityExtracterModuleFactory.getModuleName(),
359  BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts));
360  }
361  } finally {
362  if (tempFile != null) {
363  tempFile.delete();
364  }
365  }
366 
367  }
368 
378  private void getCookies(IngestJobContext context, AbstractFile file) throws TskCoreException, IOException {
379  if (file.getSize() == 0) {
380  return;
381  }
382 
383  File tempFile = null;
384 
385  try {
386  tempFile = createTemporaryFile(context, file);
387 
388  Collection<BlackboardArtifact> bbartifacts = getCookieArtifacts(file, tempFile);
389 
390  if (!bbartifacts.isEmpty()) {
391  services.fireModuleDataEvent(new ModuleDataEvent(
392  RecentActivityExtracterModuleFactory.getModuleName(),
393  BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, bbartifacts));
394  }
395  } finally {
396  if (tempFile != null) {
397  tempFile.delete();
398  }
399  }
400  }
401 
412  private Collection<BlackboardArtifact> getHistoryArtifacts(AbstractFile origFile, Path tempFilePath) throws TskCoreException {
413  List<HashMap<String, Object>> historyList = this.dbConnect(tempFilePath.toString(), HISTORY_QUERY);
414 
415  if (historyList == null || historyList.isEmpty()) {
416  return null;
417  }
418 
419  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
420  for (HashMap<String, Object> row : historyList) {
421  String url = row.get(HEAD_URL).toString();
422  String title = row.get(HEAD_TITLE).toString();
423  Long time = (Double.valueOf(row.get(HEAD_TIME).toString())).longValue();
424 
425  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY);
426  bbart.addAttributes(createHistoryAttribute(url, time, null, title,
427  this.getName(), NetworkUtils.extractDomain(url), null));
428  bbartifacts.add(bbart);
429  }
430 
431  return bbartifacts;
432  }
433 
447  private Collection<BlackboardArtifact> getBookmarkArtifacts(AbstractFile origFile, File tempFile) throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
448  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
449 
450  try {
451  NSDictionary root = (NSDictionary) PropertyListParser.parse(tempFile);
452 
453  parseBookmarkDictionary(bbartifacts, origFile, root);
454  } catch (PropertyListFormatException ex) {
455  PropertyListFormatException plfe = new PropertyListFormatException(origFile.getName() + ": " + ex.getMessage());
456  plfe.setStackTrace(ex.getStackTrace());
457  throw plfe;
458  } catch (ParseException ex) {
459  ParseException pe = new ParseException(origFile.getName() + ": " + ex.getMessage(), ex.getErrorOffset());
460  pe.setStackTrace(ex.getStackTrace());
461  throw pe;
462  } catch (ParserConfigurationException ex) {
463  ParserConfigurationException pce = new ParserConfigurationException(origFile.getName() + ": " + ex.getMessage());
464  pce.setStackTrace(ex.getStackTrace());
465  throw pce;
466  } catch (SAXException ex) {
467  SAXException se = new SAXException(origFile.getName() + ": " + ex.getMessage());
468  se.setStackTrace(ex.getStackTrace());
469  throw se;
470  }
471 
472  return bbartifacts;
473  }
474 
488  private Collection<BlackboardArtifact> getDownloadArtifacts(Content dataSource, AbstractFile origFile, File tempFile)throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException {
489  Collection<BlackboardArtifact> bbartifacts = null;
490 
491  try {
492  while(true){
493  NSDictionary root = (NSDictionary)PropertyListParser.parse(tempFile);
494 
495  if(root == null)
496  break;
497 
498  NSArray nsArray = (NSArray)root.get(PLIST_KEY_DOWNLOAD_HISTORY);
499 
500  if(nsArray == null)
501  break;
502 
503  NSObject[] objectArray = nsArray.getArray();
504  bbartifacts = new ArrayList<>();
505 
506  for(NSObject obj: objectArray){
507  if(obj instanceof NSDictionary){
508  bbartifacts.addAll(parseDownloadDictionary(dataSource, origFile, (NSDictionary)obj));
509  }
510  }
511  break;
512  }
513 
514  } catch (PropertyListFormatException ex) {
515  PropertyListFormatException plfe = new PropertyListFormatException(origFile.getName() + ": " + ex.getMessage());
516  plfe.setStackTrace(ex.getStackTrace());
517  throw plfe;
518  } catch (ParseException ex) {
519  ParseException pe = new ParseException(origFile.getName() + ": " + ex.getMessage(), ex.getErrorOffset());
520  pe.setStackTrace(ex.getStackTrace());
521  throw pe;
522  } catch (ParserConfigurationException ex) {
523  ParserConfigurationException pce = new ParserConfigurationException(origFile.getName() + ": " + ex.getMessage());
524  pce.setStackTrace(ex.getStackTrace());
525  throw pce;
526  } catch (SAXException ex) {
527  SAXException se = new SAXException(origFile.getName() + ": " + ex.getMessage());
528  se.setStackTrace(ex.getStackTrace());
529  throw se;
530  }
531 
532  return bbartifacts;
533  }
534 
545  private Collection<BlackboardArtifact> getCookieArtifacts(AbstractFile origFile, File tempFile) throws TskCoreException, IOException {
546  Collection<BlackboardArtifact> bbartifacts = null;
547  BinaryCookieReader reader = BinaryCookieReader.initalizeReader(tempFile);
548 
549  if (reader != null) {
550  bbartifacts = new ArrayList<>();
551 
552  Iterator<Cookie> iter = reader.iterator();
553  while (iter.hasNext()) {
554  Cookie cookie = iter.next();
555 
556  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE);
557  bbart.addAttributes(createCookieAttributes(cookie.getURL(), cookie.getCreationDate(), cookie.getName(), cookie.getValue(), this.getName(), NetworkUtils.extractDomain(cookie.getURL())));
558  bbartifacts.add(bbart);
559  }
560  }
561 
562  return bbartifacts;
563  }
564 
574  private void parseBookmarkDictionary(Collection<BlackboardArtifact> bbartifacts, AbstractFile origFile, NSDictionary root) throws TskCoreException {
575  if (root.containsKey(PLIST_KEY_CHILDREN)) {
576  NSArray children = (NSArray) root.objectForKey(PLIST_KEY_CHILDREN);
577 
578  if (children != null) {
579  for (NSObject obj : children.getArray()) {
580  parseBookmarkDictionary(bbartifacts, origFile, (NSDictionary) obj);
581  }
582  }
583  } else if (root.containsKey(PLIST_KEY_URL)) {
584  String url = null;
585  String title = null;
586 
587  NSString nsstr = (NSString) root.objectForKey(PLIST_KEY_URL);
588  if (nsstr != null) {
589  url = nsstr.toString();
590  }
591 
592  NSDictionary dic = (NSDictionary) root.get(PLIST_KEY_URI);
593 
594  nsstr = (NSString) root.objectForKey(PLIST_KEY_TITLE);
595 
596  if (nsstr != null) {
597  title = ((NSString) dic.get(PLIST_KEY_TITLE)).toString();
598  }
599 
600  if (url != null || title != null) {
601  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK);
602  bbart.addAttributes(createBookmarkAttributes(url, title, null, getName(), NetworkUtils.extractDomain(url)));
603  bbartifacts.add(bbart);
604  }
605  }
606  }
607 
617  private Collection<BlackboardArtifact> parseDownloadDictionary(Content dataSource, AbstractFile origFile, NSDictionary entry) throws TskCoreException {
618  Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
619  String url = null;
620  String path = null;
621  Long time = null;
622  Long pathID = null;
623 
624  FileManager fileManager = getCurrentCase().getServices().getFileManager();
625 
626  NSString nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_URL);
627  if (nsstring != null) {
628  url = nsstring.toString();
629  }
630 
631  nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_PATH);
632  if (nsstring != null) {
633  path = nsstring.toString();
634  pathID = Util.findID(dataSource, path);
635  }
636 
637  NSDate date = (NSDate) entry.get(PLIST_KEY_DOWNLOAD_DATE);
638  if (date != null) {
639  time = date.getDate().getTime();
640  }
641 
642  BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD);
643  bbart.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName()));
644  bbartifacts.add(bbart);
645 
646  // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it.
647  for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) {
648  BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
649  downloadSourceArt.addAttributes(createDownloadSourceAttributes(url));
650  bbartifacts.add(downloadSourceArt);
651  break;
652  }
653 
654  return bbartifacts;
655  }
656 }

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