Skip to content

Commit 0c19abd

Browse files
committed
Added Ticket logon and data to KerberosTicket.
1 parent 5cc4538 commit 0c19abd

9 files changed

Lines changed: 130 additions & 17 deletions

File tree

NtApiDotNet/Win32/LogonUtils.cs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
using NtApiDotNet.Win32.SafeHandles;
1616
using NtApiDotNet.Win32.Security.Authentication;
17+
using NtApiDotNet.Win32.Security.Authentication.Kerberos;
1718
using NtApiDotNet.Win32.Security.Native;
1819
using NtApiDotNet.Win32.Security.Policy;
1920
using System;
@@ -152,6 +153,36 @@ public static NtToken Logon(string user, string domain, string password, Securit
152153
}
153154
}
154155

156+
/// <summary>
157+
/// Logon user using Kerberos Ticket.
158+
/// </summary>
159+
/// <param name="type">The type of logon token.</param>
160+
/// <param name="service_ticket">The service ticket.</param>
161+
/// <param name="tgt_ticket">Optional TGT.</param>
162+
/// <param name="throw_on_error">True to throw on error.</param>
163+
/// <returns>The logged on token.</returns>
164+
public static NtResult<NtToken> LsaLogonTicket(SecurityLogonType type, KerberosTicket service_ticket, KerberosCredential tgt_ticket, bool throw_on_error)
165+
{
166+
if (service_ticket is null)
167+
{
168+
throw new ArgumentNullException(nameof(service_ticket));
169+
}
170+
171+
return LsaLogonTicket(type, service_ticket.TicketData, tgt_ticket?.ToArray(), throw_on_error);
172+
}
173+
174+
/// <summary>
175+
/// Logon user using Kerberos Ticket.
176+
/// </summary>
177+
/// <param name="type">The type of logon token.</param>
178+
/// <param name="service_ticket">The service ticket.</param>
179+
/// <param name="tgt_ticket">Optional TGT.</param>
180+
/// <returns>The logged on token.</returns>
181+
public static NtToken LsaLogonTicket(SecurityLogonType type, KerberosTicket service_ticket, KerberosCredential tgt_ticket)
182+
{
183+
return LsaLogonTicket(type, service_ticket, tgt_ticket, true).Result;
184+
}
185+
155186
/// <summary>
156187
/// Logon user using Kerberos Ticket.
157188
/// </summary>
@@ -466,10 +497,10 @@ private static NtResult<NtToken> LsaLogonUser(SecurityLogonType type, string aut
466497
var hlsa = list.AddResource(SafeLsaLogonHandle.Connect(throw_on_error));
467498
if (!hlsa.IsSuccess)
468499
return hlsa.Cast<NtToken>();
469-
NtStatus status = SecurityNativeMethods.LsaLookupAuthenticationPackage(
470-
hlsa.Result, new LsaString(auth_package), out uint auth_pkg);
471-
if (!status.IsSuccess())
472-
return status.CreateResultFromError<NtToken>(throw_on_error);
500+
501+
var auth_pkg = hlsa.Result.LookupAuthPackage(auth_package, throw_on_error);
502+
if (!auth_pkg.IsSuccess)
503+
return auth_pkg.Cast<NtToken>();
473504

474505
var groups = local_groups == null ? SafeTokenGroupsBuffer.Null
475506
: list.AddResource(SafeTokenGroupsBuffer.Create(local_groups));
@@ -478,7 +509,7 @@ private static NtResult<NtToken> LsaLogonUser(SecurityLogonType type, string aut
478509
SecurityNativeMethods.AllocateLocallyUniqueId(out tokenSource.SourceIdentifier);
479510
QUOTA_LIMITS quota_limits = new QUOTA_LIMITS();
480511
return SecurityNativeMethods.LsaLogonUser(hlsa.Result, new LsaString(origin_name),
481-
type, auth_pkg, buffer, buffer.GetLength(), groups,
512+
type, auth_pkg.Result, buffer, buffer.GetLength(), groups,
482513
tokenSource, out SafeLsaReturnBufferHandle profile,
483514
out int cbProfile, out Luid logon_id, out SafeKernelObjectHandle token_handle,
484515
quota_limits, out NtStatus subStatus).CreateResult(throw_on_error, () =>

NtApiDotNet/Win32/SafeHandles/SafeLsaLogonHandle.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,21 @@
1515
using Microsoft.Win32.SafeHandles;
1616
using NtApiDotNet.Win32.Security.Native;
1717
using System;
18+
using System.Runtime.InteropServices;
1819

1920
namespace NtApiDotNet.Win32.SafeHandles
2021
{
22+
internal struct LsaCallPackageResponse : IDisposable
23+
{
24+
public NtStatus Status;
25+
public SafeLsaReturnBufferHandle Buffer;
26+
27+
public void Dispose()
28+
{
29+
((IDisposable)Buffer)?.Dispose();
30+
}
31+
}
32+
2133
internal class SafeLsaLogonHandle : SafeHandleZeroOrMinusOneIsInvalid
2234
{
2335
public SafeLsaLogonHandle(IntPtr handle, bool ownsHandle) : base(ownsHandle)
@@ -42,5 +54,30 @@ internal static NtResult<SafeLsaLogonHandle> Connect(bool throw_on_error)
4254
}
4355
return hlsa.CreateResult();
4456
}
57+
58+
public NtResult<uint> LookupAuthPackage(string auth_package, bool throw_on_error)
59+
{
60+
return SecurityNativeMethods.LsaLookupAuthenticationPackage(
61+
this, new LsaString(auth_package), out uint auth_pkg).CreateResult(throw_on_error, () => auth_pkg);
62+
}
63+
64+
private static LsaCallPackageResponse CreateResponse(NtStatus status, SafeLsaReturnBufferHandle buffer, int length)
65+
{
66+
if (!(buffer?.IsInvalid ?? true))
67+
{
68+
buffer?.Initialize((uint)length);
69+
}
70+
return new LsaCallPackageResponse()
71+
{
72+
Status = status,
73+
Buffer = buffer
74+
};
75+
}
76+
77+
public NtResult<LsaCallPackageResponse> CallPackage(uint auth_package, SafeBuffer buffer, bool throw_on_error)
78+
{
79+
return SecurityNativeMethods.LsaCallAuthenticationPackage(this, auth_package, buffer, buffer.GetLength(),
80+
out SafeLsaReturnBufferHandle ret, out int ret_length, out NtStatus status).CreateResult(throw_on_error, () => CreateResponse(status, ret, ret_length));
81+
}
4582
}
4683
}

NtApiDotNet/Win32/Security/Authentication/Kerberos/KerberosAPRequestAuthenticationToken.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public class KerberosAPRequestAuthenticationToken : KerberosAuthenticationToken
6565
private protected KerberosAPRequestAuthenticationToken(byte[] data, DERValue[] values)
6666
: base(data, values, KerberosMessageType.KRB_AP_REQ)
6767
{
68-
Ticket = new KerberosTicket();
68+
Ticket = new KerberosTicket(new byte[0]);
6969
Authenticator = new KerberosEncryptedData();
7070
}
7171
#endregion
@@ -176,7 +176,7 @@ internal static bool TryParse(byte[] data, DERValue[] values, out KerberosAuthen
176176
case 3:
177177
if (!next.HasChildren())
178178
return false;
179-
ret.Ticket = KerberosTicket.Parse(next.Children[0]);
179+
ret.Ticket = KerberosTicket.Parse(next.Children[0], next.Data);
180180
break;
181181
case 4:
182182
if (!next.HasChildren())

NtApiDotNet/Win32/Security/Authentication/Kerberos/KerberosCredential.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ internal static bool TryParse(byte[] data, DERValue[] values, out KerberosCreden
137137
List<KerberosTicket> tickets = new List<KerberosTicket>();
138138
foreach (var child in next.Children[0].Children)
139139
{
140-
tickets.Add(KerberosTicket.Parse(child));
140+
tickets.Add(KerberosTicket.Parse(child, next.Children[0].Data));
141141
}
142142
ret.Tickets = tickets.AsReadOnly();
143143
break;

NtApiDotNet/Win32/Security/Authentication/Kerberos/KerberosTGTReplyAuthenticationToken.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ internal static bool TryParse(byte[] data, DERValue[] values, out KerberosAuthen
7979
case 2:
8080
if (!next.HasChildren())
8181
return false;
82-
ret.Ticket = KerberosTicket.Parse(next.Children[0]);
82+
ret.Ticket = KerberosTicket.Parse(next.Children[0], next.Data);
8383
break;
8484
default:
8585
return false;

NtApiDotNet/Win32/Security/Authentication/Kerberos/KerberosTicket.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public class KerberosTicket
4444
/// </summary>
4545
public string Principal => ServerName.GetPrincipal(Realm);
4646

47+
internal byte[] TicketData { get; }
48+
4749
internal bool Decrypt(KerberosKeySet keyset, KeyUsage key_usage, out KerberosTicket ticket)
4850
{
4951
if (this is KerberosTicketDecrypted)
@@ -78,28 +80,30 @@ private protected KerberosTicket(
7880
int ticket_version,
7981
string realm,
8082
KerberosPrincipalName server_name,
81-
KerberosEncryptedData encrypted_data)
83+
KerberosEncryptedData encrypted_data,
84+
byte[] ticket_data)
8285
{
8386
TicketVersion = ticket_version;
8487
Realm = realm ?? string.Empty;
8588
ServerName = server_name ?? new KerberosPrincipalName();
8689
EncryptedData = encrypted_data;
90+
TicketData = ticket_data;
8791
}
8892

89-
internal KerberosTicket()
90-
: this(5, null, null, null)
93+
internal KerberosTicket(byte[] ticket_data)
94+
: this(5, null, null, null, ticket_data)
9195
{
9296
}
9397

94-
internal static KerberosTicket Parse(DERValue value)
98+
internal static KerberosTicket Parse(DERValue value, byte[] data)
9599
{
96100
if (!value.CheckApplication(1) || !value.HasChildren())
97101
throw new InvalidDataException();
98102

99103
if (!value.Children[0].CheckSequence())
100104
throw new InvalidDataException();
101105

102-
KerberosTicket ret = new KerberosTicket();
106+
KerberosTicket ret = new KerberosTicket(data);
103107
foreach (var next in value.Children[0].Children)
104108
{
105109
if (next.Type != DERTagType.ContextSpecific)

NtApiDotNet/Win32/Security/Authentication/Kerberos/KerberosTicketDecrypted.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ private protected override void FormatTicketData(StringBuilder builder)
155155

156156
private KerberosTicketDecrypted(
157157
KerberosTicket ticket)
158-
: base(ticket.TicketVersion, ticket.Realm, ticket.ServerName, ticket.EncryptedData)
158+
: base(ticket.TicketVersion, ticket.Realm, ticket.ServerName, ticket.EncryptedData, ticket.TicketData)
159159
{
160160
HostAddresses = new List<KerberosHostAddress>().AsReadOnly();
161161
AuthorizationData = new List<KerberosAuthorizationData>().AsReadOnly();

NtApiDotNet/Win32/Security/Native/SecurityNativeMethods.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,17 @@ internal static extern NtStatus LsaEnumerateAccountsWithUserRight(
571571
out int CountReturned
572572
);
573573

574+
[DllImport("Secur32.dll", CharSet = CharSet.Unicode)]
575+
internal static extern NtStatus LsaCallAuthenticationPackage(
576+
SafeLsaLogonHandle LsaHandle,
577+
uint AuthenticationPackage,
578+
SafeBuffer ProtocolSubmitBuffer,
579+
int SubmitBufferLength,
580+
out SafeLsaReturnBufferHandle ProtocolReturnBuffer,
581+
out int ReturnBufferLength,
582+
out NtStatus ProtocolStatus
583+
);
584+
574585
[DllImport("sspicli.dll", CharSet = CharSet.Unicode)]
575586
internal static extern SecStatusCode QueryContextAttributesEx(
576587
SecHandle phContext,

NtObjectManager/Cmdlets/Object/GetNtTokenCmdlet.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
using NtApiDotNet.Win32;
2121
using System.Security;
2222
using System.Runtime.InteropServices;
23+
using NtApiDotNet.Win32.Security.Authentication;
24+
using NtApiDotNet.Win32.Security.Authentication.Kerberos;
2325

2426
namespace NtObjectManager.Cmdlets.Object
2527
{
@@ -305,9 +307,21 @@ public sealed class GetNtTokenCmdlet : PSCmdlet
305307
/// <summary>
306308
/// <para type="description">Specify logon type for logon token.</para>
307309
/// </summary>
308-
[Parameter(ParameterSetName = "Logon"), Parameter(ParameterSetName = "S4U")]
310+
[Parameter(ParameterSetName = "Logon"), Parameter(ParameterSetName = "S4U"), Parameter(ParameterSetName = "Ticket")]
309311
public SecurityLogonType LogonType { get; set; }
310312

313+
/// <summary>
314+
/// <para type="description">Specify Service Ticket for Logon.</para>
315+
/// </summary>
316+
[Parameter(Position = 0, ParameterSetName = "Ticket", Mandatory = true)]
317+
public KerberosTicket Ticket { get; }
318+
319+
/// <summary>
320+
/// <para type="description">Specify optional TGT for logon.</para>
321+
/// </summary>
322+
[Parameter(ParameterSetName = "Ticket", Mandatory = true)]
323+
public KerberosCredential KerbCred { get; }
324+
311325
/// <summary>
312326
/// <para type="description">Get anonymous token.</para>
313327
/// </summary>
@@ -582,7 +596,19 @@ private NtToken GetLogonToken(TokenAccessRights desired_access)
582596

583597
private NtToken GetS4UToken(TokenAccessRights desired_access)
584598
{
585-
using (NtToken token = LogonUtils.LsaLogonS4U(User, Domain, LogonType, "Negotiate"))
599+
using (NtToken token = LogonUtils.LsaLogonS4U(User, Domain, LogonType, AuthenticationPackage.NEGOSSP_NAME))
600+
{
601+
if (desired_access == TokenAccessRights.MaximumAllowed)
602+
{
603+
return token.Duplicate();
604+
}
605+
return token.Duplicate(desired_access);
606+
}
607+
}
608+
609+
private NtToken GetTicketToken(TokenAccessRights desired_access)
610+
{
611+
using (NtToken token = LogonUtils.LsaLogonTicket(LogonType, Ticket, KerbCred))
586612
{
587613
if (desired_access == TokenAccessRights.MaximumAllowed)
588614
{
@@ -722,6 +748,10 @@ private NtToken GetToken(TokenAccessRights desired_access)
722748
{
723749
return GetS4UToken(desired_access);
724750
}
751+
else if (Ticket != null)
752+
{
753+
return GetTicketToken(desired_access);
754+
}
725755
else if (Anonymous)
726756
{
727757
return GetAnonymousToken(desired_access);

0 commit comments

Comments
 (0)