diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index 11651a5db3..715ec31dac 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -1,4 +1,5 @@ OpenIDE-Module-Display-Category=Ingest Module + OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. OpenIDE-Module-Name=KeywordSearch OptionsCategory_Name_KeywordSearchOptions=Keyword Search diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index a58bce53a5..521b871e47 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -47,8 +47,16 @@ ExtractSafari_Error_Getting_History=An error occurred while processing Safari hi ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files ExtractSafari_Module_Name=Safari +ExtractZone_Internet=Internet Zone +ExtractZone_Local_Intranet=Local Intranet Zone +ExtractZone_Local_Machine=Local Machine Zone +ExtractZone_process_errMsg=An error occured processing ':Zone.Indentifier' files. +ExtractZone_process_errMsg_find=A failure occured while searching for :Zone.Indentifier files. +ExtractZone_progress_Msg=Extracting :Zone.Identifer files +ExtractZone_Restricted=Restricted Sites Zone +ExtractZone_Trusted=Trusted Sites Zone OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web acitivity (sites visited, stored cookies, bookmarked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\n\The module currently supports Windows only disk images.\n\The plugin is also fully functional when deployed on Windows version of Autopsy. +OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\nThe module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web acitivity (sites visited, stored cookies, bookmarked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy. OpenIDE-Module-Name=RecentActivity OpenIDE-Module-Short-Description=Recent Activity finder ingest module Chrome.moduleName=Chrome @@ -176,7 +184,7 @@ SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}. SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine SearchEngineURLQueryAnalyzer.engineName.none=NONE SearchEngineURLQueryAnalyzer.domainSubStr.none=NONE -SearchEngineURLQueryAnalyzer.toString=Name: {0}\nDomain Substring: {1}\n\count: {2}\nSplit Tokens: \n{3} +SearchEngineURLQueryAnalyzer.toString=Name: {0}\nDomain Substring: {1}\ncount: {2}\nSplit Tokens: \n{3} SearchEngineURLQueryAnalyzer.parentModuleName.noSpace=RecentActivity SearchEngineURLQueryAnalyzer.parentModuleName=Recent Activity UsbDeviceIdMapper.parseAndLookup.text=Product: {0} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java new file mode 100755 index 0000000000..56c6748712 --- /dev/null +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java @@ -0,0 +1,388 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.recentactivity; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.logging.Level; +import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.NetworkUtils; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ReadContentInputStream; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Extract the :Zone.Indentifier alternate data stream files. A file with + * a :Zone.Indentifier extention contains information about the similarly + * named (with out zone identifer extension) downloaded file. + */ +final class ExtractZoneIdentifier extends Extract { + + private static final Logger LOG = Logger.getLogger(ExtractEdge.class.getName()); + + private static final String ZONE_IDENTIFIER_FILE = "%:Zone.Identifier"; //NON-NLS + private static final String ZONE_IDENTIFIER = ":Zone.Identifier"; //NON-NLS + + @Messages({ + "ExtractZone_process_errMsg_find=A failure occured while searching for :Zone.Indentifier files.", + "ExtractZone_process_errMsg=An error occured processing ':Zone.Indentifier' files.", + "ExtractZone_progress_Msg=Extracting :Zone.Identifer files" + }) + + @Override + void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) { + + progressBar.progress(Bundle.ExtractZone_progress_Msg()); + + List zoneFiles = null; + try { + zoneFiles = currentCase.getServices().getFileManager().findFiles(dataSource, ZONE_IDENTIFIER_FILE); + } catch (TskCoreException ex) { + addErrorMessage(Bundle.ExtractZone_process_errMsg_find()); + LOG.log(Level.SEVERE, "Unable to find zone identifier files, exception thrown. ", ex); // NON-NLS + } + + if (zoneFiles == null || zoneFiles.isEmpty()) { + return; + } + + Set knownPathIDs = null; + try { + knownPathIDs = getPathIDsForType(TSK_WEB_DOWNLOAD); + } catch (TskCoreException ex) { + addErrorMessage(Bundle.ExtractZone_process_errMsg()); + LOG.log(Level.SEVERE, "Failed to build PathIDs List for TSK_WEB_DOWNLOAD", ex); // NON-NLS + } + + if (knownPathIDs == null) { + return; + } + + Collection sourceArtifacts = new ArrayList<>(); + Collection downloadArtifacts = new ArrayList<>(); + + for (AbstractFile zoneFile : zoneFiles) { + try { + processZoneFile(context, dataSource, zoneFile, sourceArtifacts, downloadArtifacts, knownPathIDs); + } catch (TskCoreException ex) { + addErrorMessage(Bundle.ExtractZone_process_errMsg()); + String message = String.format("Failed to process zone identifier file %s", zoneFile.getName()); //NON-NLS + LOG.log(Level.WARNING, message, ex); + } + } + + IngestServices services = IngestServices.getInstance(); + + if (!sourceArtifacts.isEmpty()) { + services.fireModuleDataEvent(new ModuleDataEvent( + RecentActivityExtracterModuleFactory.getModuleName(), + TSK_DOWNLOAD_SOURCE, sourceArtifacts)); + } + + if (!downloadArtifacts.isEmpty()) { + services.fireModuleDataEvent(new ModuleDataEvent( + RecentActivityExtracterModuleFactory.getModuleName(), + TSK_WEB_DOWNLOAD, downloadArtifacts)); + } + } + + /** + * Process a single Zone Identifier file. + * + * @param context IngetJobContext + * @param dataSource Content + * @param zoneFile Zone Indentifier file + * @param sourceArtifacts List for TSK_DOWNLOAD_SOURCE artifacts + * @param downloadArtifacts List for TSK_WEB_DOWNLOAD aritfacts + * + * @throws TskCoreException + */ + private void processZoneFile(IngestJobContext context, Content dataSource, + AbstractFile zoneFile, Collection sourceArtifacts, + Collection downloadArtifacts, + Set knownPathIDs) throws TskCoreException { + + ZoneIdentifierInfo zoneInfo = null; + + try { + zoneInfo = new ZoneIdentifierInfo(zoneFile); + } catch (IOException ex) { + String message = String.format("Unable to parse temporary File for %s", zoneFile.getName()); //NON-NLS + LOG.log(Level.WARNING, message, ex); + } + + if (zoneInfo == null) { + return; + } + + AbstractFile downloadFile = getDownloadFile(dataSource, zoneFile); + + if (downloadFile != null) { + // Only create a new TSK_WEB_DOWNLOAD artifact if one does not exist for downloadFile + if (!knownPathIDs.contains(downloadFile.getDataSourceObjectId())) { + // The zone identifier file is the parent of this artifact + // because it is the file we parsed to get the data + BlackboardArtifact downloadBba = createDownloadArtifact(zoneFile, zoneInfo); + if (downloadBba != null) { + downloadArtifacts.add(downloadBba); + } + } + + // check if download has a child TSK_DOWNLOAD_SOURCE artifact, if not create one + if (downloadFile.getArtifactsCount(TSK_DOWNLOAD_SOURCE) == 0) { + BlackboardArtifact sourceBba = createDownloadSourceArtifact(downloadFile, zoneInfo); + if (sourceBba != null) { + sourceArtifacts.add(sourceBba); + } + } + } + } + + /** + * Find the file that the Zone.Identifer file was created alongside. + * + * @param dataSource Content + * @param zoneFile The zone identifier case file + * + * @return The downloaded file or null if a file was not found + * + * @throws TskCoreException + */ + private AbstractFile getDownloadFile(Content dataSource, AbstractFile zoneFile) throws TskCoreException { + AbstractFile downloadFile = null; + + org.sleuthkit.autopsy.casemodule.services.FileManager fileManager + = currentCase.getServices().getFileManager(); + + String downloadFileName = zoneFile.getName().replace(ZONE_IDENTIFIER, ""); //NON-NLS + + List fileList = fileManager.findFiles(dataSource, downloadFileName, zoneFile.getParentPath()); + + if (fileList.size() == 1) { + downloadFile = fileList.get(0); + + // Check that the download file and the zone file came from the same dir + if (!downloadFile.getParentPath().equals(zoneFile.getParentPath())) { + downloadFile = null; + } else if (zoneFile.getMetaAddr() != downloadFile.getMetaAddr()) { + downloadFile = null; + } + } + + return downloadFile; + } + + /** + * Create a Download Source Artifact for the given ZoneIdentifierInfo + * object. + * + * @param downloadFile AbstractFile representing the file downloaded, not + * the zone indentifier file. + * @param zoneInfo Zone Indentifer file wrapper object + * + * @return TSK_DOWNLOAD_SOURCE object for given parameters + */ + private BlackboardArtifact createDownloadSourceArtifact(AbstractFile downloadFile, ZoneIdentifierInfo zoneInfo) { + + Collection bbattributes = new ArrayList<>(); + + bbattributes.addAll(Arrays.asList( + new BlackboardAttribute(TSK_URL, + RecentActivityExtracterModuleFactory.getModuleName(), + StringUtils.defaultString(zoneInfo.getURL(), "")), + + new BlackboardAttribute(TSK_DOMAIN, + RecentActivityExtracterModuleFactory.getModuleName(), + (zoneInfo.getURL() != null) ? NetworkUtils.extractDomain(zoneInfo.getURL()) : ""), + + new BlackboardAttribute(TSK_LOCATION, + RecentActivityExtracterModuleFactory.getModuleName(), + StringUtils.defaultString(zoneInfo.getZoneIdAsString(), "")))); //NON-NLS + + return addArtifact(TSK_DOWNLOAD_SOURCE, downloadFile, bbattributes); + } + + /** + * Create a TSK_WEB_DOWNLOAD Artifact for the given zone indentifier file. + * + * @param zoneFile Zone identifier file + * @param zoneInfo ZoneIdentifierInfo file wrapper object + * + * @return BlackboardArifact for the given parameters + */ + private BlackboardArtifact createDownloadArtifact(AbstractFile zoneFile, ZoneIdentifierInfo zoneInfo) { + + Collection bbattributes = createDownloadAttributes( + null, null, + zoneInfo.getURL(), null, + (zoneInfo.getURL() != null ? NetworkUtils.extractDomain(zoneInfo.getURL()) : ""), + null); + return addArtifact(TSK_WEB_DOWNLOAD, zoneFile, bbattributes); + } + + /** + * Creates a list of PathIDs for the given Artifact type. + * + * @param type BlackboardArtifact.ARTIFACT_TYPE + * + * @return A list of PathIDs + * + * @throws TskCoreException + */ + private Set getPathIDsForType(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException { + Set idList = new HashSet<>(); + for (BlackboardArtifact artifact : currentCase.getSleuthkitCase().getBlackboardArtifacts(type)) { + BlackboardAttribute pathIDAttribute = artifact.getAttribute(new BlackboardAttribute.Type(TSK_PATH_ID)); + + if (pathIDAttribute != null) { + long contentID = pathIDAttribute.getValueLong(); + if (contentID != -1) { + idList.add(contentID); + } + } + } + return idList; + } + + @Messages({ + "ExtractZone_Local_Machine=Local Machine Zone", + "ExtractZone_Local_Intranet=Local Intranet Zone", + "ExtractZone_Trusted=Trusted Sites Zone", + "ExtractZone_Internet=Internet Zone", + "ExtractZone_Restricted=Restricted Sites Zone" + }) + + /** + * Wrapper class for information in the :ZoneIdentifier file. The + * Zone.Identifier file has a simple format of key=value. There + * are four known keys: ZoneId, ReferrerUrl, HostUrl, and + * LastWriterPackageFamilyName. Not all browsers will put all values in the + * file, in fact most will only supply the ZoneId. Only Edge supplies the + * LastWriterPackageFamilyName. + */ + private final static class ZoneIdentifierInfo { + + private static final String ZONE_ID = "ZoneId"; //NON-NLS + private static final String REFERRER_URL = "ReferrerUrl"; //NON-NLS + private static final String HOST_URL = "HostUrl"; //NON-NLS + private static final String FAMILY_NAME = "LastWriterPackageFamilyName"; //NON-NLS + + private final Properties properties = new Properties(null); + + /** + * Opens the zone file, reading for the key\value pairs and puts them + * into a HashMap. + * + * @param zoneFile The ZoneIdentifier file + * + * @throws FileNotFoundException + * @throws IOException + */ + ZoneIdentifierInfo(AbstractFile zoneFile) throws IOException { + properties.load(new ReadContentInputStream(zoneFile)); + } + + /** + * Get the integer zone id + * + * @return interger zone id or -1 if unknown + */ + private int getZoneId() { + int zoneValue = -1; + String value = properties.getProperty(ZONE_ID); + if (value != null) { + zoneValue = Integer.parseInt(value); + } + + return zoneValue; + } + + /** + * Get the string description of the zone id. + * + * @return String description or null if a zone id was not found + */ + private String getZoneIdAsString() { + switch (getZoneId()) { + case 0: + return Bundle.ExtractZone_Local_Machine(); + case 1: + return Bundle.ExtractZone_Local_Intranet(); + case 2: + return Bundle.ExtractZone_Trusted(); + case 3: + return Bundle.ExtractZone_Internet(); + case 4: + return Bundle.ExtractZone_Restricted(); + default: + return null; + } + } + + /** + * Get the URL from which the file was downloaded. + * + * @return String url or null if a host url was not found + */ + private String getURL() { + return properties.getProperty(HOST_URL); + } + + /** + * Get the referrer url. + * + * @return String url or null if a host url was not found + */ + private String getReferrer() { + return properties.getProperty(REFERRER_URL); + } + + /** + * Gets the string value for the key LastWriterPackageFamilyName. + * + * @return String value or null if the value was not found + */ + private String getFamilyName() { + return properties.getProperty(FAMILY_NAME); + } + } + +} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java index b81a512ecb..db815e9274 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java @@ -76,6 +76,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule { Extract osExtract = new ExtractOs(); Extract dataSourceAnalyzer = new DataSourceUsageAnalyzer(); Extract safari = new ExtractSafari(); + Extract zoneInfo = new ExtractZoneIdentifier(); extractors.add(chrome); extractors.add(firefox); @@ -87,6 +88,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule { extractors.add(registry); // this should run after quicker modules like the browser modules and needs to run before the DataSourceUsageAnalyzer extractors.add(osExtract); // this needs to run before the DataSourceUsageAnalyzer extractors.add(dataSourceAnalyzer); //this needs to run after ExtractRegistry and ExtractOs + extractors.add(zoneInfo); // this needs to run after the web browser modules browserExtractors.add(chrome); browserExtractors.add(firefox);