Sunday, July 26, 2020

Powershell - Evaluate Timestamps of Files in a Directory

I was looking for a script to output files in a directory that may have been changed or created in a given time frame.  The time stamps that are evaluated are the MFT File Record for the file, the creation time, last access time and last write time.  As I was building this, I found a powershell project called PowerForensics which is very nice for more in-depth project related to forensics.

Here is the script use at your own risk:

   

# Modify the directory that you are looking at and the timestamps

$currentDirectory = "c:\windows"
$files = Get-ChildItem $currentDirectory
# Change the below line to look like the below if you want to recursively look at the files in a directory
#$files = Get-ChildItem $currentDirectory -Recurse
$startDate = Get-Date 2020-03-01
$endDate = Get-Date 2020-06-30

# Only outputs results where a creation time, last access time, and last write time are within the timeframe listed above
# Added the function to look at the MFT File Record for the file being examined.  If that time is within the timeframe it 
# is also captured...
#
# Looking at the MFT File Record will indicate time stomping...

Function Color-Text {
    param ( $inDate)
    If (($inDate -ge $startDate) -and ($inDate -le $endDate)) {
        Write-Host "$($inDate) " -ForegroundColor Yellow -NoNewline
    }
    Else {
        Write-Host "$($inDate) " -NoNewline
    }
}

Function Get-ChangeTime {
    # This function is from https://gallery.technet.microsoft.com/scriptcenter/Get-MFT-Timestamp-of-a-file-9227f399
    # Modified to work in this context.

    param ( $inFile )
    $FileStream = [System.IO.File]::Open($inFile, 'Open', 'Read', 'ReadWrite')
    #$FileStream

    #[void][ntdll]
    #region Module Builder
    $Domain = [AppDomain]::CurrentDomain
    $DynAssembly = New-Object System.Reflection.AssemblyName('TestAssembly')
    $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory
    $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('TimeStampModule', $False)
    #endregion Module Builder

    #region ENUMs
    $EnumBuilder = $ModuleBuilder.DefineEnum('FileInformationClass', 'Public', [UInt32])
    # Define values of the enum
    [void]$EnumBuilder.DefineLiteral('FileDirectoryInformation', [UInt32] 1)
    [void]$EnumBuilder.DefineLiteral('FileBasicInformation', [UInt32] 4)
    [void]$EnumBuilder.DefineLiteral('FileModeInformation', [UInt32] 16)
    [void]$EnumBuilder.DefineLiteral('FileHardLinkInformation', [UInt32] 46)

    #Create ENUM Type
    [void]$EnumBuilder.CreateType()
    #endregion ENUMs

   
    #region FileBasicInformation
    #Define STRUCT
    $Attributes = 'AutoLayout, AnsiClass, Class, ExplicitLayout, Sealed, BeforeFieldInit,public'
    $TypeBuilder = $ModuleBuilder.DefineType('FileBasicInformation', $Attributes, [System.ValueType], 8, 0x28)
    $CreateTimeField = $TypeBuilder.DefineField('CreationTime', [UInt64], 'Public')
    $CreateTimeField.SetOffset(0)
    $LastAccessTimeField = $TypeBuilder.DefineField('LastAccessTime', [UInt64], 'Public')
    $LastAccessTimeField.SetOffset(8)
    $LastWriteTimeField = $TypeBuilder.DefineField('LastWriteTime', [UInt64], 'Public')
    $LastWriteTimeField.SetOffset(16)
    $ChangeTimeField = $TypeBuilder.DefineField('ChangeTime', [UInt64], 'Public')
    $ChangeTimeField.SetOffset(24)
    $FileAttributesField = $TypeBuilder.DefineField('FileAttributes', [UInt64], 'Public')
    $FileAttributesField.SetOffset(32)
    #Create STRUCT Type
    [void]$TypeBuilder.CreateType()
    #endregion FileBasicInformation

    #region IOStatusBlock
    #Define STRUCT
    $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
    $TypeBuilder = $ModuleBuilder.DefineType('IOStatusBlock', $Attributes, [System.ValueType], 1, 0x10)
    [void]$TypeBuilder.DefineField('status', [UInt64], 'Public')
    [void]$TypeBuilder.DefineField('information', [UInt64], 'Public')
    #Create STRUCT Type
    [void]$TypeBuilder.CreateType()
    #endregion IOStatusBlock

    #region DllImport    $TypeBuilder = $ModuleBuilder.DefineType('ntdll', 'Public, Class')    #region NtQueryInformationFile Method    $PInvokeMethod = $TypeBuilder.DefineMethod(        'NtQueryInformationFile', #Method Name        [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes        [IntPtr], #Method Return Type        [Type[]] @([Microsoft.Win32.SafeHandles.SafeFileHandle], [IOStatusBlock], [IntPtr] ,[UInt16], [FileInformationClass]) #Method Parameters    )
    $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))    $FieldArray = [Reflection.FieldInfo[]] @(        [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),        [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')    )
    $FieldValueArray = [Object[]] @(        'NtQueryInformationFile', #CASE SENSITIVE!!        $True    )
    $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(        $DllImportConstructor,        @('ntdll.dll'),        $FieldArray,        $FieldValueArray    )
    $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)    #endregion NtQueryInformationFile Method
    [void]$TypeBuilder.CreateType()    #endregion DllImport


    $fbi = New-Object "FileBasicInformation"
    $iosb = New-Object "IOStatusBlock"
    $p_fbi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($fbi))
    
    # Pull file timestamps from file
    #[DllImport("ntdll.dll", SetLastError=$true)] 
    $iprc = [ntdll]::NtQueryInformationFile($FileStream.SafeFileHandle, $iosb, $p_fbi, 
        [System.Runtime.InteropServices.Marshal]::SizeOf($fbi), [FileInformationClass]::FileBasicInformation
    )

    

    # Check to make sure no issues occurred
    $IsOK = (($iprc -eq [intptr]::Zero) -AND ($iosb.status -eq 0))

    If ($IsOK) {
        # Pull data from unmanaged memory block into a usable object
        # The below line in the original document does notwork.  Add [System.Type] in the front and it works...
        # https://poshsecurity.com/blog/2014/2/3/powershell-error-the-specified-structure-must-be-blittable-o.html
        $fbi = [System.Runtime.InteropServices.Marshal]::PtrToStructure($p_fbi, [System.Type][FileBasicInformation])
        #$Object = [pscustomobject]@{
        #    FullName = $FileStream.Name
        #    CreationTime = [datetime]::FromFileTime($fbi.CreationTime)
        #    LastAccessTime = [datetime]::FromFileTime($fbi.LastAccessTime)
        #    LastWriteTime = [datetime]::FromFileTime($fbi.LastWriteTime)
        #    ChangeTime = [datetime]::FromFileTime($fbi.ChangeTime)
        #}
        #$Object.PSTypeNames.Insert(0,'System.Io.FileTimeStamp')
        return [datetime]::FromFileTime($fbi.ChangeTime)
    } Else {
        return "$($Item): $(New-Object ComponentModel.Win32Exception)"
    }
    #region Perform Cleanup
    $FileStream.Close()
    # Deallocate memory
    If ($p_fbi -ne [intptr]::Zero) {
        [System.Runtime.InteropServices.Marshal]::FreeHGlobal($p_fbi)
    }

}


