Skip to content

Commit e6c44cd

Browse files
committed
updating ticket_*_add commands to use impacket more locally
1 parent 9e3d70c commit e6c44cd

File tree

12 files changed

+143
-55
lines changed

12 files changed

+143
-55
lines changed

Payload_Type/apollo/CHANGELOG.MD

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ 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.3.9] - 2025-03-14
8+
9+
### Changed
10+
11+
- Updated `ticket_store_add` to not create a temp logon session to fetch ticket information
12+
- Instead, ticket information is fetched via impacket in the apollo container first and sent down with the task
13+
714
## [v2.3.8] - 2025-03-12
815

916
### Changed

Payload_Type/apollo/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ RUN curl -L -o donut_shellcode-2.0.0.tar.gz https://github.com/MEhrn00/donut/rel
1212

1313
WORKDIR /Mythic/
1414
RUN python3 -m venv /venv
15-
RUN /venv/bin/python -m pip install mythic-container==0.5.22 mslex impacket
15+
RUN /venv/bin/python -m pip install mythic-container==0.5.26 mslex impacket
1616
RUN /venv/bin/python -m pip install git+https://github.com/MEhrn00/donut.git@v2.0.0
1717

1818
COPY [".", "."]

Payload_Type/apollo/apollo/agent_code/ApolloInterop/Features/KerberosTickets/ITicketManager.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public interface ITicketManager
2222
//should return all tickets in the current LUID or all tickets if running as administrator
2323
public List<KerberosTicket> EnumerateTicketsInCache(bool getSystemTickets = false, string luid = "");
2424
//loads a ticket into memory and should be tracked by the agent session
25-
public bool LoadTicketIntoCache(byte[] ticket, string luid);
25+
public (bool, string) LoadTicketIntoCache(byte[] ticket, string luid);
2626
//unloads a ticket from memory and should be removed from the agent session
2727
public bool UnloadTicketFromCache(string serviceName, string domainName, string luid, bool All = false);
2828

Payload_Type/apollo/apollo/agent_code/ApolloInterop/Features/KerberosTickets/KerberosTypes.cs

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using static ApolloInterop.Features.WindowsTypesAndAPIs.WinNTTypes;
77
using static ApolloInterop.Features.WindowsTypesAndAPIs.LSATypes;
88
using static ApolloInterop.Features.WindowsTypesAndAPIs.APIInteropTypes;
9+
using System.Runtime.Serialization;
910

1011
namespace ApolloInterop.Features.KerberosTickets;
1112

