19 package org.sleuthkit.autopsy.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;
 
   48 class EvalFileObj 
extends EvaluatableObject {
 
   50     private final FileObjectType obj;
 
   52     public EvalFileObj(FileObjectType a_obj, String a_id, String a_spacing) {
 
   59     public synchronized ObservableResult evaluate() {
 
   61         Case case1 = Case.getCurrentCase();
 
   62         SleuthkitCase sleuthkitCase = case1.getSleuthkitCase();
 
   65         String whereClause = 
"";
 
   67         if (obj.getSizeInBytes() != null) {
 
   69                 String newClause = processULongObject(obj.getSizeInBytes(), 
"size"); 
 
   70                 whereClause = addClause(whereClause, newClause);
 
   71             } 
catch (TskCoreException ex) {
 
   72                 addWarning(ex.getLocalizedMessage());
 
   76         if (obj.getFileName() != null) {
 
   78                 String newClause = processStringObject(obj.getFileName(), 
"name"); 
 
   79                 whereClause = addClause(whereClause, newClause);
 
   80             } 
catch (TskCoreException ex) {
 
   81                 addWarning(ex.getLocalizedMessage());
 
   85         if (obj.getFileExtension() != null) {
 
   86             if ((obj.getFileExtension().getCondition() == null)
 
   87                     || (obj.getFileExtension().getCondition() == ConditionTypeEnum.EQUALS)) {
 
   88                 String newClause = 
"name LIKE \'%" + obj.getFileExtension().getValue() + 
"\'"; 
 
   89                 whereClause = addClause(whereClause, newClause);
 
   92                         "Could not process condition " + obj.getFileExtension().getCondition().value() + 
" on file extension"); 
 
   96         if (obj.getFilePath() != null) {
 
   99                 String[] parts = obj.getFilePath().getValue().toString().split(
"##comma##"); 
 
  100                 String finalPathStr = 
"";
 
  102                 for (String filePath : parts) {
 
  104                     String currentFilePath = filePath;
 
  107                     if (currentFilePath.matches(
"^[A-Za-z]:.*")) {
 
  108                         currentFilePath = currentFilePath.substring(2);
 
  112                     currentFilePath = currentFilePath.replace(
"\\", 
"/");
 
  115                     if (!currentFilePath.startsWith(
"/")) {
 
  116                         currentFilePath = 
"/" + currentFilePath;
 
  120                     if (!currentFilePath.endsWith(
"/")) {
 
  121                         int lastSlash = currentFilePath.lastIndexOf(
'/');
 
  122                         if (lastSlash >= 0) {
 
  123                             currentFilePath = currentFilePath.substring(0, lastSlash + 1);
 
  128                     if (!finalPathStr.isEmpty()) {
 
  129                         finalPathStr += 
"##comma##"; 
 
  131                     finalPathStr += currentFilePath;
 
  134                 String newClause = processStringObject(finalPathStr, obj.getFilePath().getCondition(),
 
  135                         obj.getFilePath().getApplyCondition(), 
"parent_path"); 
 
  137                 whereClause = addClause(whereClause, newClause);
 
  138             } 
catch (TskCoreException ex) {
 
  139                 addWarning(ex.getLocalizedMessage());
 
  143         if (obj.getCreatedTime() != null) {
 
  145                 String newClause = processTimestampObject(obj.getCreatedTime(), 
"crtime"); 
 
  146                 whereClause = addClause(whereClause, newClause);
 
  147             } 
catch (TskCoreException ex) {
 
  148                 addWarning(ex.getLocalizedMessage());
 
  152         if (obj.getModifiedTime() != null) {
 
  154                 String newClause = processTimestampObject(obj.getModifiedTime(), 
"mtime"); 
 
  155                 whereClause = addClause(whereClause, newClause);
 
  156             } 
catch (TskCoreException ex) {
 
  157                 addWarning(ex.getLocalizedMessage());
 
  161         if (obj.getAccessedTime() != null) {
 
  163                 String newClause = processTimestampObject(obj.getAccessedTime(), 
"atime"); 
 
  164                 whereClause = addClause(whereClause, newClause);
 
  165             } 
catch (TskCoreException ex) {
 
  166                 addWarning(ex.getLocalizedMessage());
 
  170         if (obj.getHashes() != null) {
 
  171             for (HashType h : obj.getHashes().getHashes()) {
 
  172                 if (h.getSimpleHashValue() != null) {
 
  173                     if (h.getType().getValue().equals(
"MD5")) { 
 
  174                         String newClause = 
"";
 
  175                         if(h.getSimpleHashValue().getValue().toString().toLowerCase().contains(
"##comma##")){
 
  176                             String[] parts = h.getSimpleHashValue().getValue().toString().toLowerCase().split(
"##comma##"); 
 
  177                             String hashList = 
"";
 
  179                                 if(!hashList.isEmpty()){
 
  182                                 hashList += 
"\'" + s + 
"\'";
 
  184                             newClause = 
"md5 IN (" + hashList + 
")";
 
  187                             newClause = 
"md5=\'" + h.getSimpleHashValue().getValue().toString().toLowerCase() + 
"\'"; 
 
  189                         whereClause = addClause(whereClause, newClause);
 
  191                         addWarning(
"Could not process hash type " + h.getType().getValue().toString()); 
 
  194                     addWarning(
"Could not process non-simple hash value"); 
 
  199         if (obj instanceof WindowsExecutableFileObjectType) {
 
  200             WindowsExecutableFileObjectType winExe = (WindowsExecutableFileObjectType) obj;
 
  201             if (winExe.getHeaders() != null) {
 
  202                 if (winExe.getHeaders().getFileHeader() != null) {
 
  203                     if (winExe.getHeaders().getFileHeader().getTimeDateStamp() != null) {
 
  205                             String result = convertTimestampString(winExe.getHeaders().getFileHeader().getTimeDateStamp().getValue().toString());
 
  206                             String newClause = processNumericFields(result,
 
  207                                     winExe.getHeaders().getFileHeader().getTimeDateStamp().getCondition(),
 
  208                                     winExe.getHeaders().getFileHeader().getTimeDateStamp().getApplyCondition(),
 
  210                             whereClause = addClause(whereClause, newClause);
 
  211                         } 
catch (TskCoreException ex) {
 
  212                             addWarning(ex.getLocalizedMessage());
 
  219         String unsupportedFields = listUnsupportedFields();
 
  220         if (!unsupportedFields.isEmpty()) {
 
  221             addWarning(
"Unsupported fields: " + unsupportedFields); 
 
  224         if (whereClause.length() > 0) {
 
  226                 List<AbstractFile> matchingFiles = sleuthkitCase.findAllFilesWhere(whereClause);
 
  228                 if (!matchingFiles.isEmpty()) {
 
  230                     if (listSecondaryFields().isEmpty()) {
 
  232                         List<StixArtifactData> artData = 
new ArrayList<StixArtifactData>();
 
  233                         for (AbstractFile a : matchingFiles) {
 
  234                             artData.add(
new StixArtifactData(a, 
id, 
"FileObject")); 
 
  237                         return new ObservableResult(
id, 
"FileObject: Found " + matchingFiles.size() + 
" matches for " + whereClause + getPrintableWarnings(), 
 
  238                                 spacing, ObservableResult.ObservableState.TRUE, artData);
 
  242                         List<AbstractFile> secondaryHits = 
new ArrayList<AbstractFile>();
 
  244                         for (AbstractFile file : matchingFiles) {
 
  245                             boolean passedTests = 
true;
 
  247                             if (obj.isIsMasqueraded() != null) {
 
  248                                 List<BlackboardArtifact> arts = file.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED);
 
  249                                 boolean isMasq = 
false;
 
  250                                 if (!arts.isEmpty()) {
 
  254                                 if (obj.isIsMasqueraded() != isMasq) {
 
  260                             if (obj.getFileFormat() != null) {
 
  262                                 boolean foundMatch = 
false;
 
  263                                 String formatsFound = 
"";
 
  264                                 List<BlackboardArtifact> arts = file.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO);
 
  265                                 for (BlackboardArtifact artifact : arts) {
 
  266                                     for (BlackboardAttribute attr : artifact.getAttributes()) {
 
  267                                         if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG.getTypeID()) {
 
  268                                             if (!formatsFound.isEmpty()) {
 
  269                                                 formatsFound += 
", ";
 
  271                                             formatsFound += attr.getValueString();
 
  272                                             if (attr.getValueString().equalsIgnoreCase(obj.getFileFormat().getValue().toString())) {
 
  278                                             String type = attr.getValueString().replaceFirst(
"^.*/", 
"");
 
  281                                             if (compareStringObject(type, ConditionTypeEnum.CONTAINS, null, obj.getFileFormat().getValue().toString())) {
 
  293                                 if (formatsFound.isEmpty()) {
 
  294                                     addWarning(
"Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString() 
 
  295                                             + 
" (no file formats found)"); 
 
  298                                         addWarning(
"Warning: Did not match File_Format field " + obj.getFileFormat().getValue().toString() 
 
  299                                                 + 
" against " + formatsFound); 
 
  305                                 secondaryHits.add(file);
 
  309                         if (secondaryHits.isEmpty()) {
 
  311                             return new ObservableResult(
id, 
"FileObject: Found " + matchingFiles.size() + 
" matches for " + whereClause 
 
  312                                     + 
" but none for secondary tests on " + listSecondaryFields() + getPrintableWarnings(), 
 
  313                                     spacing, ObservableResult.ObservableState.FALSE, null);
 
  315                             List<StixArtifactData> artData = 
new ArrayList<StixArtifactData>();
 
  316                             for (AbstractFile a : secondaryHits) {
 
  317                                 artData.add(
new StixArtifactData(a, 
id, 
"FileObject")); 
 
  319                             return new ObservableResult(
id, 
"FileObject: Found " + secondaryHits.size() + 
" matches for " + whereClause 
 
  320                                     + 
" and secondary tests on " + listSecondaryFields() + getPrintableWarnings(), 
 
  321                                     spacing, ObservableResult.ObservableState.TRUE, artData);
 
  325                     return new ObservableResult(
id, 
"FileObject: Found no matches for " + whereClause + getPrintableWarnings(), 
 
  326                             spacing, ObservableResult.ObservableState.FALSE, null);
 
  328             } 
catch (TskCoreException ex) {
 
  329                 return new ObservableResult(
id, 
"FileObject: Exception during evaluation: " + ex.getLocalizedMessage(), 
 
  330                         spacing, ObservableResult.ObservableState.INDETERMINATE, null);
 
  336         return new ObservableResult(
id, 
"FileObject: No evaluatable fields " + getPrintableWarnings(), spacing, 
 
  337                 ObservableResult.ObservableState.INDETERMINATE, null);
 
  346     private String listSecondaryFields() {
 
  347         String secondaryFields = 
"";
 
  349         if (obj.isIsMasqueraded() != null) {
 
  350             secondaryFields += 
"is_masqueraded "; 
 
  353         if (obj.getFileFormat() != null) {
 
  354             secondaryFields += 
"File_Format "; 
 
  357         return secondaryFields;
 
  365     private String listUnsupportedFields() {
 
  366         String unsupportedFields = 
"";
 
  368         if (obj.isIsPacked() != null) {
 
  369             unsupportedFields += 
"is_packed "; 
 
  371         if (obj.getDevicePath() != null) {
 
  372             unsupportedFields += 
"Device_Path "; 
 
  374         if (obj.getFullPath() != null) {
 
  375             unsupportedFields += 
"Full_Path "; 
 
  377         if (obj.getMagicNumber() != null) {
 
  378             unsupportedFields += 
"Magic_Number "; 
 
  380         if (obj.getDigitalSignatures() != null) {
 
  381             unsupportedFields += 
"Digital_Signatures "; 
 
  383         if (obj.getFileAttributesList() != null) {
 
  384             unsupportedFields += 
"File_Attributes_List "; 
 
  386         if (obj.getPermissions() != null) {
 
  387             unsupportedFields += 
"Permissions "; 
 
  389         if (obj.getUserOwner() != null) {
 
  390             unsupportedFields += 
"User_Owner "; 
 
  392         if (obj.getPackerList() != null) {
 
  393             unsupportedFields += 
"Packer_List "; 
 
  395         if (obj.getPeakEntropy() != null) {
 
  396             unsupportedFields += 
"Peak_Entropy "; 
 
  398         if (obj.getSymLinks() != null) {
 
  399             unsupportedFields += 
"Sym_Links "; 
 
  401         if (obj.getByteRuns() != null) {
 
  402             unsupportedFields += 
"Bytes_Runs "; 
 
  404         if (obj.getExtractedFeatures() != null) {
 
  405             unsupportedFields += 
"Extracted_Features "; 
 
  407         if (obj.getEncryptionAlgorithm() != null) {
 
  408             unsupportedFields += 
"Encryption_Algorithm "; 
 
  410         if (obj.getDecryptionKey() != null) {
 
  411             unsupportedFields += 
"Decryption_Key "; 
 
  413         if (obj.getCompressionMethod() != null) {
 
  414             unsupportedFields += 
"Compression_Method "; 
 
  416         if (obj.getCompressionVersion() != null) {
 
  417             unsupportedFields += 
"Compression_Version "; 
 
  419         if (obj.getCompressionComment() != null) {
 
  420             unsupportedFields += 
"Compression_Comment "; 
 
  423         return unsupportedFields;
 
  433     private static long convertTimestamp(String timeStr) 
throws ParseException {
 
  434         SimpleDateFormat dateFormat = 
new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss'Z'"); 
 
  435         dateFormat.setTimeZone(TimeZone.getTimeZone(
"GMT")); 
 
  436         Date parsedDate = dateFormat.parse(timeStr);
 
  438         Long unixTime = parsedDate.getTime() / 1000;
 
  452     private static String processULongObject(UnsignedLongObjectPropertyType longObj, String fieldName)
 
  453             throws TskCoreException {
 
  455         return processNumericFields(longObj.getValue().toString(), longObj.getCondition(),
 
  456                 longObj.getApplyCondition(), fieldName);
 
  469     private static String processNumericFields(String valueStr, ConditionTypeEnum typeCondition,
 
  470             ConditionApplicationEnum applyCondition, String fieldName)
 
  471             throws TskCoreException {
 
  473         if ((typeCondition == null)
 
  474                 || ((typeCondition != ConditionTypeEnum.INCLUSIVE_BETWEEN)
 
  475                 && (typeCondition != ConditionTypeEnum.EXCLUSIVE_BETWEEN))) {
 
  477             String fullClause = 
"";
 
  479             if (valueStr.isEmpty()) {
 
  480                 throw new TskCoreException(
"Empty value field"); 
 
  483             String[] parts = valueStr.split(
"##comma##"); 
 
  485             for (String valuePart : parts) {
 
  486                 String partialClause;
 
  488                 if ((typeCondition == null)
 
  489                         || (typeCondition == ConditionTypeEnum.EQUALS)) {
 
  491                     partialClause = fieldName + 
"=" + valuePart;
 
  492                 } 
else if (typeCondition == ConditionTypeEnum.DOES_NOT_EQUAL) {
 
  493                     partialClause = fieldName + 
"!=" + valuePart;
 
  494                 } 
else if (typeCondition == ConditionTypeEnum.GREATER_THAN) {
 
  495                     partialClause = fieldName + 
">" + valuePart;
 
  496                 } 
else if (typeCondition == ConditionTypeEnum.GREATER_THAN_OR_EQUAL) {
 
  497                     partialClause = fieldName + 
">=" + valuePart;
 
  498                 } 
else if (typeCondition == ConditionTypeEnum.LESS_THAN) {
 
  499                     partialClause = fieldName + 
"<" + valuePart;
 
  500                 } 
else if (typeCondition == ConditionTypeEnum.LESS_THAN_OR_EQUAL) {
 
  501                     partialClause = fieldName + 
"<=" + valuePart;
 
  503                     throw new TskCoreException(
"Could not process condition " + typeCondition.value() + 
" on " + fieldName); 
 
  506                 if (fullClause.isEmpty()) {
 
  508                     if (parts.length > 1) {
 
  511                     if (applyCondition == ConditionApplicationEnum.NONE) {
 
  512                         fullClause += 
" NOT "; 
 
  514                     fullClause += partialClause;
 
  516                     if (applyCondition == ConditionApplicationEnum.ALL) {
 
  517                         fullClause += 
" AND " + partialClause; 
 
  518                     } 
else if (applyCondition == ConditionApplicationEnum.NONE) {
 
  519                         fullClause += 
" AND NOT " + partialClause; 
 
  521                         fullClause += 
" OR " + partialClause; 
 
  526             if (parts.length > 1) {
 
  533             if (typeCondition == ConditionTypeEnum.INCLUSIVE_BETWEEN) {
 
  534                 String[] parts = valueStr.split(
"##comma##"); 
 
  535                 if (parts.length != 2) {
 
  536                     throw new TskCoreException(
"Unexpected number of arguments in INCLUSIVE_BETWEEN on " + fieldName 
 
  537                             + 
"(" + valueStr + 
")");
 
  539                 return (fieldName + 
">=" + parts[0] + 
" AND " + fieldName + 
"<=" + parts[1]); 
 
  541                 String[] parts = valueStr.split(
"##comma##"); 
 
  542                 if (parts.length != 2) {
 
  543                     throw new TskCoreException(
"Unexpected number of arguments in EXCLUSIVE_BETWEEN on " + fieldName 
 
  544                             + 
"(" + valueStr + 
")");
 
  546                 return (fieldName + 
">" + parts[0] + 
" AND " + fieldName + 
"<" + parts[1]); 
 
  559     private static String processStringObject(StringObjectPropertyType stringObj, String fieldName)
 
  560             throws TskCoreException {
 
  562         return processStringObject(stringObj.getValue().toString(), stringObj.getCondition(),
 
  563                 stringObj.getApplyCondition(), fieldName);
 
  576     public static String processStringObject(String valueStr, ConditionTypeEnum condition,
 
  577             ConditionApplicationEnum applyCondition, String fieldName)
 
  578             throws TskCoreException {
 
  580         String fullClause = 
"";
 
  581         String lowerFieldName = 
"lower(" + fieldName + 
")"; 
 
  583         if (valueStr.isEmpty()) {
 
  584             throw new TskCoreException(
"Empty value field"); 
 
  587         String[] parts = valueStr.split(
"##comma##"); 
 
  589         for (String value : parts) {
 
  590             String lowerValue = value.toLowerCase();
 
  591             String partialClause;
 
  592             if ((condition == null)
 
  593                     || (condition == ConditionTypeEnum.EQUALS)) {
 
  594                 partialClause = lowerFieldName + 
"=\'" + lowerValue + 
"\'";
 
  595             } 
else if (condition == ConditionTypeEnum.DOES_NOT_EQUAL) {
 
  596                 partialClause = lowerFieldName + 
" !=\'%" + lowerValue + 
"%\'";
 
  597             } 
else if (condition == ConditionTypeEnum.CONTAINS) {
 
  598                 partialClause = lowerFieldName + 
" LIKE \'%" + lowerValue + 
"%\'"; 
 
  599             } 
else if (condition == ConditionTypeEnum.DOES_NOT_CONTAIN) {
 
  600                 partialClause = lowerFieldName + 
" NOT LIKE \'%" + lowerValue + 
"%\'"; 
 
  601             } 
else if (condition == ConditionTypeEnum.STARTS_WITH) {
 
  602                 partialClause = lowerFieldName + 
" LIKE \'" + lowerValue + 
"%\'"; 
 
  603             } 
else if (condition == ConditionTypeEnum.ENDS_WITH) {
 
  604                 partialClause = lowerFieldName + 
" LIKE \'%" + lowerValue + 
"\'"; 
 
  606                 throw new TskCoreException(
"Could not process condition " + condition.value() + 
" on " + fieldName); 
 
  609             if (fullClause.isEmpty()) {
 
  611                 if (parts.length > 1) {
 
  614                 if (applyCondition == ConditionApplicationEnum.NONE) {
 
  615                     fullClause += 
" NOT "; 
 
  617                 fullClause += partialClause;
 
  619                 if (applyCondition == ConditionApplicationEnum.ALL) {
 
  620                     fullClause += 
" AND " + partialClause; 
 
  621                 } 
else if (applyCondition == ConditionApplicationEnum.NONE) {
 
  622                     fullClause += 
" AND NOT " + partialClause; 
 
  624                     fullClause += 
" OR " + partialClause; 
 
  629         if (parts.length > 1) {
 
  645     private static String processTimestampObject(DateTimeObjectPropertyType dateObj, String fieldName)
 
  646             throws TskCoreException {
 
  648         if (DatatypeEnum.DATE_TIME == dateObj.getDatatype()) {
 
  651             String result = convertTimestampString(dateObj.getValue().toString());
 
  652             return processNumericFields(result, dateObj.getCondition(), dateObj.getApplyCondition(), fieldName);
 
  655             throw new TskCoreException(
"Found non DATE_TIME field on " + fieldName); 
 
  667     private static String convertTimestampString(String timestampStr)
 
  668             throws TskCoreException {
 
  671             if (timestampStr.length() > 0) {
 
  672                 String[] parts = timestampStr.split(
"##comma##"); 
 
  674                 for (
int i = 0; i < parts.length - 1; i++) {
 
  675                     long unixTime = convertTimestamp(parts[i]);
 
  676                     result += unixTime + 
"##comma##"; 
 
  678                 result += convertTimestamp(parts[parts.length - 1]);
 
  681         } 
catch (java.text.ParseException ex) {
 
  682             throw new TskCoreException(
"Error parsing timestamp string " + timestampStr); 
 
  694     private static String addClause(String a_clause, String a_newClause) {
 
  696         if ((a_clause == null) || a_clause.isEmpty()) {
 
  700         return (a_clause + 
" AND " + a_newClause);