19 package org.sleuthkit.autopsy.modules.encryptiondetection;
21 import java.io.IOException;
22 import java.util.Collections;
23 import java.util.logging.Level;
25 import java.io.BufferedInputStream;
26 import java.io.InputStream;
27 import org.apache.tika.exception.EncryptedDocumentException;
28 import org.apache.tika.exception.TikaException;
29 import org.apache.tika.metadata.Metadata;
30 import org.apache.tika.parser.AutoDetectParser;
31 import org.apache.tika.parser.ParseContext;
32 import org.apache.tika.sax.BodyContentHandler;
46 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
49 import org.xml.sax.ContentHandler;
50 import org.xml.sax.SAXException;
57 final class EncryptionDetectionFileIngestModule
extends FileIngestModuleAdapter {
59 private static final int FILE_SIZE_MODULUS = 512;
60 private final IngestServices services = IngestServices.getInstance();
61 private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName());
62 private FileTypeDetector fileTypeDetector;
63 private Blackboard blackboard;
64 private double calculatedEntropy;
66 private final double minimumEntropy;
67 private final int minimumFileSize;
68 private final boolean fileSizeMultipleEnforced;
69 private final boolean slackFilesAllowed;
78 EncryptionDetectionFileIngestModule(EncryptionDetectionIngestJobSettings settings) {
79 minimumEntropy = settings.getMinimumEntropy();
80 minimumFileSize = settings.getMinimumFileSize();
81 fileSizeMultipleEnforced = settings.isFileSizeMultipleEnforced();
82 slackFilesAllowed = settings.isSlackFilesAllowed();
86 public void startUp(IngestJobContext context)
throws IngestModule.IngestModuleException {
89 blackboard = Case.getOpenCase().getServices().getBlackboard();
90 fileTypeDetector =
new FileTypeDetector();
91 }
catch (FileTypeDetector.FileTypeDetectorInitException ex) {
92 throw new IngestModule.IngestModuleException(
"Failed to create file type detector", ex);
93 }
catch (NoCurrentCaseException ex) {
94 throw new IngestModule.IngestModuleException(
"Exception while getting open case.", ex);
99 public IngestModule.ProcessResult process(AbstractFile file) {
105 if (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
106 && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)
107 && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR)
108 && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR)
109 && (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || slackFilesAllowed)) {
113 if (!file.getKnown().equals(TskData.FileKnown.KNOWN)) {
117 String mimeType = fileTypeDetector.getMIMEType(file);
118 if (mimeType.equals(
"application/octet-stream")) {
119 if (isFileEncryptionSuspected(file)) {
120 return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED);
123 if (isFilePasswordProtected(file)) {
124 return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
129 }
catch (ReadContentInputStreamException | SAXException | TikaException ex) {
130 logger.log(Level.WARNING, String.format(
"Unable to read file '%s'", file.getParentPath() + file.getName()), ex);
131 return IngestModule.ProcessResult.ERROR;
132 }
catch (IOException ex) {
133 logger.log(Level.SEVERE, String.format(
"Unable to process file '%s'", file.getParentPath() + file.getName()), ex);
134 return IngestModule.ProcessResult.ERROR;
137 return IngestModule.ProcessResult.OK;
146 private void validateSettings() throws IngestModule.IngestModuleException {
147 EncryptionDetectionTools.validateMinEntropyValue(minimumEntropy);
148 EncryptionDetectionTools.validateMinFileSizeValue(minimumFileSize);
160 private IngestModule.ProcessResult flagFile(AbstractFile file, BlackboardArtifact.ARTIFACT_TYPE artifactType) {
162 BlackboardArtifact artifact = file.newArtifact(artifactType);
168 blackboard.indexArtifact(artifact);
169 }
catch (Blackboard.BlackboardException ex) {
170 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + artifact.getArtifactID(), ex);
176 services.fireModuleDataEvent(
new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), artifactType, Collections.singletonList(artifact)));
181 StringBuilder detailsSb =
new StringBuilder();
182 detailsSb.append(
"File: ").append(file.getParentPath()).append(file.getName());
183 if (artifactType.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED)) {
184 detailsSb.append(
"<br/>\n").append(
"Entropy: ").append(calculatedEntropy);
187 services.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(),
188 artifactType.getDisplayName() +
" Match: " + file.getName(),
189 detailsSb.toString(),
193 return IngestModule.ProcessResult.OK;
194 }
catch (TskCoreException ex) {
195 logger.log(Level.SEVERE, String.format(
"Failed to create blackboard artifact for '%s'.", file.getParentPath() + file.getName()), ex);
196 return IngestModule.ProcessResult.ERROR;
216 private boolean isFilePasswordProtected(AbstractFile file)
throws ReadContentInputStreamException, IOException, SAXException, TikaException {
218 boolean passwordProtected =
false;
220 switch (file.getMIMEType()) {
221 case "application/x-ooxml-protected":
226 passwordProtected =
true;
229 case "application/msword":
230 case "application/vnd.ms-excel":
231 case "application/vnd.ms-powerpoint":
232 case "application/pdf":
237 InputStream in = null;
238 BufferedInputStream bin = null;
241 in =
new ReadContentInputStream(file);
242 bin =
new BufferedInputStream(in);
243 ContentHandler handler =
new BodyContentHandler(-1);
244 Metadata metadata =
new Metadata();
245 metadata.add(Metadata.RESOURCE_NAME_KEY, file.getName());
246 AutoDetectParser parser =
new AutoDetectParser();
247 parser.parse(bin, handler, metadata,
new ParseContext());
248 }
catch (EncryptedDocumentException ex) {
252 passwordProtected =
true;
263 return passwordProtected;
281 private boolean isFileEncryptionSuspected(AbstractFile file)
throws ReadContentInputStreamException, IOException {
287 boolean possiblyEncrypted =
false;
292 long contentSize = file.getSize();
293 if (contentSize >= minimumFileSize) {
294 if (!fileSizeMultipleEnforced || (contentSize % FILE_SIZE_MODULUS) == 0) {
298 calculatedEntropy = EncryptionDetectionTools.calculateEntropy(file);
299 if (calculatedEntropy >= minimumEntropy) {
300 possiblyEncrypted =
true;
304 return possiblyEncrypted;