Windows.NTFS.Timestomp

This artifact enables triage to detect potential time stomped files.

Checks:

  • $STANDARD_INFORMATION “B” time prior to $FILE_NAME “B” time
  • $STANDARD_INFORMATION “B” or “M” time has nanosecond precision.
  • PE compile time prior to any $STANDARD_INFORMATION time stamp.

Optional:

  • $STANDARD_INFORMATION “M” time prior to ShimCache timestamp
  • $STANDARD_INFORMATION times prior to $I30 slack “B” or “M” times.
  • Full PE metadata output.

Note: If an option is selected the artifact will also output additional metadata for context.

Available filters include:
PathRegex (OSPath): e.g ^C:\folder\file.ext$ or partial \folder\folder2\ or string|string2|string3
FileRegex: ^filename.ext$ or partial string1|string2


name: Windows.NTFS.Timestomp
author: "Matt Green - @mgreen27"
description: |
    This artifact enables triage to detect potential time stomped files.

    Checks:
    
    - $STANDARD_INFORMATION “B” time prior to $FILE_NAME “B” time
    - $STANDARD_INFORMATION “B” or "M" time has nanosecond precision. 
    - PE compile time prior to any $STANDARD_INFORMATION time stamp.  
    
    Optional:  
    
    - $STANDARD_INFORMATION “M” time prior to ShimCache timestamp
    - $STANDARD_INFORMATION times prior to $I30 slack "B" or "M" times.
    - Full PE metadata output.
    
    Note: If an option is selected the artifact will also output additional metadata for context.
    
    Available filters include:  
      PathRegex (OSPath): e.g ^C:\\folder\\file\.ext$ or partial \\folder\\folder2\\ or string|string2|string3  
      FileRegex: ^filename.ext$ or partial string1|string2  

    
parameters:
  - name: MFTDrive
    description: |
      The path to to the drive that holds the MFT file (can be a
      pathspec).
    default: "C:"
  - name: PathRegex
    description: "Regex search over FullPath."
    default: "."
  - name: FileRegex
    description: "Regex search over FileName."
    default: "."
    type: regex
  - name: UploadHits
    type: bool
    description: "Upload complete complete attribute data."
  - name: ShimcacheTest
    type: bool
    description: "If PE, check $STANDARD_INFORMATION “M” time prior to ShimCache timestamp"
  - name: I30Test
    type: bool
    description: "Check $STANDARD_INFORMATION times prior to $I30 slack B or M times."
  - name: OutputPEInfo
    type: bool
    description: "Output full PE metadata information."

