Skip to content

Instantly share code, notes, and snippets.

@PolarBearGod
Created June 15, 2021 21:38
Show Gist options
  • Save PolarBearGod/7721001e6ca0ed43c423287088a61819 to your computer and use it in GitHub Desktop.
Save PolarBearGod/7721001e6ca0ed43c423287088a61819 to your computer and use it in GitHub Desktop.
Slimmed Down Version of the Deep Blue CLI Tool
param ([string]$file = $env:file, [string]$log = $env:log)
function Main {
$text = ""
$minlength = 1000
$regexes = Get-Content ".\regexes.txt" | Select-String '^[^#]' | ConvertFrom-Csv
$whitelist = Get-Content ".\whitelist.txt" | Select-String '^[^#]' | ConvertFrom-Csv
$logname = Check-Options $file $log
$filter = Create-Filter $file $logname
$maxfailedlogons = 5
$failedlogons = @{}
$totalfailedlogons = 0
$totalfailedaccounts = 0
$totalsensprivuse = 0
$maxtotalsensprivuse = 4
$totaladminlogons = 0
$maxadminlogons = 10
$adminlogons = @{}
$multipleadminlogons = @{}
$alert_all_admin = 0
$minpercent = .65
$maxbinary = .50
$passspraytrack = @{}
$passsprayuniqusermax = 6
$passsprayloginmax = 6
$checkunsigned = 0
try {
$events = Invoke-Expression "Get-WinEvent $filter -ErrorAction Stop"
}
catch {
Write-Host "Get-WinEvent $filter -ErrorAction Stop"
Write-Host "Get-WinEvent error: " $_.Exception.Message "`n"
Write-Host "Exiting...`n"
exit
}
ForEach ($event in $events) {
# Custom reporting object:
$obj = [PSCustomObject]@{
Date = $event.TimeCreated
Log = $logname
EventID = $event.id
Message = $event.message
Results = ""
Command = ""
Decoded = ""
}
$eventXML = [xml]$event.ToXml()
$servicecmd = 0 # CLIs via service creation get extra checks, this defaults to 0 (no extra checks)
if ($logname -eq "Security") {
if ($event.id -eq 4688) {
# A new process has been created. (Command Line Logging)
$commandline = $eventXML.Event.EventData.Data[8]."#text" # Process Command Line
$creator = $eventXML.Event.EventData.Data[13]."#text" # Creator Process Name
if ($commandline) {
Check-Command -EventID 4688
}
}
ElseIf ($event.id -eq 4672) {
# Special privileges assigned to new logon (possible admin access)
$username = $eventXML.Event.EventData.Data[1]."#text"
$domain = $eventXML.Event.EventData.Data[2]."#text"
$securityid = $eventXML.Event.EventData.Data[3]."#text"
$privileges = $eventXML.Event.EventData.Data[4]."#text"
if ($privileges -Match "SeDebugPrivilege") {
#Admin account with SeDebugPrivilege
if ($alert_all_admin) {
# Alert for every admin logon
$obj.Message = "Logon with SeDebugPrivilege (admin access)"
$obj.Results = "Username: $username`n"
$obj.Results += "Domain: $domain`n"
$obj.Results += "User SID: $securityid`n"
$obj.Results += "Privileges: $privileges"
Write-Output $obj
}
# Track User SIDs used during admin logons (can track one account logging into multiple systems)
$totaladminlogons += 1
if ($adminlogons.ContainsKey($username)) {
$string = $adminlogons.$username
if (-Not ($string -Match $securityid)) {
# One username with multiple admin logon SIDs
$multipleadminlogons.Set_Item($username, 1)
$string += " $securityid"
$adminlogons.Set_Item($username, $string)
}
}
Else {
$adminlogons.add($username, $securityid)
}
}
}
ElseIf ($event.id -eq 4720) {
# A user account was created.
$username = $eventXML.Event.EventData.Data[0]."#text"
$securityid = $eventXML.Event.EventData.Data[2]."#text"
$obj.Message = "New User Created"
$obj.Results = "Username: $username`n"
$obj.Results += "User SID: $securityid`n"
Write-Output $obj
}
ElseIf (($event.id -eq 4728) -or ($event.id -eq 4732) -or ($event.id -eq 4756)) {
# A member was added to a security-enabled (global|local|universal) group.
$groupname = $eventXML.Event.EventData.Data[2]."#text"
# Check if group is Administrators, may later expand to all groups
if ($groupname -eq "Administrators") {
$username = $eventXML.Event.EventData.Data[0]."#text"
$securityid = $eventXML.Event.EventData.Data[1]."#text"
switch ($event.id) {
4728 { $obj.Message = "User added to global $groupname group" }
4732 { $obj.Message = "User added to local $groupname group" }
4756 { $obj.Message = "User added to universal $groupname group" }
}
$obj.Results = "Username: $username`n"
$obj.Results += "User SID: $securityid`n"
Write-Output $obj
}
}
ElseIf ($event.id -eq 4625) {
# An account failed to log on.
# Requires auditing logon failures
# https://technet.microsoft.com/en-us/library/cc976395.aspx
$totalfailedlogons += 1
$username = $eventXML.Event.EventData.Data[5]."#text"
if ($failedlogons.ContainsKey($username)) {
$count = $failedlogons.Get_Item($username)
$failedlogons.Set_Item($username, $count + 1)
}
Else {
$failedlogons.Set_Item($username, 1)
$totalfailedaccounts += 1
}
}
ElseIf ($event.id -eq 4673) {
# Sensitive Privilege Use (Mimikatz)
$totalsensprivuse += 1
# use -eq here to avoid multiple log notices
if ($totalsensprivuse -eq $maxtotalsensprivuse) {
$obj.Message = "Sensititive Privilege Use Exceeds Threshold"
$obj.Results = "Potentially indicative of Mimikatz, multiple sensitive privilege calls have been made.`n"
$username = $eventXML.Event.EventData.Data[1]."#text"
$domainname = $eventXML.Event.EventData.Data[2]."#text"
$obj.Results += "Username: $username`n"
$obj.Results += "Domain Name: $domainname`n"
Write-Output $obj
}
}
ElseIf ($event.id -eq 4674) {
if ($event.Message) {
$text = $array[0]
$application = Remove-Spaces($array[3])
$user = Remove-Spaces(($array[4] -split ':')[1])
$service = Remove-Spaces(($array[11] -split ':')[1])
$application = Remove-Spaces(($array[16] -split ': ')[1])
$accessreq = Remove-Spaces(($array[19] -split ':')[1])
if ($application.ToUpper() -Eq "C:\WINDOWS\SYSTEM32\SERVICES.EXE" `
-And $accessreq.ToUpper() -Match "WRITE_DAC") {
$obj.Message = "Possible Hidden Service Attempt"
$obj.Command = ""
$obj.Results = "User requested to modify the Dynamic Access Control (DAC) permissions of a service, possibly to hide it from view.`n"
$obj.Results += "User: $user`n"
$obj.Results += "Target service: $service`n"
$obj.Results += "Desired Access: $accessreq`n"
Write-Output $obj
}
}
}
ElseIf ($event.id -eq 4648) {
$username = $eventXML.Event.EventData.Data[1]."#text"
$hostname = $eventXML.Event.EventData.Data[2]."#text"
$targetusername = $eventXML.Event.EventData.Data[5]."#text"
$sourceip = $eventXML.Event.EventData.Data[12]."#text"
if ($passspraytrack[$targetusername] -eq $null) {
$passspraytrack[$targetusername] = 1
}
else {
$passspraytrack[$targetusername] += 1
}
if ($passspraytrack[$targetusername] -gt $passsprayloginmax) {
$passsprayuniquser = 0
foreach ($key in $passspraytrack.keys) {
if ($passspraytrack[$key] -gt $passsprayloginmax) {
$passsprayuniquser += 1
}
}
if ($passsprayuniquser -gt $passsprayuniqusermax) {
$usernames = ""
foreach ($key in $passspraytrack.keys) {
$usernames += $key
$usernames += " "
}
$obj.Message = "Distributed Account Explicit Credential Use (Password Spray Attack)"
$obj.Results = "The use of multiple user account access attempts with explicit credentials is "
$obj.Results += "an indicator of a password spray attack.`n"
$obj.Results += "Target Usernames: $usernames`n"
$obj.Results += "Accessing Username: $username`n"
$obj.Results += "Accessing Host Name: $hostname`n"
Write-Output $obj
$passspraytrack = @{} # Reset
}
}
}
ElseIf ($event.id -eq 1102) {
# The Audit log file was cleared.
if ($event.Message) {
$array = $event.message -split '\n' # Split each line of the message into an array
$user = Remove-Spaces($array[3])
}
$obj.Message = "Audit Log Clear"
$obj.Results = "The Audit log was cleared.`n"
$obj.Results += $user
Write-Output $obj
}
}
ElseIf ($logname -eq "System") {
if ($event.id -eq 7045) {
# A service was installed in the system.
$servicename = $eventXML.Event.EventData.Data[0]."#text"
$commandline = $eventXML.Event.EventData.Data[1]."#text"
# Check for suspicious service name
$text = (Check-Regex $servicename 1)
if ($text) {
$obj.Message = "New Service Created"
$obj.Command = $commandline
$obj.Results = "Service name: $servicename`n"
$obj.Results += $text
Write-Output $obj
}
# Check for suspicious cmd
if ($commandline) {
$servicecmd = 1 # CLIs via service creation get extra checks
Check-Command -EventID 7045
}
}
ElseIf ($event.id -eq 7030) {
# The ... service is marked as an interactive service. However, the system is configured
# to not allow interactive services. This service may not function properly.
$servicename = $eventXML.Event.EventData.Data."#text"
$obj.Message = "Interactive service warning"
$obj.Results = "Service name: $servicename`n"
$obj.Results += "Malware (and some third party software) trigger this warning"
# Check for suspicious service name
$servicecmd = 1 # CLIs via service creation get extra check
$obj.Results += (Check-Regex $servicename 1)
Write-Output $obj
}
ElseIf ($event.id -eq 7036) {
# The ... service entered the stopped|running state.
$servicename = $eventXML.Event.EventData.Data[0]."#text"
$text = (Check-Regex $servicename 1)
if ($text) {
$obj.Message = "Suspicious Service Name"
$obj.Results = "Service name: $servicename`n"
$obj.Results += $text
Write-Output $obj
}
}
ElseIf ($event.id -eq 7040) {
# The start type of the Windows Event Log service was changed from auto start to disabled.
$servicename = $eventXML.Event.EventData.Data[0]."#text"
$action = $eventXML.Event.EventData.Data[1]."#text"
if ($servicename -ccontains "Windows Event Log") {
$obj.Results = "Service name: $servicename`n"
$obj.Results += $text
if ($action -eq "disabled") {
$obj.Message = "Event Log Service Stopped"
$obj.Results += "Selective event log manipulation may follow this event."
}
elseIf ($action -eq "auto start") {
$obj.Message = "Event Log Service Started"
$obj.Results += "Selective event log manipulation may precede this event."
}
Write-Output $obj
}
}
ElseIf ($event.id -eq 104) {
# The System log file was cleared.
$obj.Message = "System Log Clear"
$obj.Results = $event.message
Write-Output $obj
}
}
ElseIf ($logname -eq "Application") {
if (($event.id -eq 2) -and ($event.Providername -eq "EMET")) {
# EMET Block
$obj.Message = "EMET Block"
if ($event.Message) {
$array = $event.message -split '\n' # Split each line of the message into an array
$text = $array[0]
$application = Remove-Spaces($array[3])
$command = $application -Replace "^Application: ", ""
$username = Remove-Spaces($array[4])
$obj.Message = "EMET Block"
$obj.Command = "$command"
$obj.Results = "$text`n"
$obj.Results += "$username`n"
}
Else {
$obj.Message = "Warning: EMET Message field is blank. Install EMET locally to see full details of this alert"
}
Write-Output $obj
}
}
ElseIf ($logname -eq "Applocker") {
if ($event.id -eq 8003) {
$obj.Message = "Applocker Warning"
$command = $event.message -Replace " was .*$", ""
$obj.Command = $command
$obj.Results = $event.message
Write-Output $obj
}
ElseIf ($event.id -eq 8004) {
$obj.Message = "Applocker Block"
$command = $event.message -Replace " was .*$", ""
$obj.Command = $command
$obj.Results = $event.message
Write-Output $obj
}
}
ElseIf ($logname -eq "PowerShell") {
if ($event.id -eq 4103) {
$commandline = $eventXML.Event.EventData.Data[2]."#text"
if ($commandline -Match "Host Application") {
$commandline = $commandline -Replace "(?ms)^.*Host.Application = ", ""
# Remove every line after the "Host Application = " line.
$commandline = $commandline -Replace "(?ms)`n.*$", ""
if ($commandline) {
Check-Command -EventID 4103
}
}
}
ElseIf ($event.id -eq 4104) {
if (-not ($eventxml.Event.EventData.Data[4]."#text")) {
$commandline = $eventXML.Event.EventData.Data[2]."#text"
if ($commandline) {
Check-Command -EventID 4104
}
}
}
}
ElseIf ($logname -eq "Sysmon") {
# Check command lines
if ($event.id -eq 1) {
$creator = $eventXML.Event.EventData.Data[14]."#text"
$commandline = $eventXML.Event.EventData.Data[4]."#text"
if ($commandline) {
Check-Command -EventID 1
}
}
ElseIf ($event.id -eq 7) {
if ($checkunsigned) {
if ($eventXML.Event.EventData.Data[6]."#text" -eq "false") {
$obj.Message = "Unsigned Image (DLL)"
$image = $eventXML.Event.EventData.Data[3]."#text"
$imageload = $eventXML.Event.EventData.Data[4]."#text"
# $hash=$eventXML.Event.EventData.Data[5]."#text"
$obj.Command = $imageload
$obj.Results = "Loaded by: $image"
Write-Output $obj
}
}
}
}
}
foreach ($username in $adminlogons.Keys) {
$securityid = $adminlogons.Get_Item($username)
if ($multipleadminlogons.$username) {
$obj.Message = "Multiple admin logons for one account"
$obj.Results = "Username: $username`n"
$obj.Results += "User SID Access Count: " + $securityid.split().Count
$obj.EventId = 4672
Write-Output $obj
}
}
foreach ($username in $failedlogons.Keys) {
$count = $failedlogons.Get_Item($username)
if ($count -gt $maxfailedlogons) {
$obj.Message = "High number of logon failures for one account"
$obj.Results = "Username: $username`n"
$obj.Results += "Total logon failures: $count"
$obj.EventId = 4625
Write-Output $obj
}
}
# Password spraying:
if (($totalfailedlogons -gt $maxfailedlogons) -and ($totalfailedaccounts -gt 1)) {
$obj.Message = "High number of total logon failures for multiple accounts"
$obj.Results = "Total accounts: $totalfailedaccounts`n"
$obj.Results += "Total logon failures: $totalfailedlogons`n"
$obj.EventId = 4625
Write-Output $obj
}
}
function Check-Options($file, $log) {
$log_error = "Unknown and/or unsupported log type"
$logname = ""
# Checks the command line options, return logname to parse
if ($file -eq "") {
# No filename provided, parse local logs
if (($log -eq "") -or ($log -eq "Security")) {
# Parse the security log if no log was selected
$logname = "Security"
}
ElseIf ($log -eq "System") {
$logname = "System"
}
ElseIf ($log -eq "Application") {
$logname = "Application"
}
ElseIf ($log -eq "Sysmon") {
$logname = "Sysmon"
}
ElseIf ($log -eq "Powershell") {
$logname = "Powershell"
}
Else {
write-host $log_error
exit 1
}
}
else {
if (Test-Path $file) {
try {
$event = Get-WinEvent -path $file -max 1 -ErrorAction Stop
}
catch {
Write-Host "Get-WinEvent error: " $_.Exception.Message "`n"
Write-Host "Exiting...`n"
exit
}
switch ($event.LogName) {
"Security" { $logname = "Security" }
"System" { $logname = "System" }
"Application" { $logname = "Application" }
"Microsoft-Windows-AppLocker/EXE and DLL" { $logname = "Applocker" }
"Microsoft-Windows-PowerShell/Operational" { $logname = "Powershell" }
"Microsoft-Windows-Sysmon/Operational" { $logname = "Sysmon" }
default { "Logic error 3, should not reach here..."; Exit 1 }
}
}
else {
# Filename does not exist, exit
Write-host "Error: no such file. Exiting..."
exit 1
}
}
return $logname
}
function Create-Filter($file, $logname) {
# Return the Get-Winevent filter
#
$sys_events = "7030,7036,7045,7040,104"
$sec_events = "4688,4672,4720,4728,4732,4756,4625,4673,4674,4648,1102"
$app_events = "2"
$applocker_events = "8003,8004,8006,8007"
$powershell_events = "4103,4104"
$sysmon_events = "1,7"
if ($file -ne "") {
switch ($logname) {
"Security" { $filter = "@{path=""$file"";ID=$sec_events}" }
"System" { $filter = "@{path=""$file"";ID=$sys_events}" }
"Application" { $filter = "@{path=""$file"";ID=$app_events}" }
"Applocker" { $filter = "@{path=""$file"";ID=$applocker_events}" }
"Powershell" { $filter = "@{path=""$file"";ID=$powershell_events}" }
"Sysmon" { $filter = "@{path=""$file"";ID=$sysmon_events}" }
default { "Logic error 1, should not reach here..."; Exit 1 }
}
}
else {
switch ($logname) {
"Security" { $filter = "@{Logname=""Security"";ID=$sec_events}" }
"System" { $filter = "@{Logname=""System"";ID=$sys_events}" }
"Application" { $filter = "@{Logname=""Application"";ID=$app_events}" }
"Applocker" { $filter = "@{logname=""Microsoft-Windows-AppLocker/EXE and DLL"";ID=$applocker_events}" }
"Powershell" { $filter = "@{logname=""Microsoft-Windows-PowerShell/Operational"";ID=$powershell_events}" }
"Sysmon" { $filter = "@{logname=""Microsoft-Windows-Sysmon/Operational"";ID=$sysmon_events}" }
default { "Logic error 2, should not reach here..."; Exit 1 }
}
}
return $filter
}
function Check-Command() {
Param($EventID)
$text = ""
$base64 = ""
# Check to see if command is whitelisted
foreach ($entry in $whitelist) {
if ($commandline -Match $entry.regex) {
# Command is whitelisted, return nothing
return
}
}
if ($commandline.length -gt $minlength) {
$text += "Long Command Line: greater than $minlength bytes`n"
}
$text += (Check-Obfu $commandline)
$text += (Check-Regex $commandline 0)
$text += (Check-Creator $commandline $creator)
if ($commandline -Match "\-enc.*[A-Za-z0-9/+=]{100}") {
$base64 = $commandline -Replace "^.* \-Enc(odedCommand)? ", ""
}
ElseIf ($commandline -Match ":FromBase64String\(") {
$base64 = $commandline -Replace "^.*:FromBase64String\(\'*", ""
$base64 = $base64 -Replace "\'.*$", ""
}
if ($base64) {
if ($commandline -Match "Compression.GzipStream.*Decompress") {
# Metasploit-style compressed and base64-encoded function. Uncompress it.
$decoded = New-Object IO.MemoryStream(, [Convert]::FromBase64String($base64))
$uncompressed = (New-Object IO.StreamReader(((New-Object IO.Compression.GzipStream($decoded, [IO.Compression.CompressionMode]::Decompress))), [Text.Encoding]::ASCII)).ReadToEnd()
$obj.Decoded = $uncompressed
$text += "Base64-encoded and compressed function`n"
}
else {
$decoded = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($base64))
$obj.Decoded = $decoded
$text += "Base64-encoded function`n"
$text += (Check-Obfu $decoded)
$text += (Check-Regex $decoded 0)
}
}
if ($text) {
if ($servicecmd) {
$obj.Message = "Suspicious Service Command"
$obj.Results = "Service name: $servicename`n"
}
Else {
$obj.Message = "Suspicious Command Line"
}
$obj.Command = $commandline
$obj.Results += $text
$obj.EventID = $EventID
Write-Output $obj
}
return
}
function Check-Regex($string, $type) {
$regextext = "" # Local variable for return output
foreach ($regex in $regexes) {
if ($regex.Type -eq $type) {
# Type is 0 for Commands, 1 for services. Set in regexes.csv
if ($string -Match $regex.regex) {
$regextext += $regex.String + "`n"
}
}
}
return $regextext
}
function Check-Obfu($string) {
$obfutext = "" # Local variable for return output
$lowercasestring = $string.ToLower()
$length = $lowercasestring.length
$noalphastring = $lowercasestring -replace "[a-z0-9/\;:|.]"
$nobinarystring = $lowercasestring -replace "[01]" # To catch binary encoding
# Calculate the percent alphanumeric/common symbols
if ($length -gt 0) {
$percent = (($length - $noalphastring.length) / $length)
# Adjust minpercent for very short commands, to avoid triggering short warnings
if (($length / 100) -lt $minpercent) {
$minpercent = ($length / 100)
}
if ($percent -lt $minpercent) {
$percent = "{0:P0}" -f $percent # Convert to a percent
$obfutext += "Possible command obfuscation: only $percent alphanumeric and common symbols`n"
}
# Calculate the percent of binary characters
$percent = (($nobinarystring.length - $length / $length) / $length)
$binarypercent = 1 - $percent
if ($binarypercent -gt $maxbinary) {
#$binarypercent = 1-$percent
$binarypercent = "{0:P0}" -f $binarypercent # Convert to a percent
$obfutext += "Possible command obfuscation: $binarypercent zeroes and ones (possible numeric or binary encoding)`n"
}
}
return $obfutext
}
function Check-Creator($command, $creator) {
$creatortext = ""
if ($creator) {
if ($command -Match "powershell") {
if ($creator -Match "PSEXESVC") {
$creatortext += "PowerShell launched via PsExec: $creator`n"
}
ElseIf ($creator -Match "WmiPrvSE") {
$creatortext += "PowerShell launched via WMI: $creator`n"
}
}
}
return $creatortext
}
function Remove-Spaces($string) {
$string = $string.trim() -Replace "\s+:", ":"
return $string
}
. Main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment