-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathInvoke-SessionExec.ps1
254 lines (204 loc) · 9.88 KB
/
Invoke-SessionExec.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
function Invoke-SessionExec {
<#
.SYNOPSIS
Executes a specified command within a a user logon session.
.DESCRIPTION
The Invoke-SessionExec function allows you to run commands within a specific user session by utilizing Windows API calls.
This function requires SYSTEM privileges and is capable of targeting all sessions or a specific session identified by Session ID.
It sets up necessary security attributes, pipes, and handles to facilitate command execution and captures the output from the session.
.PARAMETER SessionID
Specifies the session ID where the command should be executed. If set to "All", the command will be executed in all active sessions.
.PARAMETER Command
The command to execute within the specified session.
.EXAMPLE
Invoke-SessionExec -SessionID 1 -Command "whoami /all"
Executes the "whoami /all" command in the session with ID 1.
.EXAMPLE
Invoke-SessionExec -SessionID "All" -Command "whoami /all"
Executes the "whoami /all" command in all active sessions, excluding the current session.
.NOTES
Requires SYSTEM privileges to execute successfully.
#>
param (
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$SessionID,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Command
)
if (-not ([System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value -eq "S-1-5-18")) { Write-Output "[-] Not running as SYSTEM" ; return }
if (-not [System.Management.Automation.PSTypeName]'NativeMethods'.Type) {
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern bool WTSQueryUserToken(int sessionId, out IntPtr Token);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);
}
"@
}
function ReadStream {
param (
[IntPtr]$handle
)
$result = ""
$buffer = New-Object byte[] 4096
$safeHandle = New-Object Microsoft.Win32.SafeHandles.SafeFileHandle($handle, $false)
$stream = New-Object System.IO.FileStream($safeHandle, [System.IO.FileAccess]::Read, 4096, $false)
$reader = New-Object System.IO.StreamReader($stream, [System.Text.Encoding]::Default)
while (($line = $reader.ReadLine()) -ne $null) {
$result += $line + "`n"
}
$reader.Close()
$stream.Close()
$safeHandle.Close()
return $result
}
function Execute-CommandInSession {
param (
[int]$SessionId,
[string]$Command
)
$userToken = [IntPtr]::Zero
if (-not [NativeMethods]::WTSQueryUserToken($SessionId, [ref]$userToken)) {
throw [System.ComponentModel.Win32Exception]::New([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())
}
$sa = New-Object NativeMethods+SECURITY_ATTRIBUTES
$sa.nLength = [System.Runtime.InteropServices.Marshal]::SizeOf($sa)
$sa.bInheritHandle = $true
$si = New-Object NativeMethods+STARTUPINFO
$pi = New-Object NativeMethods+PROCESS_INFORMATION
$si.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($si)
$stdoutReadPipe = [IntPtr]::Zero
$stdoutWritePipe = [IntPtr]::Zero
$stderrReadPipe = [IntPtr]::Zero
$stderrWritePipe = [IntPtr]::Zero
if (-not [NativeMethods]::CreatePipe([ref]$stdoutReadPipe, [ref]$stdoutWritePipe, [ref]$sa, 0)) {
throw [System.ComponentModel.Win32Exception]::New([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())
}
if (-not [NativeMethods]::CreatePipe([ref]$stderrReadPipe, [ref]$stderrWritePipe, [ref]$sa, 0)) {
throw [System.ComponentModel.Win32Exception]::New([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())
}
$si.dwFlags = 0x00000100 -bor 0x00000200 -bor 0x00000400 -bor 0x00000001 # STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
$si.hStdOutput = $stdoutWritePipe
$si.hStdError = $stderrWritePipe
$si.wShowWindow = 0 # SW_HIDE
$PowerShell = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$base64Command = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($Command))
$arguments = "-NoProfile -EncodedCommand $base64Command"
if (-not [NativeMethods]::CreateProcessAsUser($userToken, $PowerShell, $arguments, [ref]$sa, [ref]$sa, $true, 0, [IntPtr]::Zero, $PWD.Path, [ref]$si, [ref]$pi)) {
throw [System.ComponentModel.Win32Exception]::New([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())
}
[void][NativeMethods]::CloseHandle($stdoutWritePipe)
[void][NativeMethods]::CloseHandle($stderrWritePipe)
$stdoutOutput = ReadStream -handle $stdoutReadPipe
$stderrOutput = ReadStream -handle $stderrReadPipe
# Clean up
[void][NativeMethods]::CloseHandle($pi.hProcess)
[void][NativeMethods]::CloseHandle($pi.hThread)
[void][NativeMethods]::CloseHandle($userToken)
[void][NativeMethods]::CloseHandle($stdoutReadPipe)
[void][NativeMethods]::CloseHandle($stderrReadPipe)
return $stdoutOutput + $stderrOutput
}
function Get-UserSessions {
$quserOutput = quser
$quserLines = $quserOutput | Select-Object -Skip 1
$sessions = foreach ($line in $quserLines) {
$cleanLine = $line -replace '>', ''
$regex = '^\s*(\S*)\s+(\S*)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.+)$'
if ($cleanLine -match $regex) {
[PSCustomObject]@{
Username = $matches[1]
SessionName = $matches[2]
ID = [int]$matches[3]
State = $matches[4]
IdleTime = $matches[5]
LogonTime = $matches[6]
}
}
}
return $sessions
}
Write-Output ""
$sessions = Get-UserSessions
$CurrentSessionID = (Get-Process -PID $pid).SessionID
$validStates = @("Active", "Connected", "ConnectQuery", "Shadow", "Disconnected", "Idle", "Disc", "Listen")
if ($SessionID -eq "All") {
foreach ($session in $sessions) {
if ($session.ID -eq $CurrentSessionID -or -not $validStates -contains $session.State) { continue }
Write-Output ""
Write-Output "[+] Invoke Command as $($session.Username) under Session ID:$($session.ID)"
$output = Execute-CommandInSession -SessionId ($session.ID) -Command $Command
Write-Output $output
Write-Output ""
}
}
else {
$session = $sessions | Where-Object { $_.ID -eq $SessionID }
if ($null -ne $session -and $validStates -contains $session.State) {
Write-Output "[+] Invoke Command under Session ID:$SessionID"
$output = Execute-CommandInSession -SessionId $SessionID -Command $Command
Write-Output $output
}
}
}