sources:
  - query: |
      -- find all MFT entries in scope
      LET mft_entries = SELECT *,OSPath,FileName,FileNames,EntryNumber,
            LastModified0x10,LastAccess0x10, LastRecordChange0x10,Created0x10, --SI used in test
            LastModified0x30,Created0x30, -- FN used in test
            IsDir,InUse,SI_Lt_FN,uSecZeros,
            parse_pe(file=OSPath) as PE,
            magic(path=OSPath) as Magic
        FROM Artifact.Windows.NTFS.MFT(MFTDrive=MFTDrive,PathRegex=PathRegex,FileRegex=FileRegex)
        --WHERE NOT IsDir
      
      -- if MZ files collect shimcache to compare modification time
      LET shimcache <= SELECT * 
        FROM if(condition= ShimcacheTest,
            then= { SELECT Name, ModificationTime FROM Artifact.Windows.Registry.AppCompatCache() })
      LET shimcache_mtime(target) = SELECT Name, ModificationTime FROM shimcache 
        WHERE Name = target 
        ORDER BY ModificationTime
      
      LET i30 <= SELECT * FROM if(condition= I30Test,
            then= { 
                SELECT * FROM foreach(
                    row={ 
                        SELECT dirname(path=OSPath) as Directory FROM mft_entries
                        GROUP BY Directory
                    },
                    query={
                        SELECT
                            split(sep_string='\\\\.\\',string=FullPath)[1] + '\\' + Name as FullPath,
                            IsSlack,MFTId,Mtime,Atime,Ctime,Btime
                        FROM Artifact.Windows.NTFS.I30(DirectoryGlobs=Directory,preconditions=True)
                        WHERE MFTId OR FullPath
                    })
            })
      LET find_i30_slack(inode,folder,filenames,mtime,ctime,slackcheck) = SELECT * FROM i30 
        WHERE MFTId = str(str=inode) OR ( split(sep_string='''\\.\''',string=FullPath)[1] = folder AND Name in filenames) 
            AND if(condition= slackcheck,
                then= ( Mtime > mtime OR Btime > btime ) AND IsSlack,
                else= True )
                
      LET base_results = SELECT 
            OSPath,
            dict(Created0x10=Created0x10,Created0x30=Created0x30) as CreatedTimestamps,
            InUse,
            SI_Lt_FN as `SI<FN`,uSecZeros,
            if(condition=PE.FileHeader.TimeDateStamp,
                then= if(condition= LastModified0x10 < PE.FileHeader.TimeDateStamp OR LastAccess0x10 < PE.FileHeader.TimeDateStamp OR LastRecordChange0x10 < PE.FileHeader.TimeDateStamp OR Created0x10 < PE.FileHeader.TimeDateStamp,
                        then= True,
                        else= False),
                else= 'N/A') as SuspiciousCompileTime,
            parse_ntfs(mft=EntryNumber, device=MFTDrive ) as NtfsMetadata,
            Magic,
            if(condition=PE, then=PE,else='N/A') as PE,
            Created0x10,LastModified0x10,FileNames,EntryNumber,
            if(condition= Magic=~'^PE' AND NOT Magic =~ '\(DLL\)',
                then= if(condition= LastModified0x10 < shimcache_mtime(target=str(str=OSPath))[0].ModificationTime,
                    then= True,
                    else= False),
                else= 'N/A') as SuspiciousShimcache,
            if(condition= Magic=~'^PE' AND NOT Magic =~ '\(DLL\)',
                then= shimcache_mtime(target=str(str=OSPath))[0],
                else= 'N/A') as Shimcache,
            if(condition = find_i30_slack(inode=EntryNumber,folder=dirname(path=OSPath),filenames=FileNames,mtime=LastModified0x10,ctime=Created0x10,slackcheck=True),
                then= True,
                else= False ) as SuspiciousI30,
            find_i30_slack(inode=EntryNumber,folder=dirname(path=OSPath),filenames=FileNames,mtime=LastModified0x10,ctime=Created0x10,slackcheck=False) as I30
      FROM mft_entries
      
      LET results = SELECT * FROM if(condition= ShimcacheTest AND I30Test AND OutputPEInfo,
            then={
                SELECT 
                    OSPath,CreatedTimestamps,InUse,
                    `SI<FN`,uSecZeros,
                    SuspiciousCompileTime,
                    SuspiciousShimcache,
                    SuspiciousI30,
                    NtfsMetadata,
                    Magic,
                    Shimcache,
                    I30,
                    PE
                FROM base_results
            },
            else= if(condition= ShimcacheTest AND I30Test,
                then={
                SELECT 
                    OSPath,CreatedTimestamps,InUse,
                    `SI<FN`,uSecZeros,
                    SuspiciousCompileTime,
                    SuspiciousShimcache,
                    SuspiciousI30,
                    NtfsMetadata,
                    Magic,
                    Shimcache,
                    I30
                FROM base_results
            },
            else= if(condition= ShimcacheTest AND OutputPEInfo,
                then={
                    SELECT 
                    OSPath,CreatedTimestamps,InUse,
                    `SI<FN`,uSecZeros,
                    SuspiciousCompileTime,
                    SuspiciousShimcache,
                    NtfsMetadata,
                    Magic,
                    Shimcache,
                    PE
                FROM base_results
            },
            else= if(condition= I30Test AND OutputPEInfo,
                then={
                    SELECT 
                    OSPath,CreatedTimestamps,InUse,
                    `SI<FN`,uSecZeros,
                    SuspiciousCompileTime,
                    SuspiciousI30,
                    NtfsMetadata,
                    Magic,
                    I30,
                    PE
                FROM base_results
            },
            else= if(condition= ShimcacheTest,
                then={
                    SELECT 
                    OSPath,CreatedTimestamps,InUse,
                    `SI<FN`,uSecZeros,
                    SuspiciousCompileTime,
                    SuspiciousShimcache,
                    NtfsMetadata,
                    Magic,
                    Shimcache
                FROM base_results
            },
            else= if(condition= I30Test,
                then={
                    SELECT 
                    OSPath,CreatedTimestamps,InUse,
                    `SI<FN`,uSecZeros,
                    SuspiciousCompileTime,
                    SuspiciousI30,
                    NtfsMetadata,
                    Magic,
                    I30
                FROM base_results
            },
            else= if(condition= OutputPEInfo,
                then={
                    SELECT 
                    OSPath,CreatedTimestamps,InUse,
                    `SI<FN`,uSecZeros,
                    SuspiciousCompileTime,
                    NtfsMetadata,
                    Magic,
                    PE
                FROM base_results
            },
                else={
                    SELECT 
                        OSPath,CreatedTimestamps,InUse,
                        `SI<FN`,uSecZeros,
                        SuspiciousCompileTime,
                        NtfsMetadata,
                        Magic
                    FROM base_results
            })
            ))))))

      LET upload_results = SELECT *, upload(file=OSPath) as Upload FROM results
      
      SELECT * FROM if(condition= UploadHits,
        then= upload_results,
        else= results)