Lab 3 - Dumping hashes and performing Pass the Hash

Bonus: Run tasklist in Empire and hunt for it, what do you see? Run it again and this time tick the "literal" button, what do you see? why is there a difference?

The answer is in how Empire is executing shell commands.. take a look at https://github.com/BC-SECURITY/Empire/blob/main/empire/server/data/agent/agent.ps1#L349 and be amazed :)

  1. Interact with your empire agent and use the built-in mimikatz task to dump password hashes by using the powershell_credentials_mimikatz_logonpasswords module.

Since this is PowerShell, you'd expect that we would be able to find mimikatz execution by taking a look at the scriptblock logging (event id 4104) and looking for mimikatz, but that doesn't yield a result... can you figure out why that is? Empire obfuscates mimikatz by default, check your obfuscation tab in Empire, likely the obfuscation will be different for you

  1. Check it out with KQL in your Kibana interface : winlog.event_data.ScriptBlockText : (Mimi*) Should yield 0 results. KQL that bypasses the obfuscation: winlog.event_data.ScriptBlockText : (*logonpassword*)

  1. Alternatively, we can also look for event.code: 10 and winlog.event_data.TargetImage: lsass Since mimikatz needs to touch LSASS prior to dumping it, it's a dead giveaway, especially when PowerShell is touching LSASS. You will not be fooling anyone in a red team operation with this, even though you did not drop mimikatz to disk :)

  2. Inside the mimikatz dump you should find some new credentials...

  1. Let's perform pass the hash in Empire, go back to the interact screen, select powershell_credentials_pth. Select the credential you want to use (adm_asnyder) Press Submit

  2. Since mimikatz pth spawns a new process, we can hunt for it using the parent process that we already identified when we hunted for our stager. If you forgot the PID already, don't worry, you can find it in your agent overview. My agent is PID 5344, so my KQL query would look like: event.code: 1 and winlog.event_data.ParentProcessId: 5344

Indeed, we see cmd.exe spawned from our process with PID 840 (yours will be different), which corresponds to my empire output.

Since our lab has script block logging enabled, we can also find the pth execution by litteraly just looking for it in our scriptblock logs...

What is interesting about pass the hash is that a new logon session is created with logontype 9 (the same logon type when using runas /netonly which usually is interesting to investigate. KQL: winlog.event_id:4624 and host.hostname : ws01 and winlog.event_data.LogonType: 9

Indeed, we see a new logon impersonating for adm_asnyder.

  1. We now have two options, either we steal the token of the process we just spawned, or we steal the token of a process that was actually already running as adm_asnyder (yes, we did pth for nothing!, we could have just lived off the land!) The technique is the same in both cases, either you look at tasklist to find the PID of a process belonging to adm_asnyder, or you take a look at the mimikatz output and note the PID of the output there. We will be using powershell_credentials_tokens to achieve this task. Set the ImpersonateUser switch to true and specify the processID of the process you wish to steal

The underlying script that is used by Empire to achieve this is: https://github.com/BC-SECURITY/Empire/blob/main/empire/server/data/module_source/credentials/Invoke-TokenManipulation.ps1#L1397

The core of the token stealing and impersonation logic happens when you run a command like steal_token -ProcessId <PID> or invoke_tokenmanipulation -ImpersonateUser -ProcessId <PID>. This triggers the following key functions in the script:

Enum-AllTokens: This is the reconnaissance phase. It iterates through all processes on the system.

Get-PrimaryToken: Inside Enum-AllTokens, this function is called for each process. It uses the Windows API call OpenProcess to get a handle to the target process, and then OpenProcessToken to get a handle to that process's primary access token.

Main function logic: After enumerating all available tokens, the main logic block selects the specific token you requested (e.g., by -ProcessId).

Invoke-ImpersonateUser: This is the action phase for impersonation.

  • DuplicateTokenEx: It first calls this API to create a new, usable copy of the stolen token handle. It specifically requests an impersonation token.

  • ImpersonateLoggedOnUser: This is the final step. It applies the duplicated token to the current thread of the PowerShell agent. After this call, that specific thread is now running with the rights and identity of the user whose token was stolen. From a detection point of view, the core telemetry here would be sysmon event ID 10 (process access) and cross refferencing with process ownership. However, sysmon event id 10 is a very noisy event log, which causes a lot of telemetry to be generated. Most of the time in an enterprise environment this will severely be tuned to only monitor relevant processes (i.e LSASS) or be disabled. As a result, we won't be hunting for this step in the killchain.

  1. using the Shell Command we can now do Invoke-Command -ScriptBlock {<YOUR EMPIRE STAGER HERE>} -ComputerName sandbox-dc01.sandbox.pwnzone.lab

    When you run Invoke-Command -ComputerName <target>, the following happens:

    1. Authentication: Your source machine authenticates to the target machine over the network.

    2. Connection: It connects to the WinRM service on the target, which listens on TCP ports 5985 (HTTP) or 5986 (HTTPS).

    3. Session Creation: The WinRM service on the target creates a new PowerShell session for you.

    4. Process Spawning: To host this new session, the WinRM service spawns a new process on the target machine. This is typically a wsmprovhost.exe (Windows Services Management Provider Host) process.

    5. Execution: The ScriptBlock you provided (your Empire stager) is then executed within the context of this wsmprovhost.exe process. https://help.fortinet.com/fsiem/Public_Resource_Access/7_3_0/rules/PH_RULE_Remote_PowerShell_Sessions.htm provides a sample detection rule our KQL would look like the following host.name : "ws01.sandbox.pwnzone.lab" and winlog.channel : "Microsoft-Windows-Sysmon/Operational" and winlog.event_id : 3 and winlog.event_data.DestinationPort: 5985 To no one's surprise, this yields results!

we can track the movement on the DC using the following KQL host.name : "sandbox-dc01.sandbox.pwnzone.lab" and winlog.channel : "Security" and winlog.event_id : 4624 and winlog.event_data.LogonType : 3 and winlog.event_data.IpPort: 5999* Do note that the SourcePort does not always equal the sourceport on the other side of the tunnel, authentication is a complex process, beyond the scope of this workshop.

Finally, we can also take a look at what was actually executed. Tracking PSRemoting is actually reasonably straight forward as the parent process is predictable host.name : "sandbox-dc01.sandbox.pwnzone.lab" and winlog.channel : "Microsoft-Windows-Sysmon/Operational" and winlog.event_id : 1 and winlog.event_data.ParentImage: wsmprovhost.exe

  1. We can do exactly the same attack using

powershell_lateral_movement_invoke_psexec If you lost your implant from ws01, spawn a new one and perform token impersonation once again.

Detection here is a new service creation KQL: host.name : "sandbox-dc01.sandbox.pwnzone.lab" and winlog.channel : "System" and winlog.event_id : 7045 You should see a hit, be warned... it aint pretty :)

Last updated