19 package org.sleuthkit.autopsy.report.modules.stix;
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;
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;
49 class EvalFileObj
extends EvaluatableObject {
51 private final FileObjectType obj;
53 public EvalFileObj(FileObjectType a_obj, String a_id, String a_spacing) {
60 @SuppressWarnings(
"deprecation")
61 public synchronized ObservableResult evaluate() {
65 case1 = Case.getCurrentCaseThrows();
66 }
catch (NoCurrentCaseException ex) {
67 return new ObservableResult(
id,
"Exception while getting open case.",
68 spacing, ObservableResult.ObservableState.FALSE, null);
70 SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
73 String whereClause =
"";
75 if (obj.getSizeInBytes() != null) {
77 String newClause = processULongObject(obj.getSizeInBytes(),
"size");
78 whereClause = addClause(whereClause, newClause);
79 }
catch (TskCoreException ex) {
80 addWarning(ex.getLocalizedMessage());
84 if (obj.getFileName() != null) {
86 String newClause = processStringObject(obj.getFileName(),
"name");
87 whereClause = addClause(whereClause, newClause);
88 }
catch (TskCoreException ex) {
89 addWarning(ex.getLocalizedMessage());
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() +
"\')";
97 whereClause = addClause(whereClause, newClause);
100 "Could not process condition " + obj.getFileExtension().getCondition().value() +
" on file extension");
104 if (obj.getFilePath() != null) {
107 String[] parts = obj.getFilePath().getValue().toString().split(
"##comma##");
108 String finalPathStr =
"";
110 for (String filePath : parts) {
112 String currentFilePath = filePath;
115 if (currentFilePath.matches(
"^[A-Za-z]:.*")) {
116 currentFilePath = currentFilePath.substring(2);
120 currentFilePath = currentFilePath.replace(
"\\",
"/");
123 if (!currentFilePath.startsWith(
"/")) {
124 currentFilePath =
"/" + currentFilePath;
128 if (!currentFilePath.endsWith(
"/")) {
129 int lastSlash = currentFilePath.lastIndexOf(
'/');
130 if (lastSlash >= 0) {
131 currentFilePath = currentFilePath.substring(0, lastSlash + 1);
136 if (!finalPathStr.isEmpty()) {
137 finalPathStr +=
"##comma##";
139 finalPathStr += currentFilePath;
142 String newClause = processStringObject(finalPathStr, obj.getFilePath().getCondition(),
143 obj.getFilePath().getApplyCondition(),
"parent_path");
145 whereClause = addClause(whereClause, newClause);
146 }
catch (TskCoreException ex) {
147 addWarning(ex.getLocalizedMessage());
151 if (obj.getCreatedTime() != null) {
153 String newClause = processTimestampObject(obj.getCreatedTime(),
"crtime");
154 whereClause = addClause(whereClause, newClause);
155 }
catch (TskCoreException ex) {
156 addWarning(ex.getLocalizedMessage());
160 if (obj.getModifiedTime() != null) {
162 String newClause = processTimestampObject(obj.getModifiedTime(),
"mtime");
163 whereClause = addClause(whereClause, newClause);
164 }
catch (TskCoreException ex) {
165 addWarning(ex.getLocalizedMessage());
169 if (obj.getAccessedTime() != null) {
171 String newClause = processTimestampObject(obj.getAccessedTime(),
"atime");
172 whereClause = addClause(whereClause, newClause);
173 }
catch (TskCoreException ex) {
174 addWarning(ex.getLocalizedMessage());
178 if (obj.getHashes() != null) {
179 for (HashType h : obj.getHashes().getHashes()) {
180 if (h.getSimpleHashValue() != null) {
181 if (h.getType().getValue().equals(
"MD5")) {
182 String newClause =
"";
183 if (h.getSimpleHashValue().getValue().toString().toLowerCase().contains(
"##comma##")) {
184 String[] parts = h.getSimpleHashValue().getValue().toString().toLowerCase().split(
"##comma##");
185 String hashList =
"";
186 for (String s : parts) {
187 if (!hashList.isEmpty()) {
190 hashList +=
"\'" + s +
"\'";
192 newClause =
"md5 IN (" + hashList +
")";
194 newClause =
"md5=\'" + h.getSimpleHashValue().getValue().toString().toLowerCase() +
"\'";
196 whereClause = addClause(whereClause, newClause);
198 addWarning(
"Could not process hash type " + h.getType().getValue().toString());
201 addWarning(
"Could not process non-simple hash value");
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) {
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(),
217 whereClause = addClause(whereClause, newClause);
218 }
catch (TskCoreException ex) {
219 addWarning(ex.getLocalizedMessage());
226 String unsupportedFields = listUnsupportedFields();
227 if (!unsupportedFields.isEmpty()) {
228 addWarning(
"Unsupported fields: " + unsupportedFields);
231 if (whereClause.length() > 0) {
233 List<AbstractFile> matchingFiles = sleuthkitCase.findAllFilesWhere(whereClause);
235 if (!matchingFiles.isEmpty()) {
237 if (listSecondaryFields().isEmpty()) {
239 List<StixArtifactData> artData =
new ArrayList<StixArtifactData>();
240 for (AbstractFile a : matchingFiles) {
241 artData.add(
new StixArtifactData(a,
id,
"FileObject"));
244 return new ObservableResult(
id,
"FileObject: Found " + matchingFiles.size() +
" matches for " + whereClause + getPrintableWarnings(),
245 spacing, ObservableResult.ObservableState.TRUE, artData);
249 List<AbstractFile> secondaryHits =
new ArrayList<AbstractFile>();
251 for (AbstractFile file : matchingFiles) {
252 boolean passedTests =
true;
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()) {
261 if (obj.isIsMasqueraded() != isMasq) {
267 if (obj.getFileFormat() != null) {
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()
273 +
" against " + formatsFound);
276 addWarning(
"Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString()
277 +
" (no file formats found)");
286 secondaryHits.add(file);
290 if (secondaryHits.isEmpty()) {
292 return new ObservableResult(
id,
"FileObject: Found " + matchingFiles.size() +
" matches for " + whereClause
293 +
" but none for secondary tests on " + listSecondaryFields() + getPrintableWarnings(),
294 spacing, ObservableResult.ObservableState.FALSE, null);
296 List<StixArtifactData> artData =
new ArrayList<StixArtifactData>();
297 for (AbstractFile a : secondaryHits) {
298 artData.add(
new StixArtifactData(a,
id,
"FileObject"));
300 return new ObservableResult(
id,
"FileObject: Found " + secondaryHits.size() +
" matches for " + whereClause
301 +
" and secondary tests on " + listSecondaryFields() + getPrintableWarnings(),
302 spacing, ObservableResult.ObservableState.TRUE, artData);
306 return new ObservableResult(
id,
"FileObject: Found no matches for " + whereClause + getPrintableWarnings(),
307 spacing, ObservableResult.ObservableState.FALSE, null);
309 }
catch (TskCoreException ex) {
310 return new ObservableResult(
id,
"FileObject: Exception during evaluation: " + ex.getLocalizedMessage(),
311 spacing, ObservableResult.ObservableState.INDETERMINATE, null);
317 return new ObservableResult(
id,
"FileObject: No evaluatable fields " + getPrintableWarnings(), spacing,
318 ObservableResult.ObservableState.INDETERMINATE, null);
327 private String listSecondaryFields() {
328 String secondaryFields =
"";
330 if (obj.isIsMasqueraded() != null) {
331 secondaryFields +=
"is_masqueraded ";
334 if (obj.getFileFormat() != null) {
335 secondaryFields +=
"File_Format ";
338 return secondaryFields;
346 private String listUnsupportedFields() {
347 String unsupportedFields =
"";
349 if (obj.isIsPacked() != null) {
350 unsupportedFields +=
"is_packed ";
352 if (obj.getDevicePath() != null) {
353 unsupportedFields +=
"Device_Path ";
355 if (obj.getFullPath() != null) {
356 unsupportedFields +=
"Full_Path ";
358 if (obj.getMagicNumber() != null) {
359 unsupportedFields +=
"Magic_Number ";
361 if (obj.getDigitalSignatures() != null) {
362 unsupportedFields +=
"Digital_Signatures ";
364 if (obj.getFileAttributesList() != null) {
365 unsupportedFields +=
"File_Attributes_List ";
367 if (obj.getPermissions() != null) {
368 unsupportedFields +=
"Permissions ";
370 if (obj.getUserOwner() != null) {
371 unsupportedFields +=
"User_Owner ";
373 if (obj.getPackerList() != null) {
374 unsupportedFields +=
"Packer_List ";
376 if (obj.getPeakEntropy() != null) {
377 unsupportedFields +=
"Peak_Entropy ";
379 if (obj.getSymLinks() != null) {
380 unsupportedFields +=
"Sym_Links ";
382 if (obj.getByteRuns() != null) {
383 unsupportedFields +=
"Bytes_Runs ";
385 if (obj.getExtractedFeatures() != null) {
386 unsupportedFields +=
"Extracted_Features ";
388 if (obj.getEncryptionAlgorithm() != null) {
389 unsupportedFields +=
"Encryption_Algorithm ";
391 if (obj.getDecryptionKey() != null) {
392 unsupportedFields +=
"Decryption_Key ";
394 if (obj.getCompressionMethod() != null) {
395 unsupportedFields +=
"Compression_Method ";
397 if (obj.getCompressionVersion() != null) {
398 unsupportedFields +=
"Compression_Version ";
400 if (obj.getCompressionComment() != null) {
401 unsupportedFields +=
"Compression_Comment ";
404 return unsupportedFields;
416 private static long convertTimestamp(String timeStr)
throws ParseException {
417 SimpleDateFormat dateFormat =
new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss'Z'");
418 dateFormat.setTimeZone(TimeZone.getTimeZone(
"GMT"));
419 Date parsedDate = dateFormat.parse(timeStr);
421 Long unixTime = parsedDate.getTime() / 1000;
437 private static String processULongObject(UnsignedLongObjectPropertyType longObj, String fieldName)
438 throws TskCoreException {
440 return processNumericFields(longObj.getValue().toString(), longObj.getCondition(),
441 longObj.getApplyCondition(), fieldName);
456 private static String processNumericFields(String valueStr, ConditionTypeEnum typeCondition,
457 ConditionApplicationEnum applyCondition, String fieldName)
458 throws TskCoreException {
460 if ((typeCondition == null)
461 || ((typeCondition != ConditionTypeEnum.INCLUSIVE_BETWEEN)
462 && (typeCondition != ConditionTypeEnum.EXCLUSIVE_BETWEEN))) {
464 String fullClause =
"";
466 if (valueStr.isEmpty()) {
467 throw new TskCoreException(
"Empty value field");
470 String[] parts = valueStr.split(
"##comma##");
472 for (String valuePart : parts) {
473 String partialClause;
475 if ((typeCondition == null)
476 || (typeCondition == ConditionTypeEnum.EQUALS)) {
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;
490 throw new TskCoreException(
"Could not process condition " + typeCondition.value() +
" on " + fieldName);
493 if (fullClause.isEmpty()) {
495 if (parts.length > 1) {
498 if (applyCondition == ConditionApplicationEnum.NONE) {
499 fullClause +=
" NOT ";
501 fullClause += partialClause;
503 if (applyCondition == ConditionApplicationEnum.ALL) {
504 fullClause +=
" AND " + partialClause;
505 }
else if (applyCondition == ConditionApplicationEnum.NONE) {
506 fullClause +=
" AND NOT " + partialClause;
508 fullClause +=
" OR " + partialClause;
513 if (parts.length > 1) {
520 if (typeCondition == ConditionTypeEnum.INCLUSIVE_BETWEEN) {
521 String[] parts = valueStr.split(
"##comma##");
522 if (parts.length != 2) {
523 throw new TskCoreException(
"Unexpected number of arguments in INCLUSIVE_BETWEEN on " + fieldName
524 +
"(" + valueStr +
")");
526 return (fieldName +
">=" + parts[0] +
" AND " + fieldName +
"<=" + parts[1]);
528 String[] parts = valueStr.split(
"##comma##");
529 if (parts.length != 2) {
530 throw new TskCoreException(
"Unexpected number of arguments in EXCLUSIVE_BETWEEN on " + fieldName
531 +
"(" + valueStr +
")");
533 return (fieldName +
">" + parts[0] +
" AND " + fieldName +
"<" + parts[1]);
548 private static String processStringObject(StringObjectPropertyType stringObj, String fieldName)
549 throws TskCoreException {
551 return processStringObject(stringObj.getValue().toString(), stringObj.getCondition(),
552 stringObj.getApplyCondition(), fieldName);
567 public static String processStringObject(String valueStr, ConditionTypeEnum condition,
568 ConditionApplicationEnum applyCondition, String fieldName)
569 throws TskCoreException {
571 String fullClause =
"";
572 String lowerFieldName =
"lower(" + fieldName +
")";
574 if (valueStr.isEmpty()) {
575 throw new TskCoreException(
"Empty value field");
578 String[] parts = valueStr.split(
"##comma##");
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 +
"%\'";
590 }
else if (condition == ConditionTypeEnum.DOES_NOT_CONTAIN) {
591 partialClause = lowerFieldName +
" NOT LIKE \'%" + lowerValue +
"%\'";
592 }
else if (condition == ConditionTypeEnum.STARTS_WITH) {
593 partialClause = lowerFieldName +
" LIKE \'" + lowerValue +
"%\'";
594 }
else if (condition == ConditionTypeEnum.ENDS_WITH) {
595 partialClause = lowerFieldName +
" LIKE \'%" + lowerValue +
"\'";
597 throw new TskCoreException(
"Could not process condition " + condition.value() +
" on " + fieldName);
600 if (fullClause.isEmpty()) {
602 if (parts.length > 1) {
605 if (applyCondition == ConditionApplicationEnum.NONE) {
606 fullClause +=
" NOT ";
608 fullClause += partialClause;
610 if (applyCondition == ConditionApplicationEnum.ALL) {
611 fullClause +=
" AND " + partialClause;
612 }
else if (applyCondition == ConditionApplicationEnum.NONE) {
613 fullClause +=
" AND NOT " + partialClause;
615 fullClause +=
" OR " + partialClause;
620 if (parts.length > 1) {
638 private static String processTimestampObject(DateTimeObjectPropertyType dateObj, String fieldName)
639 throws TskCoreException {
641 if (DatatypeEnum.DATE_TIME == dateObj.getDatatype()) {
644 String result = convertTimestampString(dateObj.getValue().toString());
645 return processNumericFields(result, dateObj.getCondition(), dateObj.getApplyCondition(), fieldName);
648 throw new TskCoreException(
"Found non DATE_TIME field on " + fieldName);
662 private static String convertTimestampString(String timestampStr)
663 throws TskCoreException {
666 if (timestampStr.length() > 0) {
667 String[] parts = timestampStr.split(
"##comma##");
669 for (
int i = 0; i < parts.length - 1; i++) {
670 long unixTime = convertTimestamp(parts[i]);
671 result += unixTime +
"##comma##";
673 result += convertTimestamp(parts[parts.length - 1]);
676 }
catch (java.text.ParseException ex) {
677 throw new TskCoreException(
"Error parsing timestamp string " + timestampStr);
690 private static String addClause(String a_clause, String a_newClause) {
692 if ((a_clause == null) || a_clause.isEmpty()) {
696 return (a_clause +
" AND " + a_newClause);