Deploy Sysmon with PowerShell Desired State Configuration

The technet page of sysmon 2.0 provided the following advanced configuration sample:

@'
<Sysmon schemaversion="1.0">
    <Configuration>
        <!-- Capture all hashes -->
        <Hashing>*</Hashing>
        <!-- Enable network logging -->
        <Network />
    </Configuration>
    <Rules>
        <!-- Log all drivers except if the signature -->
        <!-- contains Microsoft or Windows -->
        <DriverLoad default="include">
        <Signature condition="contains">microsoft</Signature>
        <Signature condition="contains">windows</Signature>
        </DriverLoad>
        <!-- Do not log process termination -->
        <ProcessTerminate />
        <!-- Log network connection if the destination port equal 443 -->
        <NetworkConnect>
        <DestinationPort>443</DestinationPort>
        </NetworkConnect>
    </Rules>
</Sysmon>
'@


If you want to do more advanced stuff, my fellow Windows PowerSell MVP Carlos Perez wrote an awesome module named Posh-Sysmon that would help you create XML configuration for Sysmon.

April 20, 2015, Sysmon 3.0 was published πŸ™‚
It delivers a version 2.0 of the XML schema which looks like this:

@'
<Sysmon schemaversion="2.0">
 <!-- Capture all hashes -->
 <HashAlgorithms>*</HashAlgorithms>
 <EventFiltering>
  <!-- Log all drivers except if the signature -->
  <!-- contains Microsoft or Windows -->
  <DriverLoad onmatch="include">
   <Signature condition="contains">microsoft</Signature>
   <Signature condition="contains">windows</Signature>
  </DriverLoad>
  <!-- Do not log process termination -->
  <ProcessTerminate onmatch="include"/>
  <!-- Log network connection if the destination port equal 443 -->
  <NetworkConnect onmatch="include">
   <DestinationPort>443</DestinationPort>
  </NetworkConnect>
 </EventFiltering>
</Sysmon>
'@

sysmon.v3.sample.config

In March 2015, I’ve asked the following question to Thomas Garnier who co-authored this awesome sysmon tool with Mark Russinovich.

Unfortunately, there’s no way currently in version 2.0 or 3.0 to dump the configuration directly to XML 😐
But, that’s not a huge problem for PowerShell πŸ˜‰
The only trick that made the reverse engineering experience consistent was to read the dumped rules from the bottom up 😎

Let’s see a capture of the DSC configuration that deploys sysmon when it runs for the first time:
deploy-sysmon.1
If I run the same configuration a 2nd time, all the TEST phases performed are skipped (which is a good thing):
deploy-sysmon.2

Quick and dirty!
But Sysmon got deployed and configured through Desired State Configuration on a Windows Server 2012 R2 and its built-in PowerShell version 4.0

