Autopsy  4.5.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.BufferedInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.Collections;
25 import java.util.logging.Level;
36 import org.sleuthkit.datamodel.AbstractFile;
37 import org.sleuthkit.datamodel.BlackboardArtifact;
38 import org.sleuthkit.datamodel.ReadContentInputStream;
39 import org.sleuthkit.datamodel.TskCoreException;
40 import org.sleuthkit.datamodel.TskData;
41 
45 final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter {
46 
47  static final double DEFAULT_CONFIG_MINIMUM_ENTROPY = 7.5;
48  static final int DEFAULT_CONFIG_MINIMUM_FILE_SIZE = 5242880; // 5MB;
49  static final boolean DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED = true;
50  static final boolean DEFAULT_CONFIG_SLACK_FILES_ALLOWED = true;
51 
52  private static final int FILE_SIZE_MODULUS = 512;
53  private static final double ONE_OVER_LOG2 = 1.4426950408889634073599246810019; // (1 / log(2))
54  private static final int BYTE_OCCURENCES_BUFFER_SIZE = 256;
55 
56  private final IngestServices SERVICES = IngestServices.getInstance();
57  private final Logger LOGGER = SERVICES.getLogger(EncryptionDetectionModuleFactory.getModuleName());
58  private FileTypeDetector fileTypeDetector;
59  private Blackboard blackboard;
60  private double calculatedEntropy;
61 
62  private final double minimumEntropy;
63  private final int minimumFileSize;
64  private final boolean fileSizeMultipleEnforced;
65  private final boolean slackFilesAllowed;
66 
73  EncryptionDetectionFileIngestModule(EncryptionDetectionIngestJobSettings settings) {
74  minimumEntropy = settings.getMinimumEntropy();
75  minimumFileSize = settings.getMinimumFileSize();
76  fileSizeMultipleEnforced = settings.isFileSizeMultipleEnforced();
77  slackFilesAllowed = settings.isSlackFilesAllowed();
78  }
79 
80  @Override
81  public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
82  blackboard = Case.getCurrentCase().getServices().getBlackboard();
83  try {
84  fileTypeDetector = new FileTypeDetector();
86  throw new IngestModule.IngestModuleException("Failed to create file type detector", ex);
87  }
88  }
89 
90  @Override
91  public IngestModule.ProcessResult process(AbstractFile file) {
92 
93  try {
94  if (isFileEncrypted(file)) {
95  return flagFile(file);
96  }
97  } catch (IOException | TskCoreException ex) {
98  LOGGER.log(Level.SEVERE, String.format("Unable to process file '%s'", file.getParentPath() + file.getName()), ex);
100  }
101 
103  }
104 
113  private IngestModule.ProcessResult flagFile(AbstractFile file) {
114  try {
115  BlackboardArtifact artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED);
116 
117  try {
118  /*
119  * Index the artifact for keyword search.
120  */
121  blackboard.indexArtifact(artifact);
122  } catch (Blackboard.BlackboardException ex) {
123  LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
124  }
125 
126  /*
127  * Send an event to update the view with the new result.
128  */
129  SERVICES.fireModuleDataEvent(new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, Collections.singletonList(artifact)));
130 
131  /*
132  * Make an ingest inbox message.
133  */
134  StringBuilder detailsSb = new StringBuilder();
135  detailsSb.append("File: ").append(file.getParentPath()).append(file.getName()).append("<br/>\n");
136  detailsSb.append("Entropy: ").append(calculatedEntropy);
137 
139  "Encryption Detected Match: " + file.getName(),
140  detailsSb.toString(),
141  file.getName(),
142  artifact));
143 
145  } catch (TskCoreException ex) {
146  LOGGER.log(Level.SEVERE, String.format("Failed to create blackboard artifact for '%s'.", file.getParentPath() + file.getName()), ex); //NON-NLS
148  }
149  }
150 
161  private boolean isFileEncrypted(AbstractFile file) throws IOException, TskCoreException {
162  /*
163  * Criteria for the checks in this method are partially based on
164  * http://www.forensicswiki.org/wiki/TrueCrypt#Detection
165  */
166 
167  boolean possiblyEncrypted = false;
168 
169  /*
170  * Qualify the file type.
171  */
172  if (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
173  && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)
174  && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR)
175  && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR)
176  && (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || slackFilesAllowed)) {
177  /*
178  * Qualify the file against hash databases.
179  */
180  if (!file.getKnown().equals(TskData.FileKnown.KNOWN)) {
181  /*
182  * Qualify the size.
183  */
184  long contentSize = file.getSize();
185  if (contentSize >= minimumFileSize) {
186  if (!fileSizeMultipleEnforced || (contentSize % FILE_SIZE_MODULUS) == 0) {
187  /*
188  * Qualify the MIME type.
189  */
190  String mimeType = fileTypeDetector.getMIMEType(file);
191  if (mimeType.equals("application/octet-stream")) {
192  possiblyEncrypted = true;
193  }
194  }
195  }
196  }
197  }
198 
199  if (possiblyEncrypted) {
200  try {
201  calculatedEntropy = calculateEntropy(file);
202  if (calculatedEntropy >= minimumEntropy) {
203  return true;
204  }
205  } catch (IOException ex) {
206  throw new IOException("Unable to calculate the entropy.", ex);
207  }
208  }
209 
210  return false;
211  }
212 
224  private double calculateEntropy(AbstractFile file) throws IOException {
225  /*
226  * Logic in this method is based on
227  * https://github.com/willjasen/entropy/blob/master/entropy.java
228  */
229 
230  InputStream in = null;
231  BufferedInputStream bin = null;
232 
233  try {
234  in = new ReadContentInputStream(file);
235  bin = new BufferedInputStream(in);
236 
237  /*
238  * Determine the number of times each byte value appears.
239  */
240  int[] byteOccurences = new int[BYTE_OCCURENCES_BUFFER_SIZE];
241  int readByte;
242  while ((readByte = bin.read()) != -1) {
243  byteOccurences[readByte]++;
244  }
245 
246  /*
247  * Calculate the entropy based on the byte occurence counts.
248  */
249  long dataLength = file.getSize() - 1;
250  double entropyAccumulator = 0;
251  for (int i = 0; i < BYTE_OCCURENCES_BUFFER_SIZE; i++) {
252  if (byteOccurences[i] > 0) {
253  double byteProbability = (double) byteOccurences[i] / (double) dataLength;
254  entropyAccumulator += (byteProbability * Math.log(byteProbability) * ONE_OVER_LOG2);
255  }
256  }
257 
258  return -entropyAccumulator;
259 
260  } catch (IOException ex) {
261  throw new IOException("IOException occurred while trying to read data from InputStream.", ex);
262  } finally {
263  try {
264  if (in != null) {
265  in.close();
266  }
267  if (bin != null) {
268  bin.close();
269  }
270  } catch (IOException ex) {
271  throw new IOException("Failed to close InputStream.", ex);
272  }
273  }
274  }
275 }
static IngestMessage createDataMessage(String source, String subject, String detailsHtml, String uniqueKey, BlackboardArtifact data)
Logger getLogger(String moduleDisplayName)
void postMessage(final IngestMessage message)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
synchronized void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:59
static synchronized IngestServices getInstance()

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