2019-04-05 14:57:19 -04:00

394 lines
15 KiB
Java
Executable File

/*
*
* 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 <i>:Zone.Indentifier<i> alternate data stream files. A file with
* a <i>:Zone.Indentifier<i> 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<AbstractFile> 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<Long> 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<BlackboardArtifact> sourceArtifacts = new ArrayList<>();
Collection<BlackboardArtifact> downloadArtifacts = new ArrayList<>();
for (AbstractFile zoneFile : zoneFiles) {
if (context.dataSourceIngestIsCancelled()) {
return;
}
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<BlackboardArtifact> sourceArtifacts,
Collection<BlackboardArtifact> downloadArtifacts,
Set<Long> 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<AbstractFile> 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<BlackboardAttribute> 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<BlackboardAttribute> 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<Long> getPathIDsForType(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
Set<Long> 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 \<i\>key\<i\>=\<i\>value\<i\>. 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);
}
}
}