Skip to content

Instantly share code, notes, and snippets.

@vikas891
Last active March 27, 2024 09:12
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save vikas891/841ac223e69913b49dc2aa9cc8663e34 to your computer and use it in GitHub Desktop.
Save vikas891/841ac223e69913b49dc2aa9cc8663e34 to your computer and use it in GitHub Desktop.
A PowerShell script to re-construct a suspicious .PS1 from script-blocks recorded in Event ID 4104
#Usage:
#
#NOTE: The script expects an argument which is the full File Path of the EVTX file.
#
#C:\>ExtractAllScripts.ps1
#The default behavior of the script is to assimilate and extract every script/command to disk.
#
#C:\ExtractAllScripts -List
#This will only list Script Block IDs with associated Script Names(if logged.)
#
#C:\>ExtractAllScripts.ps1 -ScriptBlockID aeb8cd23-3052-44f8-b6ba-ff3c083e912d
#This will only extract the script corresponding to the user specified ScriptBlock ID
#
#Twitter: @vikas891
$ErrorActionPreference= 'silentlycontinue'
$Question = read-host "Please provide full path of PowerShell Operational EVTX:"
param ($ScriptBlockID, [switch]$List)
$StoreArrayHere = Get-WinEvent -FilterHashtable @{ Path="$Question"; ProviderName="Microsoft-Windows-PowerShell"; Id = 4104 }
$Desc = $StoreArrayHere | sort -Descending { $_.Properties[1].Value }
$ArrayofUniqueIDs = @()
if(!$ScriptBlockID)
{
$Desc | %{ $ArrayofUniqueIDs += $_.Properties[3].Value }
}
else
{
$Desc | %{ $ArrayofUniqueIDs += $_.Properties[3].Value }
if($ScriptBlockID -in $ArrayofUniqueIDs)
{
$ArrayofUniqueIDs = $ScriptBlockID
}
else
{
""
Write-Host "[!] Specified Script Block ID does not exist. Exiting.." -ForegroundColor Red
break
}
}
$ArrayofUniqueIDs = $ArrayofUniqueIDs | select -Unique
if($List)
{
foreach ($a in $ArrayofUniqueIDs)
{
$Temp = $StoreArrayHere | Where-Object { $_.Message -like "*$a*" }
$SortIt = $Temp | sort { $_.Properties[0].Value }
""
if($SortIt[0].Properties[4].Value)
{
$OriginalName = Split-Path -Path $SortIt[0].Properties[4].Value -Leaf
$FileName = "$($a)_$($OriginalName)"
$DisplayName = $SortIt[0].Properties[4].Value
}
else
{
$OriginalName = ''
$FileName = $a
$DisplayName = 'NULL'
}
Write-Host -NoNewline "Script ID: "
Write-Host -NoNewline $a -ForegroundColor Yellow
Write-Host -NoNewline " | " -ForegroundColor White
Write-Host -NoNewline "Script Name:"
Write-Host -NoNewline $DisplayName -ForegroundColor Magenta
$NumberOfRecords = $Temp.Count
$MessageTotal = $Temp[0] | % {$_.Properties[1].Value}
if($NumberOfRecords -eq $MessageTotal)
{
Write-Host -NoNewline " | Complete Script " -ForegroundColor Green
Write-Host -NoNewline " | Event Records Logged"$NumberOfRecords/$MessageTotal
""
}
else
{
Write-Host -NoNewline " | InComplete Script Logged" -ForegroundColor Red
Write-Host -NoNewline " | Event Records Logged"$NumberOfRecords/$MessageTotal
""
}
}
break
}
foreach ($a in $ArrayofUniqueIDs)
{
$Temp = $StoreArrayHere | Where-Object { $_.Message -like "*$a*" }
$SortIt = $Temp | sort { $_.Properties[0].Value }
""
if($SortIt[0].Properties[4].Value)
{
$OriginalName = Split-Path -Path $SortIt[0].Properties[4].Value -Leaf
$FileName = "$($a)_$($OriginalName)"
$DisplayName = $SortIt[0].Properties[4].Value
}
else
{
$OriginalName = ''
$FileName = $a
$DisplayName = 'NULL'
}
Write-Host -NoNewline "Extracting "
Write-Host -NoNewline $a -ForegroundColor Yellow
if ($OriginalName)
{
Write-Host -NoNewline _$OriginalName -ForegroundColor Magenta
}
Write-Host -NoNewline " | " -ForegroundColor White
Write-Host -NoNewline "ScriptName:"
Write-Host -NoNewline $DisplayName -ForegroundColor Magenta
$MergedScript = -join ($SortIt | % { $_.Properties[2].Value }) | Out-File $FileName
$NumberOfRecords = $Temp.Count
$MessageTotal = $Temp[0] | % {$_.Properties[1].Value}
if($NumberOfRecords -eq $MessageTotal)
{
Write-Host -NoNewline " | Complete Script Logged " -ForegroundColor Green
Write-Host -NoNewline " | Event Records Exported"$NumberOfRecords/$MessageTotal
Write-Host -NoNewline " | Number of lines" (Get-Content $FileName).Length
""
}
else
{
Write-Host -NoNewline " | InComplete Script Logged" -ForegroundColor Red
ren $FileName "$FileName.partial"
Write-Host -NoNewline " | Event Records Exported"$NumberOfRecords/$MessageTotal
Write-Host -NoNewline " | Number of lines" (Get-Content "$FileName.partial").Length
""
}
$FileName = ''
}
@mkayoh
Copy link

mkayoh commented Feb 13, 2023

You might also consider fixing those left and right double quotation marks so it's "Microsoft-Windows-Powershell" :) Kudos for cool script!

@vikas891
Copy link
Author

Thank you for the kind words @mkayoh! - didn't realize :) fixed now.

@ramy1234
Copy link

can you calculate the script content hash then check it on Virustotal and mark the malicious ones?

@vikas891
Copy link
Author

@ramy1234 I thought of pursuing that avenue but given VT's rate limiting (number of lookups per minute) and the need of an API key, I didn't go down that route!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment