GoFetch: The Self-Replicating PowerShell Worm That Turns BloodHound Graphs Into Automated AD Attacks
Hook
What if your penetration test could read a network graph and automatically hack its way to Domain Admin while you grabbed coffee? That's exactly what GoFetch does—and why it terrified defenders when revealed at BlackHat 2017.
Context
BloodHound revolutionized Active Directory security assessments when it launched in 2016. By modeling AD relationships as a graph database, it could instantly reveal hidden paths to Domain Admin privileges that would take human analysts weeks to discover. The problem? BloodHound only told you where to go—it didn't actually take you there. Penetration testers still needed to manually execute each step: dump credentials on Machine A, move laterally to Machine B, repeat until target compromised. This manual process was time-consuming, error-prone, and broke the momentum of security assessments.
GoFetch emerged from this gap between reconnaissance and exploitation. Presented at BlackHat Europe 2016 and refined for 2017, it answered a provocative question: could you feed BloodHound's JSON output directly into an automation engine that would execute the entire attack chain without human intervention? The result was a self-propagating PowerShell framework that reads attack graphs, pivots autonomously across network boundaries, harvests credentials at each hop, and recursively continues until reaching the target. It represented a significant escalation in offensive automation—turning what was essentially a mapping tool into a fully autonomous attack platform.
Technical Insight
GoFetch's architecture implements two distinct operational modes, but the recursive chain reaction mode (Invoke-GoFetch) reveals the most interesting technical decisions. The tool begins by parsing BloodHound's exported JSON attack path, which defines a sequence of machines and the relationships between them (local admin access, session tokens, group memberships). It then embeds the entire attack logic into a single PowerShell script that can self-replicate across network boundaries.
The core recursion pattern works like this: on each compromised machine, GoFetch loads a modified Invoke-Mimikatz DLL directly into memory (no disk writes), dumps credentials from LSASS, identifies which credentials grant access to the next hop in the BloodHound path, then uses Invoke-PsExec to copy itself to the target machine and execute. The recursion continues until the final objective is reached. Here's a simplified representation of the attack flow:
function Invoke-GoFetch {
param(
[string]$BloodHoundPath,
[string]$CurrentMachine,
[int]$HopNumber = 0
)
# Parse BloodHound JSON to determine next target
$NextTarget = Get-NextHopFromPath -Path $BloodHoundPath -Current $CurrentMachine
if ($NextTarget -eq $null) {
Write-Output "[+] Reached final objective!"
return
}
# Dump credentials using embedded Mimikatz
$Creds = Invoke-Mimikatz -DumpCreds
# Find credential that grants access to next hop
$ValidCred = $Creds | Where-Object {
Test-Access -Target $NextTarget.ComputerName -Credential $_
} | Select-Object -First 1
if ($ValidCred) {
Write-Output "[+] Hop $HopNumber : $CurrentMachine -> $($NextTarget.ComputerName)"
# Self-replicate to next machine and recurse
Invoke-PsExec -ComputerName $NextTarget.ComputerName `
-Credential $ValidCred `
-Command "powershell.exe -exec bypass -EncodedCommand $EncodedSelf"
# Recursion happens on the remote machine now
} else {
Write-Output "[-] Failed to find valid credential for next hop"
}
}
The truly clever (and dangerous) aspect is how GoFetch embeds itself for propagation. The entire script, including the Mimikatz DLL, gets base64-encoded and passed as a PowerShell encoded command. This means each hop receives a complete, self-contained attack package that can continue the chain independently. No file drops, no external dependencies beyond PowerShell v2 and .NET 3.5 (both present on Windows 7/Server 2008 R2 and later by default).
The alternative Python-based centralized mode takes a different approach. Instead of distributing the attack logic, it runs from a single control machine and orchestrates remote execution via WMI or PsExec. This mode parses the BloodHound CSV exports and maintains a state machine tracking which machines have been compromised and which credentials are available. It's less autonomous but provides better visibility and control:
def execute_attack_path(bloodhound_csv, target):
path = parse_bloodhound_path(bloodhound_csv, target)
credential_cache = []
for hop in path:
# Find usable credential from cache
cred = find_valid_credential(credential_cache, hop.target)
if not cred:
print(f"[-] Cannot proceed to {hop.target}")
break
# Execute Mimikatz remotely via WMI
new_creds = remote_mimikatz(hop.target, cred)
credential_cache.extend(new_creds)
print(f"[+] Compromised {hop.target}, harvested {len(new_creds)} credentials")
The Python mode sacrifices autonomy for observability—you can pause, inspect credential dumps, and manually adjust the attack path if BloodHound's data proves stale. The PowerShell mode, by contrast, is fire-and-forget: once launched, it propagates until completion or failure, with limited feedback channels.
Both modes rely on a critical assumption: that the BloodHound path remains valid between reconnaissance and exploitation. In practice, users log off, admin sessions end, and group memberships change. GoFetch includes basic error handling to try alternate paths when a hop fails, but it cannot dynamically re-run BloodHound analysis from within the target network. This makes timing crucial—the fresher the BloodHound data, the higher the success rate.
Gotcha
GoFetch's biggest limitation is that it's essentially a loud, obvious attack that security tools have been detecting since 2017. The combination of Mimikatz credential dumping (which triggers Sysmon Event ID 10 for LSASS access), PsExec lateral movement (Service Control Manager events), and PowerShell execution (Script Block Logging) creates a signature that any competent SOC will catch within minutes. Modern endpoint detection platforms have behavioral rules specifically tuned to detect this exact attack pattern. If you're testing against any environment with Windows 10/Server 2016 or later with default logging enabled, expect immediate detection.
The tool also hasn't been updated since 2017, meaning it predates critical Windows security improvements like Credential Guard, Windows Defender Application Control, and AMSI (Anti-Malware Scan Interface) integration in PowerShell. On modern systems, the embedded Mimikatz component will likely fail against protected LSASS memory, and the PowerShell payload may get blocked at the script level before execution. You'll also hit authentication failures on networks using newer security features like Protected Users group membership or authentication policies. BloodHound itself has evolved significantly—the newer BloodHound CE uses different data formats and relationship types that GoFetch's parser won't understand. Using this tool today requires working against legacy Windows environments or deliberately downgrading security controls, which defeats the purpose of realistic testing.
Verdict
Use if: You're conducting controlled Red Team exercises in lab environments to demonstrate the exploitability of BloodHound findings to stakeholders who need visual proof of risk, or you're a Blue Team member testing detection capabilities against known attack automation patterns. It's an excellent educational tool for understanding how graph-based reconnaissance translates into automated exploitation. Skip if: You need modern operational security for actual penetration tests (this tool will get you caught immediately), you're targeting Windows 10/Server 2016+ environments with security features enabled (it likely won't work), or you want maintained, supported offensive tooling (last commit was 2017). For practical AD exploitation automation today, integrate BloodHound findings into frameworks like Mythic, Covenant, or Cobalt Strike that offer proper OPSEC and active development. GoFetch's primary value in 2024 is defensive: understanding attack automation to build better detections, not executing it.