Autopsy  4.6.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
EncryptionDetectionFileIngestModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2017-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.modules.encryptiondetection;
20 
21 import java.io.IOException;
22 import java.util.Collections;
23 import java.util.logging.Level;
24 import org.sleuthkit.datamodel.ReadContentInputStream;
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;
44 import org.sleuthkit.datamodel.AbstractFile;
45 import org.sleuthkit.datamodel.BlackboardArtifact;
46 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
47 import org.sleuthkit.datamodel.TskCoreException;
48 import org.sleuthkit.datamodel.TskData;
49 import org.xml.sax.ContentHandler;
50 import org.xml.sax.SAXException;
51 
52 
53 
57 final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter {
58 
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;
65 
66  private final double minimumEntropy;
67  private final int minimumFileSize;
68  private final boolean fileSizeMultipleEnforced;
69  private final boolean slackFilesAllowed;
70 
78  EncryptionDetectionFileIngestModule(EncryptionDetectionIngestJobSettings settings) {
79  minimumEntropy = settings.getMinimumEntropy();
80  minimumFileSize = settings.getMinimumFileSize();
81  fileSizeMultipleEnforced = settings.isFileSizeMultipleEnforced();
82  slackFilesAllowed = settings.isSlackFilesAllowed();
83  }
84 
85  @Override
86  public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
87  try {
88  validateSettings();
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);
95  }
96  }
97 
98  @Override
99  public IngestModule.ProcessResult process(AbstractFile file) {
100 
101  try {
102  /*
103  * Qualify the file type.
104  */
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)) {
110  /*
111  * Qualify the file against hash databases.
112  */
113  if (!file.getKnown().equals(TskData.FileKnown.KNOWN)) {
114  /*
115  * Qualify the MIME type.
116  */
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);
121  }
122  } else {
123  if (isFilePasswordProtected(file)) {
124  return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
125  }
126  }
127  }
128  }
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;
135  }
136 
137  return IngestModule.ProcessResult.OK;
138  }
139 
146  private void validateSettings() throws IngestModule.IngestModuleException {
147  EncryptionDetectionTools.validateMinEntropyValue(minimumEntropy);
148  EncryptionDetectionTools.validateMinFileSizeValue(minimumFileSize);
149  }
150 
160  private IngestModule.ProcessResult flagFile(AbstractFile file, BlackboardArtifact.ARTIFACT_TYPE artifactType) {
161  try {
162  BlackboardArtifact artifact = file.newArtifact(artifactType);
163 
164  try {
165  /*
166  * Index the artifact for keyword search.
167  */
168  blackboard.indexArtifact(artifact);
169  } catch (Blackboard.BlackboardException ex) {
170  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
171  }
172 
173  /*
174  * Send an event to update the view with the new result.
175  */
176  services.fireModuleDataEvent(new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), artifactType, Collections.singletonList(artifact)));
177 
178  /*
179  * Make an ingest inbox message.
180  */
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);
185  }
186 
187  services.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(),
188  artifactType.getDisplayName() + " Match: " + file.getName(),
189  detailsSb.toString(),
190  file.getName(),
191  artifact));
192 
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); //NON-NLS
196  return IngestModule.ProcessResult.ERROR;
197  }
198  }
199 
216  private boolean isFilePasswordProtected(AbstractFile file) throws ReadContentInputStreamException, IOException, SAXException, TikaException {
217 
218  boolean passwordProtected = false;
219 
220  switch (file.getMIMEType()) {
221  case "application/x-ooxml-protected":
222  /*
223  * Office Open XML files that are password protected can be
224  * determined so simply by checking the MIME type.
225  */
226  passwordProtected = true;
227  break;
228 
229  case "application/msword":
230  case "application/vnd.ms-excel":
231  case "application/vnd.ms-powerpoint":
232  case "application/pdf":
233  /*
234  * A file of one of these types will be determined to be
235  * password protected or not by attempting to parse it via Tika.
236  */
237  InputStream in = null;
238  BufferedInputStream bin = null;
239 
240  try {
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) {
249  /*
250  * File is determined to be password protected.
251  */
252  passwordProtected = true;
253  } finally {
254  if (in != null) {
255  in.close();
256  }
257  if (bin != null) {
258  bin.close();
259  }
260  }
261  }
262 
263  return passwordProtected;
264  }
265 
281  private boolean isFileEncryptionSuspected(AbstractFile file) throws ReadContentInputStreamException, IOException {
282  /*
283  * Criteria for the checks in this method are partially based on
284  * http://www.forensicswiki.org/wiki/TrueCrypt#Detection
285  */
286 
287  boolean possiblyEncrypted = false;
288 
289  /*
290  * Qualify the size.
291  */
292  long contentSize = file.getSize();
293  if (contentSize >= minimumFileSize) {
294  if (!fileSizeMultipleEnforced || (contentSize % FILE_SIZE_MODULUS) == 0) {
295  /*
296  * Qualify the entropy.
297  */
298  calculatedEntropy = EncryptionDetectionTools.calculateEntropy(file);
299  if (calculatedEntropy >= minimumEntropy) {
300  possiblyEncrypted = true;
301  }
302  }
303  }
304  return possiblyEncrypted;
305  }
306 }

Copyright © 2012-2016 Basis Technology. Generated on: Mon May 7 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.