diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
old mode 100644
new mode 100755
index 7b48606ea0..89cc99230e
--- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
@@ -23,6 +23,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.logging.Level;
+import org.apache.tika.exception.EncryptedDocumentException;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.AutoDetectParser;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.BodyContentHandler;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@@ -41,9 +47,11 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
/**
- * File ingest module to detect encryption.
+ * File ingest module to detect encryption and password protection.
*/
final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter {
@@ -73,9 +81,10 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
/**
* Create a EncryptionDetectionFileIngestModule object that will detect
- * files that are encrypted and create blackboard artifacts as appropriate.
- * The supplied EncryptionDetectionIngestJobSettings object is used to
- * configure the module.
+ * files that are either encrypted or password protected and create
+ * blackboard artifacts as appropriate. The supplied
+ * EncryptionDetectionIngestJobSettings object is used to configure the
+ * module.
*/
EncryptionDetectionFileIngestModule(EncryptionDetectionIngestJobSettings settings) {
minimumEntropy = settings.getMinimumEntropy();
@@ -101,13 +110,37 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
public IngestModule.ProcessResult process(AbstractFile file) {
try {
- if (isFileEncrypted(file)) {
- return flagFile(file);
+ /*
+ * Qualify the file type.
+ */
+ if (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
+ && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)
+ && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR)
+ && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR)
+ && (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || slackFilesAllowed)) {
+ /*
+ * Qualify the file against hash databases.
+ */
+ if (!file.getKnown().equals(TskData.FileKnown.KNOWN)) {
+ /*
+ * Qualify the MIME type.
+ */
+ String mimeType = fileTypeDetector.getMIMEType(file);
+ if (mimeType.equals("application/octet-stream")) {
+ if (isFileEncryptionSuspected(file)) {
+ return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED);
+ }
+ } else {
+ if (isFilePasswordProtected(file)) {
+ return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
+ }
+ }
+ }
}
- } catch (ReadContentInputStreamException ex) {
+ } catch (ReadContentInputStreamException | SAXException | TikaException ex) {
logger.log(Level.WARNING, String.format("Unable to read file '%s'", file.getParentPath() + file.getName()), ex);
return IngestModule.ProcessResult.ERROR;
- } catch (IOException | TskCoreException ex) {
+ } catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Unable to process file '%s'", file.getParentPath() + file.getName()), ex);
return IngestModule.ProcessResult.ERROR;
}
@@ -138,14 +171,15 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
/**
* Create a blackboard artifact.
*
- * @param The file to be processed.
+ * @param file The file to be processed.
+ * @param artifactType The type of artifact to create.
*
* @return 'OK' if the file was processed successfully, or 'ERROR' if there
* was a problem.
*/
- private IngestModule.ProcessResult flagFile(AbstractFile file) {
+ private IngestModule.ProcessResult flagFile(AbstractFile file, BlackboardArtifact.ARTIFACT_TYPE artifactType) {
try {
- BlackboardArtifact artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED);
+ BlackboardArtifact artifact = file.newArtifact(artifactType);
try {
/*
@@ -159,17 +193,19 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
/*
* Send an event to update the view with the new result.
*/
- services.fireModuleDataEvent(new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, Collections.singletonList(artifact)));
+ services.fireModuleDataEvent(new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), artifactType, Collections.singletonList(artifact)));
/*
* Make an ingest inbox message.
*/
StringBuilder detailsSb = new StringBuilder();
- detailsSb.append("File: ").append(file.getParentPath()).append(file.getName()).append("
\n");
- detailsSb.append("Entropy: ").append(calculatedEntropy);
+ detailsSb.append("File: ").append(file.getParentPath()).append(file.getName());
+ if (artifactType.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED)) {
+ detailsSb.append("
\n").append("Entropy: ").append(calculatedEntropy);
+ }
services.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(),
- "Encryption Detected Match: " + file.getName(),
+ artifactType.getDisplayName() + " Match: " + file.getName(),
detailsSb.toString(),
file.getName(),
artifact));
@@ -182,16 +218,86 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
}
/**
- * This method checks if the AbstractFile input is encrypted. Initial
- * qualifications require that it be an actual file that is not known, meets
- * file size requirements, and has a MIME type of
- * 'application/octet-stream'.
+ * This method checks if the AbstractFile input is password protected.
*
* @param file AbstractFile to be checked.
*
- * @return True if the AbstractFile is encrypted.
+ * @return True if the file is password protected.
+ *
+ * @throws ReadContentInputStreamException If there is a failure reading
+ * from the InputStream.
+ * @throws IOException If there is a failure closing or
+ * reading from the InputStream.
+ * @throws SAXException If there was an issue parsing the
+ * file with Tika.
+ * @throws TikaException If there was an issue parsing the
+ * file with Tika.
*/
- private boolean isFileEncrypted(AbstractFile file) throws ReadContentInputStreamException, IOException, TskCoreException {
+ private boolean isFilePasswordProtected(AbstractFile file) throws ReadContentInputStreamException, IOException, SAXException, TikaException {
+
+ boolean passwordProtected = false;
+
+ switch (file.getMIMEType()) {
+ case "application/x-ooxml-protected":
+ /*
+ * Office Open XML files that are password protected can be
+ * determined so simply by checking the MIME type.
+ */
+ passwordProtected = true;
+ break;
+
+ case "application/msword":
+ case "application/vnd.ms-excel":
+ case "application/vnd.ms-powerpoint":
+ /*
+ * A file of one of these types will be determined to be
+ * password protected or not by attempting to parse it via Tika.
+ */
+ InputStream in = null;
+ BufferedInputStream bin = null;
+
+ try {
+ in = new ReadContentInputStream(file);
+ bin = new BufferedInputStream(in);
+ ContentHandler handler = new BodyContentHandler(-1);
+ Metadata metadata = new Metadata();
+ metadata.add(Metadata.RESOURCE_NAME_KEY, file.getName());
+ AutoDetectParser parser = new AutoDetectParser();
+ parser.parse(bin, handler, metadata, new ParseContext());
+ } catch (EncryptedDocumentException ex) {
+ /*
+ * Office OLE2 file is determined to be password protected.
+ */
+ passwordProtected = true;
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ if (bin != null) {
+ bin.close();
+ }
+ }
+ }
+
+ return passwordProtected;
+ }
+
+ /**
+ * This method checks if the AbstractFile input is encrypted. It must meet
+ * file size requirements before its entropy is calculated. If the entropy
+ * result meets the minimum entropy value set, the file will be considered
+ * to be possibly encrypted.
+ *
+ * @param file AbstractFile to be checked.
+ *
+ * @return True if encryption is suspected.
+ *
+ * @throws ReadContentInputStreamException If there is a failure reading
+ * from the InputStream.
+ * @throws IOException If there is a failure closing or
+ * reading from the InputStream.
+ */
+ private boolean isFileEncryptionSuspected(AbstractFile file) throws ReadContentInputStreamException, IOException {
/*
* Criteria for the checks in this method are partially based on
* http://www.forensicswiki.org/wiki/TrueCrypt#Detection
@@ -200,55 +306,36 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
boolean possiblyEncrypted = false;
/*
- * Qualify the file type.
+ * Qualify the size.
*/
- if (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
- && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)
- && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR)
- && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR)
- && (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || slackFilesAllowed)) {
- /*
- * Qualify the file against hash databases.
- */
- if (!file.getKnown().equals(TskData.FileKnown.KNOWN)) {
+ long contentSize = file.getSize();
+ if (contentSize >= minimumFileSize) {
+ if (!fileSizeMultipleEnforced || (contentSize % FILE_SIZE_MODULUS) == 0) {
/*
- * Qualify the size.
+ * Qualify the entropy.
*/
- long contentSize = file.getSize();
- if (contentSize >= minimumFileSize) {
- if (!fileSizeMultipleEnforced || (contentSize % FILE_SIZE_MODULUS) == 0) {
- /*
- * Qualify the MIME type.
- */
- String mimeType = fileTypeDetector.getMIMEType(file);
- if (mimeType.equals("application/octet-stream")) {
- possiblyEncrypted = true;
- }
- }
+ calculatedEntropy = calculateEntropy(file);
+ if (calculatedEntropy >= minimumEntropy) {
+ possiblyEncrypted = true;
}
}
}
- if (possiblyEncrypted) {
- calculatedEntropy = calculateEntropy(file);
- if (calculatedEntropy >= minimumEntropy) {
- return true;
- }
- }
-
- return false;
+ return possiblyEncrypted;
}
/**
* Calculate the entropy of the file. The result is used to qualify the file
- * as an encrypted file.
+ * as possibly encrypted.
*
* @param file The file to be calculated against.
*
* @return The entropy of the file.
*
- * @throws IOException If there is a failure closing or reading from the
- * InputStream.
+ * @throws ReadContentInputStreamException If there is a failure reading
+ * from the InputStream.
+ * @throws IOException If there is a failure closing or
+ * reading from the InputStream.
*/
private double calculateEntropy(AbstractFile file) throws ReadContentInputStreamException, IOException {
/*
diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java
old mode 100644
new mode 100755
index 27549f648f..7798d5be51
--- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionModuleFactory.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2017 Basis Technology Corp.
+ * Copyright 2017-2018 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,7 +30,8 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/**
- * A factory that creates file ingest modules that detect encryption.
+ * A factory that creates file ingest modules that detect encryption and
+ * password protection.
*/
@ServiceProvider(service = IngestModuleFactory.class)
@Messages({