@@ -27,20 +28,31 @@ public record struct LogonSessionData
2728
/// <summary>
2829
/// Record type to represent a Kerberos ticket.
2930
/// </summary>
31+
[DataContract]
3032
public record KerberosTicket
3133
{
34+
[DataMember(Name = "luid")]
3235
public LUID Luid { get; set; }
3336
public string LogonId => Luid.ToString();
37+
[DataMember(Name = "ClientName")]
3438
public string ClientName { get; set; }
39+
[DataMember(Name = "ClientRealm")]
3540
public string ClientRealm { get; set; }
3641
public string ClientFullName => $"{ClientName}@{ClientRealm}";
42+
[DataMember(Name = "ServerName")]
3743
public string ServerName { get; set; }
44+
[DataMember(Name = "ServerRealm")]
3845
public string ServerRealm { get; set; }
3946
public string ServerFullName => $"{ServerName}@{ServerRealm}";
47+
[DataMember(Name = "StartTime")]
4048
public DateTime StartTime { get; set; }
49+
[DataMember(Name = "EndTime")]
4150
public DateTime EndTime { get; set; }
51+
[DataMember(Name = "RenewTime")]
4252
public DateTime RenewTime { get; set; }
53+
[DataMember(Name = "EncryptionType")]
4354
public KerbEncType EncryptionType { get; set; }
55+
[DataMember(Name = "TicketFlags")]
4456
public KerbTicketFlags TicketFlags { get; set; }
4557
public byte[] Kirbi { get; set; } = [];
4658
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ internal static (KerberosTicket?, string) ExtractTicket(LUID targetLuid, string
522522
}
523523

524524
// load ticket
525-
internal static bool LoadTicket(byte[] submittedTicket, LUID targetLuid)
525+
internal static (bool, string) LoadTicket(byte[] submittedTicket, LUID targetLuid)
526526
{
527527
HANDLE requestAndTicketHandle = new();
528528
HANDLE lsaHandle = new();
@@ -570,14 +570,14 @@ internal static bool LoadTicket(byte[] submittedTicket, LUID targetLuid)
570570
if (status != NTSTATUS.STATUS_SUCCESS || returnStatus != NTSTATUS.STATUS_SUCCESS)
571571
{
572572
DebugHelp.DebugWriteLine($"Failed to submit ticket with api status: {status} and return status: {returnStatus}");
573-
return false;
573+
return (false, $"Failed to submit ticket:\n{returnStatus}");
574574
}
575575
DebugHelp.DebugWriteLine("Ticket submitted");
576-
return true;
576+
return (true, "");
577577
}
578578
catch (Exception e)
579579
{
580-
return false;
580+
return (false, e.Message);
581581
}
582582
finally
583583
{

Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosTicketManager.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public KerberosTicketManager(IAgent agent)
3838
public (KerberosTicket?, string) ExtractTicketFromCache(string luid, string serviceName) => KerberosHelpers.ExtractTicket(WinNTTypes.LUID.FromString(luid), serviceName);
3939
public List<KerberosTicket> EnumerateTicketsInCache(bool getSystemTickets = false, string luid = "") => KerberosHelpers.TriageTickets(getSystemTickets,luid).ToList();
4040

41-
public bool LoadTicketIntoCache(byte[] ticket, string luid) => KerberosHelpers.LoadTicket(ticket, WinNTTypes.LUID.FromString(luid));
41+
public (bool, string) LoadTicketIntoCache(byte[] ticket, string luid) => KerberosHelpers.LoadTicket(ticket, WinNTTypes.LUID.FromString(luid));
4242

4343
public bool UnloadTicketFromCache(string serviceName, string domainName, string luid, bool All = false) => KerberosHelpers.UnloadTicket(serviceName, domainName, WinNTTypes.LUID.FromString(luid), All);
4444

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,16 @@ public override void Start()
3838
string luid = parameters.luid ?? "";
3939
string base64Ticket = parameters.base64Ticket;
4040
byte[] ticketBytes = Convert.FromBase64String(base64Ticket);
41-
if (_agent.GetTicketManager().LoadTicketIntoCache(ticketBytes, luid))
41+
bool success;
42+
string errorMsg;
43+
(success, errorMsg) = _agent.GetTicketManager().LoadTicketIntoCache(ticketBytes, luid);
44+
if (success)
4245
{
4346
resp = CreateTaskResponse($"Injected Ticket into Cache", true);
4447
}
4548
else
4649
{
47-
resp = CreateTaskResponse($"Failed to inject ticket into cache", true, "error");
50+
resp = CreateTaskResponse($"{errorMsg}", true, "error");
4851
}
4952
}
5053
catch (Exception e)

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

+31-11
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@ internal struct TicketStoreAddParameters
2525
{
2626
[DataMember(Name = "base64ticket")]
2727
internal string base64Ticket;
28+
[DataMember(Name = "ClientName")]
29+
public string ClientName;
30+
[DataMember(Name = "ClientRealm")]
31+
public string ClientRealm;
32+
[DataMember(Name = "ServerName")]
33+
public string ServerName;
34+
[DataMember(Name = "ServerRealm")]
35+
public string ServerRealm;
36+
[DataMember(Name = "StartTime")]
37+
public int StartTime;
38+
[DataMember(Name = "EndTime")]
39+
public int EndTime;
40+
[DataMember(Name = "RenewTime")]
41+
public int RenewTime;
42+
[DataMember(Name = "EncryptionType")]
43+
public int EncryptionType;
44+
[DataMember(Name = "TicketFlags")]
45+
public int TicketFlags;
2846
}
2947

3048
public ticket_store_add(IAgent agent, MythicTask data) : base(agent, data)
@@ -37,17 +55,19 @@ public override void Start()
3755
TicketStoreAddParameters parameters = _jsonSerializer.Deserialize<TicketStoreAddParameters>(_data.Parameters);
3856
string base64Ticket = parameters.base64Ticket;
3957
byte[] ticketBytes = Convert.FromBase64String(base64Ticket);
40-
//make a placeholder ticket for now
41-
KerberosTicket? ticket = _agent.GetTicketManager().GetTicketDetailsFromKirbi(ticketBytes);
42-
if(ticket == null)
43-
{
44-
resp = CreateTaskResponse($"Failed to extract ticket from kirbi or failed to parse new data", true, "error");
45-
}
46-
else
47-
{
48-
_agent.GetTicketManager().AddTicketToTicketStore(new KerberosTicketStoreDTO(ticket));
49-
resp = CreateTaskResponse($"Added Ticket to Ticket Store", true);
50-
}
58+
KerberosTicket ticket = new KerberosTicket();
59+
ticket.ClientRealm = parameters.ClientRealm;
60+
ticket.ClientName = parameters.ClientName;
61+
ticket.ServerName = parameters.ServerName;
62+
ticket.ServerRealm = parameters.ServerRealm;
63+
ticket.StartTime = new DateTime(1970,1,1,0,0,0).AddSeconds(parameters.StartTime);
64+
ticket.EndTime = new DateTime(1970,1,1,0,0,0).AddSeconds(parameters.EndTime);
65+
ticket.RenewTime = new DateTime(1970,1,1,0,0,0).AddSeconds(parameters.RenewTime);
66+
ticket.TicketFlags = (KerbTicketFlags)parameters.TicketFlags;
67+
ticket.EncryptionType = (KerbEncType)parameters.EncryptionType;
68+
ticket.Kirbi = ticketBytes;
69+
_agent.GetTicketManager().AddTicketToTicketStore(new KerberosTicketStoreDTO(ticket));
70+
resp = CreateTaskResponse($"Added Ticket to Ticket Store", true);
5171
}
5272
catch (Exception e)
5373
{

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.3.8"
24+
version = "2.3.9"
2525
wrapper = False
2626
wrapped_payloads = ["scarecrow_wrapper", "service_wrapper"]
2727
note = """

Payload_Type/apollo/apollo/mythic/agent_functions/ticket_cache_add.py

+19-16
Original file line numberDiff line numberDiff line change
@@ -79,23 +79,25 @@ class ticket_cache_addCommand(CommandBase):
7979

8080
async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse:
8181
response = PTTaskCreateTaskingMessageResponse( TaskID=taskData.Task.ID,Success=True)
82-
if taskData.args.get_parameter_group_name() == "Use Existing Ticket":
82+
current_group_name = taskData.args.get_parameter_group_name()
83+
if current_group_name == "Use Existing Ticket":
8384
credentialData = taskData.args.get_arg("existingTicket")
8485
taskData.args.remove_arg("existingTicket")
85-
taskData.args.add_arg("base64ticket", credentialData["credential"], parameter_group_info=[ParameterGroupInfo(group_name="Use Existing Ticket")])
86-
else:
87-
base64Ticket = taskData.args.get_arg("base64ticket")
88-
ccache = CCache()
89-
ccache.fromKRBCRED(base64.b64decode(base64Ticket))
90-
#ccache.credentials[0].__getitem__('client').prettyPrint() # user@domain
91-
#ccache.credentials[0].__getitem__('server').prettyPrint() # krbtgt/domain@domain
92-
#datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['starttime']).isoformat()
93-
#datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['endtime']).isoformat()
94-
#datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['renew_till']).isoformat()
95-
formattedComment = f"Service: {ccache.credentials[0].__getitem__('server').prettyPrint().decode('utf-8')}\n"
96-
formattedComment += f"Start: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['starttime']).isoformat()}\n"
97-
formattedComment += f"End: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['endtime']).isoformat()}\n"
98-
formattedComment += f"Renew: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['renew_till']).isoformat()}\n"
86+
taskData.args.add_arg("base64ticket", credentialData["credential"], parameter_group_info=[ParameterGroupInfo(group_name=current_group_name)])
87+
88+
base64Ticket = taskData.args.get_arg("base64ticket")
89+
ccache = CCache()
90+
ccache.fromKRBCRED(base64.b64decode(base64Ticket))
91+
#ccache.credentials[0].__getitem__('client').prettyPrint() # user@domain
92+
#ccache.credentials[0].__getitem__('server').prettyPrint() # krbtgt/domain@domain
93+
#datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['starttime']).isoformat()
94+
#datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['endtime']).isoformat()
95+
#datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['renew_till']).isoformat()
96+
formattedComment = f"Service: {ccache.credentials[0].__getitem__('server').prettyPrint().decode('utf-8')}\n"
97+
formattedComment += f"Start: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['starttime']).isoformat()}\n"
98+
formattedComment += f"End: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['endtime']).isoformat()}\n"
99+
formattedComment += f"Renew: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['renew_till']).isoformat()}\n"
100+
if current_group_name == "Add New Ticket":
99101
resp = await SendMythicRPCCredentialCreate(MythicRPCCredentialCreateMessage(
100102
TaskID=taskData.Task.ID,
101103
Credentials=[
@@ -108,7 +110,8 @@ async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreat
108110
)
109111
]
110112
))
111-
response.DisplayParams = f"-base64ticket {taskData.args.get_arg('base64ticket')}"
113+
response.DisplayParams = f" client: {ccache.credentials[0].__getitem__('client').prettyPrint().decode('utf-8')}"
114+
response.DisplayParams += f", service: {ccache.credentials[0].__getitem__('server').prettyPrint().decode('utf-8')}"
112115
luid = taskData.args.get_arg("luid")
113116
if luid is not None and luid != "":
114117
response.DisplayParams += f" -luid {luid}"

Payload_Type/apollo/apollo/mythic/agent_functions/ticket_cache_extract.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,11 @@ async def parse_credentials(task: PTTaskCompletionFunctionMessage, ) -> PTTaskCo
8484
TaskID=task.TaskData.Task.ID,
8585
Response=f"\nFailed to add to Mythic's credential store:\n{resp.Error}".encode()
8686
))
87-
except:
88-
pass
87+
except Exception as e:
88+
logger.error(e)
8989
return response
9090

91+
9192
class ticket_cache_extractCommand(CommandBase):
9293
cmd = "ticket_cache_extract"
9394
needs_admin = False

0 commit comments

Comments
 (0)