#Requires -Version 4.0
#Requires -RunAsAdministrator
Configuration SysmonDSC {
param
(
[string[]]$NodeName = 'localhost'
)
Node $NodeName
{
Script DownloadSysmon {
GetScript = {
@{
GetScript = $GetScript
SetScript = $SetScript
TestScript = $TestScript
Result = $(Test-Path (Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath sysmon.exe));
}
}
SetScript = {
try {
# https://msdn.microsoft.com/en-us/library/system.io.path.gettempfilename%28v=vs.110%29.aspx
$tmpfile = [System.IO.Path]::GetTempFileName()
$null = Invoke-WebRequest -Uri 'https://live.sysinternals.com/Sysmon.exe' `
-OutFile $tmpfile -ErrorAction Stop
Write-Verbose -Message 'Sucessfully downloaded Sysmon.exe'
Unblock-File -Path $tmpfile -ErrorAction Stop
$exefile = Join-Path -Path (Split-Path -Path $tmpfile -Parent) -ChildPath 'a.exe'
if (Test-Path $exefile) {
Remove-Item -Path $exefile -Force -ErrorAction Stop
}
$tmpfile | Rename-Item -NewName 'a.exe' -Force -ErrorAction Stop
} catch {
Write-Verbose -Message "Something went wrong $($_.Exception.Message)"
}
}
TestScript = {
$s = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath a.exe -ErrorAction SilentlyContinue
if (-not(Test-Path -Path $s -PathType Leaf)) {
Write-Verbose -Message "Cannot find sysmon.exe in temp"
return $false
}
if(
(Get-FileHash -Path $s -Algorithm SHA256).Hash -eq 'E6BA49275B3EC33232D91741CAEF1B99A58460EEB4BC44F26086FE076FAD333A' -and
(Get-AuthenticodeSignature -FilePath $s).Status.value__ -eq 0 # Valid
) {
Write-Verbose -Message 'Successfully found a valid signed sysmon.exe'
return $true
} else {
Write-Verbose -Message 'A valid signed sysmon.exe was not found'
return $false
}
}
}
Registry SysmonEULA {
Key = 'HKEY_USERS\S-1-5-18\Software\Sysinternals\System Monitor'
ValueName = 'EulaAccepted';
ValueType = 'DWORD'
ValueData = '1'
Ensure = 'Present'
Force = $true;
}
Script InstallSysmon {
GetScript = {
@{
GetScript = $GetScript
SetScript = $SetScript
TestScript = $TestScript
Result = $(if (@(Get-Service -Name sysmon,sysmondrv).Count -eq 2) { $true } else { $false });
}
}
SetScript = {
$sysmonbin = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath a.exe
$s = Copy-Item -Path $sysmonbin -Destination "$($env:systemroot)\system32\sysmon.exe" -PassThru -Force
try {
$null = Start-Process -FilePath $s -ArgumentList @('-i','-accepteula') -PassThru -NoNewWindow -ErrorAction Stop | Wait-Process
Write-Verbose -Message 'Successfully installed sysmon'
} catch {
throw $_
}
}
TestScript = {
if(
Get-WinEvent -ListLog * | Where LogName -eq 'Microsoft-Windows-Sysmon/Operational'
) {
Write-Verbose -Message "Sysmon is installed"
return $true
} else {
Write-Verbose -Message "Sysmon isn't installed"
return $false
}
}
DependsOn = '[Script]DownloadSysmon','[Registry]SysmonEULA'
}
Script ConfigureSysmon {
GetScript = {
@{
GetScript = $GetScript
SetScript = $SetScript
TestScript = $TestScript
Result = $();
}
}
SetScript = {
$s = "$($env:systemroot)\system32\sysmon.exe"
$null = Start-Process -FilePath $s -ArgumentList @('-c','--') -PassThru -NoNewWindow | Wait-Process
$null = Start-Process -FilePath $s -ArgumentList @('-c','C:\windows\temp\polSysmon.xml') -PassThru -NoNewWindow | Wait-Process
}
TestScript = {
Function Convert-SysmonConfigToXMLBlob {
[CmdletBinding()]
Param()
try {
$t = [system.io.path]::GetTempFileName()
$null = Start-Process -FilePath "$($env:systemroot)\system32\sysmon.exe" -ArgumentList @('-c') `
-NoNewWindow -PassThru -RedirectStandardOutput $t -ErrorAction Stop -Wait
} catch {
Write-Warning "Dumping sysmon config went wrong!"
break
}
if($config = Get-Content $t) {
'<Sysmon schemaversion="2.0">'
$Hashing = (([regex]'\s-\sHashingAlgorithms:\s+(?<Hash>.*)').Match(@($config)[3]) | Select -expand Groups)[-1].Value
' <HashAlgorithms>{0}</HashAlgorithms>' -f $Hashing
if (@($config)[7] -match '^Rule\sconfiguration\s\(version\s\d{1}\.\d{1,2}\):$') {
' <EventFiltering>'
$prop = @()
(($config)[-1..-(($config).Length-8)]) | ForEach-Object {
if ($_ -notmatch '\s-\s.*') {
$prop += $_
} else {
$node,$attribute = ([regex]'\s-\s(?<NodeName>\w+)\s+onm(n)?atch:\s(?<attribute>.*clude)').Matches($_).Groups |
Select -Last 2| Select -expand Value
if ($prop) {
' <{0} onmatch="{1}">' -f $node,$attribute
$prop | ForEach-Object {
$ChildNode,$filter,$value = ([regex]"\s+(?<ChildNode>\w+)\s+filter:\s(?<filter>\w+)\s+value:\s'(?<Value>.*)'").Matches($_).Groups |
Select -Last 3 | Select -Expand Value
' <{0} condition="{1}">{2}</{0}>' -f $ChildNode,$filter,$value
}
' </{0}>' -f $node
} else {
' <{0} onmatch="{1}"/>' -f $node,$attribute
}
$prop = @()
}
}
' </EventFiltering>'
}
'</Sysmon>'
} else {
Write-Warning "Cannot find output in $t"
}
} #endof function
if(
Compare-Object -ReferenceObject ([xml](Convert-SysmonConfigToXMLBlob)).InnerXML `
-DifferenceObject ([xml](Get-Content -Path C:\windows\temp\invpolSysmon.xml -Encoding UTF8 )).InnerXml
) {
Write-Verbose -Message "Sysmon needs to be configured"
return $false
} else {
Write-Verbose -Message "Sysmon is already configured"
return $true
}
}
DependsOn = '[Script]InstallSysmon','[Registry]SysmonEULA','[File]SysmonXMLPol','[File]InvSysmonXMLPol'
}
File SysmonXMLPol {
DestinationPath = 'C:\windows\temp\polSysmon.xml'
Ensure = 'Present';
Force = $true
Contents = @'
<Sysmon schemaversion="2.0">
<HashAlgorithms>SHA1,SHA256,IMPHASH</HashAlgorithms>
<EventFiltering>
<DriverLoad onmatch="include">
<Signature condition="contains">microsoft</Signature>
<Signature condition="contains">windows</Signature>
</DriverLoad>
<ProcessTerminate onmatch="include"/>
<NetworkConnect onmatch="include">
<DestinationPort>443</DestinationPort>
</NetworkConnect>
</EventFiltering>
</Sysmon>
'@
}
File InvSysmonXMLPol {
DestinationPath = 'C:\windows\temp\invpolSysmon.xml'
Ensure = 'Present';
Force = $true
Contents = @'
<Sysmon schemaversion="2.0">
<HashAlgorithms>SHA1,SHA256,IMPHASH</HashAlgorithms>
<EventFiltering>
<NetworkConnect onmatch="include">
<DestinationPort condition="is">443</DestinationPort>
</NetworkConnect>
<ProcessTerminate onmatch="include"/>
<DriverLoad onmatch="include">
<Signature condition="contains">windows</Signature>
<Signature condition="contains">microsoft</Signature>
</DriverLoad>
</EventFiltering>
</Sysmon>
'@
}
}
}
if (-not(test-path -Path C:\DSC -PathType Container)){
mkdir C:\DSC
}
# Compile the configuration file to a MOF format
SysmonDSC -OutputPath C:\DSC
# Run the configuration on localhost
Start-DscConfiguration -Path C:\DSC -ComputerName localhost -Verbose -Force -Wait
view raw Deploy-Sysmon.ps1 hosted with ❤ by GitHub

PS: If you check the different revisions of the gist file, you can get a DSC configuration that works against sysmon version 2.0 πŸ˜€

1 thought on “Deploy Sysmon with PowerShell Desired State Configuration

  1. Pingback: Dew Drop – April 22, 2015 (#1998) | Morning Dew

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.