From 9d2c87f03d077cf4fca1825b944fc42471bbd38c Mon Sep 17 00:00:00 2001 From: Kelly Kelly <kelly@basistech.com> Date: Mon, 25 Feb 2019 13:22:24 -0500 Subject: [PATCH] Plist jar moved to RecentActivities, Safari support for bookmarks and merged in changes from history branch --- RecentActivity/ivy.xml | 3 + RecentActivity/nbproject/project.properties | 1 + RecentActivity/nbproject/project.xml | 4 + .../autopsy/recentactivity/ExtractSafari.java | 190 +++++++++++++++++- 4 files changed, 188 insertions(+), 10 deletions(-) diff --git a/RecentActivity/ivy.xml b/RecentActivity/ivy.xml index 290c8371ea..ca95f14a98 100644 --- a/RecentActivity/ivy.xml +++ b/RecentActivity/ivy.xml @@ -6,4 +6,7 @@ <conf name="recent-activity"/> </configurations> + <dependencies> + <dependency conf="recent-activity->default" org="com.googlecode.plist" name="dd-plist" rev="1.20"/> + </dependencies> </ivy-module> diff --git a/RecentActivity/nbproject/project.properties b/RecentActivity/nbproject/project.properties index 9736070e53..b5f9e4cc71 100644 --- a/RecentActivity/nbproject/project.properties +++ b/RecentActivity/nbproject/project.properties @@ -1,3 +1,4 @@ +file.reference.dd-plist-1.20.jar=C:\\Users\\kelly\\Workspace\\autopsy\\RecentActivity\\release\\modules\\ext\\dd-plist-1.20.jar javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial license.file=../LICENSE-2.0.txt diff --git a/RecentActivity/nbproject/project.xml b/RecentActivity/nbproject/project.xml index 87619a8356..f397b6b23b 100644 --- a/RecentActivity/nbproject/project.xml +++ b/RecentActivity/nbproject/project.xml @@ -74,6 +74,10 @@ </dependency> </module-dependencies> <public-packages/> + <class-path-extension> + <runtime-relative-path>ext/dd-plist-1.20.jar</runtime-relative-path> + <binary-origin>C:\Users\kelly\Workspace\autopsy\RecentActivity\release\modules\ext\dd-plist-1.20.jar</binary-origin> + </class-path-extension> </data> </configuration> </project> diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java index 8bd52a1f2d..cc172d56e8 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java @@ -18,15 +18,22 @@ */ package org.sleuthkit.autopsy.recentactivity; +import com.dd.plist.NSArray; +import com.dd.plist.NSDictionary; +import com.dd.plist.NSObject; +import com.dd.plist.NSString; +import com.dd.plist.PropertyListFormatException; +import com.dd.plist.PropertyListParser; import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.nio.file.Paths; +import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.logging.Level; +import javax.xml.parsers.ParserConfigurationException; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.coreutils.Logger; @@ -39,6 +46,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; +import org.xml.sax.SAXException; /** * Extract the bookmarks, cookies, downloads and history from Safari @@ -52,22 +60,28 @@ final class ExtractSafari extends Extract { 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 private static final String HISTORY_FILE_NAME = "History.db"; //NON-NLS + private static final String BOOKMARK_FILE_NAME = "Bookmarks.plist"; //NON-NLS private static final String HEAD_URL = "url"; //NON-NLS private static final String HEAD_TITLE = "title"; //NON-NLS private static final String HEAD_TIME = "time"; //NON-NLS + private static final String PLIST_KEY_CHILDREN = "Children"; //NON-NLS + private static final String PLIST_KEY_URL = "URLString"; //NON-NLS + private static final String PLIST_KEY_URI = "URIDictionary"; //NON-NLS + private static final String PLIST_KEY_TITLE = "title"; //NON-NLS + private final Logger logger = Logger.getLogger(this.getClass().getName()); @Messages({ "ExtractSafari_Module_Name=Safari", - "ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files." - }) + "ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files.", + "ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files",}) /** - * Extract the bookmarks, cookies, downloads and history from Safari - * - */ + * Extract the bookmarks, cookies, downloads and history from Safari + * + */ ExtractSafari() { } @@ -83,9 +97,17 @@ void process(Content dataSource, IngestJobContext context) { try { processHistoryDB(dataSource, context); + } catch (IOException | TskCoreException ex) { this.addErrorMessage(Bundle.ExtractSafari_Error_Getting_History()); - logger.log(Level.SEVERE, "Exception thrown while processing history file: " + ex); //NON-NLS + logger.log(Level.SEVERE, "Exception thrown while processing history file: {0}", ex); //NON-NLS + } + + try { + processBookmarkPList(dataSource, context); + } catch (IOException | TskCoreException | SAXException | PropertyListFormatException | ParseException | ParserConfigurationException ex) { + this.addErrorMessage(Bundle.ExtractSafari_Error_Parsing_Bookmark()); + logger.log(Level.SEVERE, "Exception thrown while parsing Safari Bookmarks file: {0}", ex); //NON-NLS } } @@ -116,6 +138,37 @@ private void processHistoryDB(Content dataSource, IngestJobContext context) thro } } + /** + * + * @param dataSource + * @param context + * @throws TskCoreException + * @throws IOException + * @throws SAXException + * @throws PropertyListFormatException + * @throws ParseException + * @throws ParserConfigurationException + */ + private void processBookmarkPList(Content dataSource, IngestJobContext context) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException { + FileManager fileManager = getCurrentCase().getServices().getFileManager(); + + List<AbstractFile> files = fileManager.findFiles(dataSource, BOOKMARK_FILE_NAME); + + if (files == null || files.isEmpty()) { + return; + } + + this.setFoundData(true); + + for (AbstractFile file : files) { + if (context.dataSourceIngestIsCancelled()) { + break; + } + + getBookmarks(context, file); + } + } + /** * Creates a temporary copy of historyFile and creates a list of * BlackboardArtifacts for the history information in the file. @@ -129,7 +182,7 @@ private void getHistory(IngestJobContext context, AbstractFile historyFile) thro return; } - File tempHistoryFile = this.createTemporaryFile(context, historyFile); + File tempHistoryFile = createTemporaryFile(context, historyFile); try { ContentUtils.writeToFile(historyFile, tempHistoryFile, context::dataSourceIngestIsCancelled); @@ -149,14 +202,47 @@ private void getHistory(IngestJobContext context, AbstractFile historyFile) thro } } + /** + * Creates a temporary bookmark file from the AbstractFile and creates + * BlackboardArtifacts for the any bookmarks found. + * + * @param context IngestJobContext object + * @param file AbstractFile from case + * @throws TskCoreException + * @throws IOException + * @throws SAXException + * @throws PropertyListFormatException + * @throws ParseException + * @throws ParserConfigurationException + */ + private void getBookmarks(IngestJobContext context, AbstractFile file) throws TskCoreException, IOException, SAXException, PropertyListFormatException, ParseException, ParserConfigurationException { + if (file.getSize() == 0) { + return; + } + + File tempFile = createTemporaryFile(context, file); + + try { + Collection<BlackboardArtifact> bbartifacts = getBookmarkArtifacts(file, tempFile); + if (!bbartifacts.isEmpty()) { + services.fireModuleDataEvent(new ModuleDataEvent( + RecentActivityExtracterModuleFactory.getModuleName(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bbartifacts)); + } + } finally { + tempFile.delete(); + } + + } + /** * Queries the history db for the history information creating a list of * BlackBoardArtifact for each row returned from the db. * * @param origFile AbstractFile of the history file from the case * @param tempFilePath Path to temporary copy of the history db - * @return Blackboard Artifacts for the history db or null if there are - * no history artifacts + * @return Blackboard Artifacts for the history db or null if there are no + * history artifacts * @throws TskCoreException */ private Collection<BlackboardArtifact> getHistoryArtifacts(AbstractFile origFile, Path tempFilePath) throws TskCoreException { @@ -180,4 +266,88 @@ private Collection<BlackboardArtifact> getHistoryArtifacts(AbstractFile origFile return bbartifacts; } + + /** + * Parses the temporary version of bookmarks.plist and creates + * + * @param origFile The origFile Bookmark.plist file from the case + * @param tempFile The temporary local version of Bookmark.plist + * @return Collection of BlackboardArtifacts for the bookmarks in origFile + * @throws IOException + * @throws PropertyListFormatException + * @throws ParseException + * @throws ParserConfigurationException + * @throws SAXException + * @throws TskCoreException + */ + private Collection<BlackboardArtifact> getBookmarkArtifacts(AbstractFile origFile, File tempFile) throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException, TskCoreException { + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); + + try { + NSDictionary root = (NSDictionary) PropertyListParser.parse(tempFile); + + parseBookmarkDictionary(bbartifacts, origFile, root); + } catch (PropertyListFormatException ex) { + PropertyListFormatException plfe = new PropertyListFormatException(origFile.getName() + ": " + ex.getMessage()); + plfe.setStackTrace(ex.getStackTrace()); + throw plfe; + } catch (ParseException ex) { + ParseException pe = new ParseException(origFile.getName() + ": " + ex.getMessage(), ex.getErrorOffset()); + pe.setStackTrace(ex.getStackTrace()); + throw pe; + } catch (ParserConfigurationException ex) { + ParserConfigurationException pce = new ParserConfigurationException(origFile.getName() + ": " + ex.getMessage()); + pce.setStackTrace(ex.getStackTrace()); + throw pce; + } catch (SAXException ex) { + SAXException se = new SAXException(origFile.getName() + ": " + ex.getMessage()); + se.setStackTrace(ex.getStackTrace()); + throw se; + } + + return bbartifacts; + } + + /** + * Parses the plist object to find the bookmark child objects, then creates + * an artifact with the bookmark information + * + * @param bbartifacts BlackboardArtifact list to add new the artifacts to + * @param origFile The origFile Bookmark.plist file from the case + * @param root NSDictionary object to parse + * @throws TskCoreException + */ + private void parseBookmarkDictionary(Collection<BlackboardArtifact> bbartifacts, AbstractFile origFile, NSDictionary root) throws TskCoreException { + if (root.containsKey(PLIST_KEY_CHILDREN)) { + NSArray children = (NSArray) root.objectForKey(PLIST_KEY_CHILDREN); + + if (children != null) { + for (NSObject obj : children.getArray()) { + parseBookmarkDictionary(bbartifacts, origFile, (NSDictionary) obj); + } + } + } else if (root.containsKey(PLIST_KEY_URL)) { + String url = null; + String title = null; + + NSString nsstr = (NSString) root.objectForKey(PLIST_KEY_URL); + if (nsstr != null) { + url = nsstr.toString(); + } + + NSDictionary dic = (NSDictionary) root.get(PLIST_KEY_URI); + + nsstr = (NSString) root.objectForKey(PLIST_KEY_TITLE); + + if (nsstr != null) { + title = ((NSString) dic.get(PLIST_KEY_TITLE)).toString(); + } + + if (url != null || title != null) { + BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK); + bbart.addAttributes(createBookmarkAttributes(url, title, null, this.getName(), NetworkUtils.extractDomain(url))); + bbartifacts.add(bbart); + } + } + } } -- GitLab