Autopsy  4.19.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
EvalFileObj.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-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.report.modules.stix;
20 
22 import org.sleuthkit.datamodel.SleuthkitCase;
23 import org.sleuthkit.datamodel.AbstractFile;
24 import org.sleuthkit.datamodel.BlackboardArtifact;
25 import org.sleuthkit.datamodel.BlackboardAttribute;
26 import org.sleuthkit.datamodel.TskCoreException;
27 
28 import java.util.List;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.TimeZone;
32 import java.text.ParseException;
33 import java.text.SimpleDateFormat;
34 import org.mitre.cybox.common_2.ConditionApplicationEnum;
35 
36 import org.mitre.cybox.objects.FileObjectType;
37 import org.mitre.cybox.objects.WindowsExecutableFileObjectType;
38 import org.mitre.cybox.common_2.ConditionTypeEnum;
39 import org.mitre.cybox.common_2.DatatypeEnum;
40 import org.mitre.cybox.common_2.HashType;
41 import org.mitre.cybox.common_2.DateTimeObjectPropertyType;
42 import org.mitre.cybox.common_2.StringObjectPropertyType;
43 import org.mitre.cybox.common_2.UnsignedLongObjectPropertyType;
45 
49 class EvalFileObj extends EvaluatableObject {
50 
51  private final FileObjectType obj;
52 
53  public EvalFileObj(FileObjectType a_obj, String a_id, String a_spacing) {
54  obj = a_obj;
55  id = a_id;
56  spacing = a_spacing;
57  }
58 
59  @Override
60  @SuppressWarnings("deprecation")
61  public synchronized ObservableResult evaluate() {
62 
63  Case case1;
64  try {
65  case1 = Case.getCurrentCaseThrows();
66  } catch (NoCurrentCaseException ex) {
67  return new ObservableResult(id, "Exception while getting open case.", //NON-NLS
68  spacing, ObservableResult.ObservableState.FALSE, null);
69  }
70  SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
71 
72  setWarnings("");
73  String whereClause = "";
74 
75  if (obj.getSizeInBytes() != null) {
76  try {
77  String newClause = processULongObject(obj.getSizeInBytes(), "size"); //NON-NLS
78  whereClause = addClause(whereClause, newClause);
79  } catch (TskCoreException ex) {
80  addWarning(ex.getLocalizedMessage());
81  }
82  }
83 
84  if (obj.getFileName() != null) {
85  try {
86  String newClause = processStringObject(obj.getFileName(), "name"); //NON-NLS
87  whereClause = addClause(whereClause, newClause);
88  } catch (TskCoreException ex) {
89  addWarning(ex.getLocalizedMessage());
90  }
91  }
92 
93  if (obj.getFileExtension() != null) {
94  if ((obj.getFileExtension().getCondition() == null)
95  || (obj.getFileExtension().getCondition() == ConditionTypeEnum.EQUALS)) {
96  String newClause = "LOWER(name) LIKE LOWER(\'%" + obj.getFileExtension().getValue() + "\')"; //NON-NLS
97  whereClause = addClause(whereClause, newClause);
98  } else {
99  addWarning(
100  "Could not process condition " + obj.getFileExtension().getCondition().value() + " on file extension"); //NON-NLS
101  }
102  }
103 
104  if (obj.getFilePath() != null) {
105  try {
106 
107  String[] parts = obj.getFilePath().getValue().toString().split("##comma##"); //NON-NLS
108  String finalPathStr = "";
109 
110  for (String filePath : parts) {
111  // First, we need to normalize the path
112  String currentFilePath = filePath;
113 
114  // Remove the drive letter
115  if (currentFilePath.matches("^[A-Za-z]:.*")) {
116  currentFilePath = currentFilePath.substring(2);
117  }
118 
119  // Change any backslashes to forward slashes
120  currentFilePath = currentFilePath.replace("\\", "/");
121 
122  // The path needs to start with a slash
123  if (!currentFilePath.startsWith("/")) {
124  currentFilePath = "/" + currentFilePath;
125  }
126 
127  // If the path does not end in a slash, the final part should be the file name.
128  if (!currentFilePath.endsWith("/")) {
129  int lastSlash = currentFilePath.lastIndexOf('/');
130  if (lastSlash >= 0) {
131  currentFilePath = currentFilePath.substring(0, lastSlash + 1);
132  }
133  }
134 
135  // Reconstruct the path string (which may be multi-part)
136  if (!finalPathStr.isEmpty()) {
137  finalPathStr += "##comma##"; //NON-NLS
138  }
139  finalPathStr += currentFilePath;
140  }
141 
142  String newClause = processStringObject(finalPathStr, obj.getFilePath().getCondition(),
143  obj.getFilePath().getApplyCondition(), "parent_path"); //NON-NLS
144 
145  whereClause = addClause(whereClause, newClause);
146  } catch (TskCoreException ex) {
147  addWarning(ex.getLocalizedMessage());
148  }
149  }
150 
151  if (obj.getCreatedTime() != null) {
152  try {
153  String newClause = processTimestampObject(obj.getCreatedTime(), "crtime"); //NON-NLS
154  whereClause = addClause(whereClause, newClause);
155  } catch (TskCoreException ex) {
156  addWarning(ex.getLocalizedMessage());
157  }
158  }
159 
160  if (obj.getModifiedTime() != null) {
161  try {
162  String newClause = processTimestampObject(obj.getModifiedTime(), "mtime"); //NON-NLS
163  whereClause = addClause(whereClause, newClause);
164  } catch (TskCoreException ex) {
165  addWarning(ex.getLocalizedMessage());
166  }
167  }
168 
169  if (obj.getAccessedTime() != null) {
170  try {
171  String newClause = processTimestampObject(obj.getAccessedTime(), "atime"); //NON-NLS
172  whereClause = addClause(whereClause, newClause);
173  } catch (TskCoreException ex) {
174  addWarning(ex.getLocalizedMessage());
175  }
176  }
177 
178  if (obj.getHashes() != null) {
179  for (HashType h : obj.getHashes().getHashes()) {
180  if (h.getSimpleHashValue() != null) {
181  if (h.getType().getValue().equals("MD5")) { //NON-NLS
182  String newClause = "";
183  if (h.getSimpleHashValue().getValue().toString().toLowerCase().contains("##comma##")) { //NON-NLS
184  String[] parts = h.getSimpleHashValue().getValue().toString().toLowerCase().split("##comma##"); //NON-NLS
185  String hashList = "";
186  for (String s : parts) {
187  if (!hashList.isEmpty()) {
188  hashList += ", ";
189  }
190  hashList += "\'" + s + "\'";
191  }
192  newClause = "md5 IN (" + hashList + ")"; //NON-NLS
193  } else {
194  newClause = "md5=\'" + h.getSimpleHashValue().getValue().toString().toLowerCase() + "\'"; //NON-NLS
195  }
196  whereClause = addClause(whereClause, newClause);
197  } else {
198  addWarning("Could not process hash type " + h.getType().getValue().toString()); //NON-NLS
199  }
200  } else {
201  addWarning("Could not process non-simple hash value"); //NON-NLS
202  }
203  }
204  }
205 
206  if (obj instanceof WindowsExecutableFileObjectType) {
207  WindowsExecutableFileObjectType winExe = (WindowsExecutableFileObjectType) obj;
208  if (winExe.getHeaders() != null) {
209  if (winExe.getHeaders().getFileHeader() != null) {
210  if (winExe.getHeaders().getFileHeader().getTimeDateStamp() != null) {
211  try {
212  String result = convertTimestampString(winExe.getHeaders().getFileHeader().getTimeDateStamp().getValue().toString());
213  String newClause = processNumericFields(result,
214  winExe.getHeaders().getFileHeader().getTimeDateStamp().getCondition(),
215  winExe.getHeaders().getFileHeader().getTimeDateStamp().getApplyCondition(),
216  "crtime"); //NON-NLS
217  whereClause = addClause(whereClause, newClause);
218  } catch (TskCoreException ex) {
219  addWarning(ex.getLocalizedMessage());
220  }
221  }
222  }
223  }
224  }
225 
226  String unsupportedFields = listUnsupportedFields();
227  if (!unsupportedFields.isEmpty()) {
228  addWarning("Unsupported fields: " + unsupportedFields); //NON-NLS
229  }
230 
231  if (whereClause.length() > 0) {
232  try {
233  List<AbstractFile> matchingFiles = sleuthkitCase.findAllFilesWhere(whereClause);
234 
235  if (!matchingFiles.isEmpty()) {
236 
237  if (listSecondaryFields().isEmpty()) {
238 
239  List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
240  for (AbstractFile a : matchingFiles) {
241  artData.add(new StixArtifactData(a, id, "FileObject")); //NON-NLS
242  }
243 
244  return new ObservableResult(id, "FileObject: Found " + matchingFiles.size() + " matches for " + whereClause + getPrintableWarnings(), //NON-NLS
245  spacing, ObservableResult.ObservableState.TRUE, artData);
246  } else {
247 
248  // We need to tag the matching files in Autopsy, so keep track of them
249  List<AbstractFile> secondaryHits = new ArrayList<AbstractFile>();
250 
251  for (AbstractFile file : matchingFiles) {
252  boolean passedTests = true;
253 
254  if (obj.isIsMasqueraded() != null) {
255  List<BlackboardArtifact> arts = file.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED);
256  boolean isMasq = false;
257  if (!arts.isEmpty()) {
258  isMasq = true;
259  }
260 
261  if (obj.isIsMasqueraded() != isMasq) {
262  passedTests = false;
263  }
264 
265  }
266 
267  if (obj.getFileFormat() != null) {
268 
269  String formatsFound = file.getMIMEType();
270  if (formatsFound != null) {
271  if (!(formatsFound.equalsIgnoreCase(obj.getFileFormat().getValue().toString()))) {
272  addWarning("Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString() //NON-NLS
273  + " against " + formatsFound); //NON-NLS
274  }
275  } else {
276  addWarning("Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString() //NON-NLS
277  + " (no file formats found)"); //NON-NLS
278  }
279  // It looks like the STIX file formats can be different than what Autopsy stores
280  // (mime vs. unix file), so don't kill a file based on this field not matching.
281  //if (!foundMatch) {
282  // passedTests = false;
283  //}
284  }
285  if (passedTests) {
286  secondaryHits.add(file);
287  }
288  }
289 
290  if (secondaryHits.isEmpty()) {
291 
292  return new ObservableResult(id, "FileObject: Found " + matchingFiles.size() + " matches for " + whereClause //NON-NLS
293  + " but none for secondary tests on " + listSecondaryFields() + getPrintableWarnings(), //NON-NLS
294  spacing, ObservableResult.ObservableState.FALSE, null);
295  } else {
296  List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
297  for (AbstractFile a : secondaryHits) {
298  artData.add(new StixArtifactData(a, id, "FileObject")); //NON-NLS
299  }
300  return new ObservableResult(id, "FileObject: Found " + secondaryHits.size() + " matches for " + whereClause //NON-NLS
301  + " and secondary tests on " + listSecondaryFields() + getPrintableWarnings(), //NON-NLS
302  spacing, ObservableResult.ObservableState.TRUE, artData);
303  }
304  }
305  } else {
306  return new ObservableResult(id, "FileObject: Found no matches for " + whereClause + getPrintableWarnings(), //NON-NLS
307  spacing, ObservableResult.ObservableState.FALSE, null);
308  }
309  } catch (TskCoreException ex) {
310  return new ObservableResult(id, "FileObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS
311  spacing, ObservableResult.ObservableState.INDETERMINATE, null);
312  }
313  } else {
314 
315  }
316 
317  return new ObservableResult(id, "FileObject: No evaluatable fields " + getPrintableWarnings(), spacing, //NON-NLS
318  ObservableResult.ObservableState.INDETERMINATE, null);
319  }
320 
327  private String listSecondaryFields() {
328  String secondaryFields = "";
329 
330  if (obj.isIsMasqueraded() != null) {
331  secondaryFields += "is_masqueraded "; //NON-NLS
332  }
333 
334  if (obj.getFileFormat() != null) {
335  secondaryFields += "File_Format "; //NON-NLS
336  }
337 
338  return secondaryFields;
339  }
340 
346  private String listUnsupportedFields() {
347  String unsupportedFields = "";
348 
349  if (obj.isIsPacked() != null) {
350  unsupportedFields += "is_packed "; //NON-NLS
351  }
352  if (obj.getDevicePath() != null) {
353  unsupportedFields += "Device_Path "; //NON-NLS
354  }
355  if (obj.getFullPath() != null) {
356  unsupportedFields += "Full_Path "; //NON-NLS
357  }
358  if (obj.getMagicNumber() != null) {
359  unsupportedFields += "Magic_Number "; //NON-NLS
360  }
361  if (obj.getDigitalSignatures() != null) {
362  unsupportedFields += "Digital_Signatures "; //NON-NLS
363  }
364  if (obj.getFileAttributesList() != null) {
365  unsupportedFields += "File_Attributes_List "; //NON-NLS
366  }
367  if (obj.getPermissions() != null) {
368  unsupportedFields += "Permissions "; //NON-NLS
369  }
370  if (obj.getUserOwner() != null) {
371  unsupportedFields += "User_Owner "; //NON-NLS
372  }
373  if (obj.getPackerList() != null) {
374  unsupportedFields += "Packer_List "; //NON-NLS
375  }
376  if (obj.getPeakEntropy() != null) {
377  unsupportedFields += "Peak_Entropy "; //NON-NLS
378  }
379  if (obj.getSymLinks() != null) {
380  unsupportedFields += "Sym_Links "; //NON-NLS
381  }
382  if (obj.getByteRuns() != null) {
383  unsupportedFields += "Bytes_Runs "; //NON-NLS
384  }
385  if (obj.getExtractedFeatures() != null) {
386  unsupportedFields += "Extracted_Features "; //NON-NLS
387  }
388  if (obj.getEncryptionAlgorithm() != null) {
389  unsupportedFields += "Encryption_Algorithm "; //NON-NLS
390  }
391  if (obj.getDecryptionKey() != null) {
392  unsupportedFields += "Decryption_Key "; //NON-NLS
393  }
394  if (obj.getCompressionMethod() != null) {
395  unsupportedFields += "Compression_Method "; //NON-NLS
396  }
397  if (obj.getCompressionVersion() != null) {
398  unsupportedFields += "Compression_Version "; //NON-NLS
399  }
400  if (obj.getCompressionComment() != null) {
401  unsupportedFields += "Compression_Comment "; //NON-NLS
402  }
403 
404  return unsupportedFields;
405  }
406 
416  private static long convertTimestamp(String timeStr) throws ParseException {
417  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); //NON-NLS
418  dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); //NON-NLS
419  Date parsedDate = dateFormat.parse(timeStr);
420 
421  Long unixTime = parsedDate.getTime() / 1000;
422 
423  return unixTime;
424  }
425 
437  private static String processULongObject(UnsignedLongObjectPropertyType longObj, String fieldName)
438  throws TskCoreException {
439 
440  return processNumericFields(longObj.getValue().toString(), longObj.getCondition(),
441  longObj.getApplyCondition(), fieldName);
442  }
443 
456  private static String processNumericFields(String valueStr, ConditionTypeEnum typeCondition,
457  ConditionApplicationEnum applyCondition, String fieldName)
458  throws TskCoreException {
459 
460  if ((typeCondition == null)
461  || ((typeCondition != ConditionTypeEnum.INCLUSIVE_BETWEEN)
462  && (typeCondition != ConditionTypeEnum.EXCLUSIVE_BETWEEN))) {
463 
464  String fullClause = "";
465 
466  if (valueStr.isEmpty()) {
467  throw new TskCoreException("Empty value field"); //NON-NLS
468  }
469 
470  String[] parts = valueStr.split("##comma##"); //NON-NLS
471 
472  for (String valuePart : parts) {
473  String partialClause;
474 
475  if ((typeCondition == null)
476  || (typeCondition == ConditionTypeEnum.EQUALS)) {
477 
478  partialClause = fieldName + "=" + valuePart;
479  } else if (typeCondition == ConditionTypeEnum.DOES_NOT_EQUAL) {
480  partialClause = fieldName + "!=" + valuePart;
481  } else if (typeCondition == ConditionTypeEnum.GREATER_THAN) {
482  partialClause = fieldName + ">" + valuePart;
483  } else if (typeCondition == ConditionTypeEnum.GREATER_THAN_OR_EQUAL) {
484  partialClause = fieldName + ">=" + valuePart;
485  } else if (typeCondition == ConditionTypeEnum.LESS_THAN) {
486  partialClause = fieldName + "<" + valuePart;
487  } else if (typeCondition == ConditionTypeEnum.LESS_THAN_OR_EQUAL) {
488  partialClause = fieldName + "<=" + valuePart;
489  } else {
490  throw new TskCoreException("Could not process condition " + typeCondition.value() + " on " + fieldName); //NON-NLS
491  }
492 
493  if (fullClause.isEmpty()) {
494 
495  if (parts.length > 1) {
496  fullClause += "( ";
497  }
498  if (applyCondition == ConditionApplicationEnum.NONE) {
499  fullClause += " NOT "; //NON-NLS
500  }
501  fullClause += partialClause;
502  } else {
503  if (applyCondition == ConditionApplicationEnum.ALL) {
504  fullClause += " AND " + partialClause; //NON-NLS
505  } else if (applyCondition == ConditionApplicationEnum.NONE) {
506  fullClause += " AND NOT " + partialClause; //NON-NLS
507  } else {
508  fullClause += " OR " + partialClause; //NON-NLS
509  }
510  }
511  }
512 
513  if (parts.length > 1) {
514  fullClause += " )";
515  }
516 
517  return fullClause;
518  } else {
519  // I don't think apply conditions make sense for these two.
520  if (typeCondition == ConditionTypeEnum.INCLUSIVE_BETWEEN) {
521  String[] parts = valueStr.split("##comma##"); //NON-NLS
522  if (parts.length != 2) {
523  throw new TskCoreException("Unexpected number of arguments in INCLUSIVE_BETWEEN on " + fieldName //NON-NLS
524  + "(" + valueStr + ")");
525  }
526  return (fieldName + ">=" + parts[0] + " AND " + fieldName + "<=" + parts[1]); //NON-NLS
527  } else {
528  String[] parts = valueStr.split("##comma##"); //NON-NLS
529  if (parts.length != 2) {
530  throw new TskCoreException("Unexpected number of arguments in EXCLUSIVE_BETWEEN on " + fieldName //NON-NLS
531  + "(" + valueStr + ")");
532  }
533  return (fieldName + ">" + parts[0] + " AND " + fieldName + "<" + parts[1]); //NON-NLS
534  }
535  }
536  }
537 
548  private static String processStringObject(StringObjectPropertyType stringObj, String fieldName)
549  throws TskCoreException {
550 
551  return processStringObject(stringObj.getValue().toString(), stringObj.getCondition(),
552  stringObj.getApplyCondition(), fieldName);
553  }
554 
567  public static String processStringObject(String valueStr, ConditionTypeEnum condition,
568  ConditionApplicationEnum applyCondition, String fieldName)
569  throws TskCoreException {
570 
571  String fullClause = "";
572  String lowerFieldName = "lower(" + fieldName + ")"; //NON-NLS
573 
574  if (valueStr.isEmpty()) {
575  throw new TskCoreException("Empty value field"); //NON-NLS
576  }
577 
578  String[] parts = valueStr.split("##comma##"); //NON-NLS
579 
580  for (String value : parts) {
581  String lowerValue = value.toLowerCase();
582  String partialClause;
583  if ((condition == null)
584  || (condition == ConditionTypeEnum.EQUALS)) {
585  partialClause = lowerFieldName + "=\'" + lowerValue + "\'";
586  } else if (condition == ConditionTypeEnum.DOES_NOT_EQUAL) {
587  partialClause = lowerFieldName + " !=\'%" + lowerValue + "%\'";
588  } else if (condition == ConditionTypeEnum.CONTAINS) {
589  partialClause = lowerFieldName + " LIKE \'%" + lowerValue + "%\'"; //NON-NLS
590  } else if (condition == ConditionTypeEnum.DOES_NOT_CONTAIN) {
591  partialClause = lowerFieldName + " NOT LIKE \'%" + lowerValue + "%\'"; //NON-NLS
592  } else if (condition == ConditionTypeEnum.STARTS_WITH) {
593  partialClause = lowerFieldName + " LIKE \'" + lowerValue + "%\'"; //NON-NLS
594  } else if (condition == ConditionTypeEnum.ENDS_WITH) {
595  partialClause = lowerFieldName + " LIKE \'%" + lowerValue + "\'"; //NON-NLS
596  } else {
597  throw new TskCoreException("Could not process condition " + condition.value() + " on " + fieldName); //NON-NLS
598  }
599 
600  if (fullClause.isEmpty()) {
601 
602  if (parts.length > 1) {
603  fullClause += "( ";
604  }
605  if (applyCondition == ConditionApplicationEnum.NONE) {
606  fullClause += " NOT "; //NON-NLS
607  }
608  fullClause += partialClause;
609  } else {
610  if (applyCondition == ConditionApplicationEnum.ALL) {
611  fullClause += " AND " + partialClause; //NON-NLS
612  } else if (applyCondition == ConditionApplicationEnum.NONE) {
613  fullClause += " AND NOT " + partialClause; //NON-NLS
614  } else {
615  fullClause += " OR " + partialClause; //NON-NLS
616  }
617  }
618  }
619 
620  if (parts.length > 1) {
621  fullClause += " )";
622  }
623 
624  return fullClause;
625  }
626 
638  private static String processTimestampObject(DateTimeObjectPropertyType dateObj, String fieldName)
639  throws TskCoreException {
640 
641  if (DatatypeEnum.DATE_TIME == dateObj.getDatatype()) {
642 
643  // Change the string into unix timestamps
644  String result = convertTimestampString(dateObj.getValue().toString());
645  return processNumericFields(result, dateObj.getCondition(), dateObj.getApplyCondition(), fieldName);
646 
647  } else {
648  throw new TskCoreException("Found non DATE_TIME field on " + fieldName); //NON-NLS
649  }
650  }
651 
662  private static String convertTimestampString(String timestampStr)
663  throws TskCoreException {
664  try {
665  String result = "";
666  if (timestampStr.length() > 0) {
667  String[] parts = timestampStr.split("##comma##"); //NON-NLS
668 
669  for (int i = 0; i < parts.length - 1; i++) {
670  long unixTime = convertTimestamp(parts[i]);
671  result += unixTime + "##comma##"; //NON-NLS
672  }
673  result += convertTimestamp(parts[parts.length - 1]);
674  }
675  return result;
676  } catch (java.text.ParseException ex) {
677  throw new TskCoreException("Error parsing timestamp string " + timestampStr); //NON-NLS
678  }
679 
680  }
681 
690  private static String addClause(String a_clause, String a_newClause) {
691 
692  if ((a_clause == null) || a_clause.isEmpty()) {
693  return a_newClause;
694  }
695 
696  return (a_clause + " AND " + a_newClause); //NON-NLS
697  }
698 
699 }

Copyright © 2012-2021 Basis Technology. Generated on: Fri Aug 6 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.