ForEach ($file in $files) {
    $creationTime = $file.CreationTime
    $lastAccessTime = $file.LastAccessTime
    $lastWriteTime = $file.LastWriteTime
    Try {
        If ($file.Attributes -notmatch 'Directory') {
            $changeTime = Get-ChangeTime -inFile $file.FullName
        }
        Else {
            # Arbitrary change time if one is not found by the function...
            $changeTime = (Get-Date).AddYears(-20)
        }
    }
    Catch {
        # Arbitrary change time if one is not found by the function...
        $changeTime = (Get-Date).AddYears(-20)
    }


    # Only output what matches the criteria above
    # If the MFT time of creation matches the timeframe it is output
    If ((($creationTime -ge $startDate) -and ($creationTime -le $endDate)) -or (($lastAccessTime -ge $startDate) -and ($lastAccessTime -le $endDate)) -or (($lastWriteTime -ge $startDate) -and ($lastWriteTime -le $endDate)) -or (($changeTime -ge $startDate) -and ($changeTime -le $endDate))) {
        Write-Host "-- File Information --"
        Write-Host "Name: " -NoNewLine  -ForegroundColor Green
        Write-Host "$($file.FullName)"
        Write-Host "Attributes: " -NoNewline -ForegroundColor Green
        Write-Host "$($file.Attributes)" 
        Write-Host "Length: " -NoNewline -ForegroundColor Green
        Write-Host "$($file.Length)"
        If ($file.Attributes -notmatch 'Directory') {
            $changeTime = Get-ChangeTime -inFile $file.FullName
            Write-Host "MFT Change Time: " -NoNewline -ForegroundColor Green
            Write-Host (Color-Text -inDate $changeTime)
        }
        Write-Host "Create: " -NoNewline -ForegroundColor Green
        Write-Host (Color-Text -inDate $creationTime) -NoNewline
        Write-Host "Last Access Time: " -NoNewline -ForegroundColor Green
        Write-Host (Color-Text -inDate $lastAccessTime) -NoNewline
        Write-Host "Last Write Time: " -NoNewline -ForegroundColor Green
        Write-Host (Color-Text -inDate $lastWriteTime)
        If ($file.Attributes -notmatch 'Directory') {
            $md5 = (Get-FileHash -LiteralPath $file.FullName -Algorithm MD5 -ErrorAction SilentlyContinue).Hash
            $sha1 = (Get-FileHash -LiteralPath $file.FullName -Algorithm SHA1 -ErrorAction SilentlyContinue).Hash
            $sha256 = (Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
            Write-Host "-- Hashes --"
            Write-Host "MD5: " -NoNewline -ForegroundColor Green
            Write-Host $md5 
            Write-Host "SHA1: " -NoNewline -ForegroundColor Green
            Write-Host $sha1
            Write-Host "SHA256: " -NoNewline -ForegroundColor Green
            Write-Host $sha256
        }
        Write-Host "`r`n`r`n"
    }
    
}


No comments:

Post a Comment

Test Authentication from Linux Console using python3 pexpect

Working with the IT420 lab, you will discover that we need to discover a vulnerable user account.  The following python3 script uses the pex...