miércoles, 27 de mayo de 2026

WINDOWS. GPO. Usuario bloqueado email

Propósito:

Por diferentes motivos las cuentas de nuestros usuarios del AD se bloquean, ya sea de manera automática, por culpa del propio usuario o cualquier otra circunstancia.

Esto al final acaba en tiempo de gestión para los administradores y poca visibilidad e información para los usuarios.

El script que voy a pegar podría hacerse de muchas maneras, en las que aportarían más información, pero he querido que sea sencillo y que no necesite mirar el visor de eventos (que tendría que ir a cada uno de nuestros DC si no están centralizados) y sin crear ningún fichero en el disco.

Al final acaba enviando un email al usuario e informándole que ha sido bloqueado. Te puedes preguntar ¿cómo lo va a leer si tiene el usuario del AD bloqueado?.. buena pregunta. En mi caso las cuentas de correo no están gestionadas por el AD, en otras cuentas de proveedor pongo la externa (no tienen cuenta de correo interna), hay muchos casos.

Esta claro que para que funcione la cuenta del usuario del AD debe tener documentado el email. Otra opción es que siempre que se bloquee, se envíe email solo a IT o con copia a IT.

La idea es que solo envíe una alerta por hora, para no ser muy pesado. Con lo que es importante configurar bien el programador de tareas.

Esto va muy bien sobre todo con proveedores, se dejan sesiones abiertas en los servidores durante mucho tiempo y cuando van a conectar porque les necesitamos no pueden. Con esta medida les llega un email para que cierren las sesiones y eviten este tipo de bloqueo.

En mi caso a los proveedores les caduca la contraseña automáticamente cada 24h, así que si no cierran sesión de un día para otro, esto les supone un bloqueo ya que tienen la sesión abierta con una contraseña caducada.

El resultado sería el siguiente email:


Pasos

El Script :

# ============================================================

# SCRIPT: AD-Lockout-UserNotify.ps1

# ============================================================

# FUNCION:

# Detecta usuarios bloqueados en Active Directory usando

# el atributo lockoutTime, consultando directamente contra

# el PDC Emulator para garantizar consistencia.

#

# ============================================================


Import-Module ActiveDirectory


# ==========================

# VARIABLES DEL USUARIO

# ==========================

$SMTPServer = "smtp.MiDominio.com"

$SMTPPort   = 25

$From       = "Servodorenvio@MiDominio.com"


# ==========================

# OBTENCION DEL PDC

# ==========================

$domain = Get-ADDomain

$PDC = $domain.PDCEmulator


# ==========================

# CONFIGURACION

# ==========================

$now = Get-Date


$startWindow = $now.AddMinutes(-65)

$endWindow   = $now.AddMinutes(-2)


$lockoutDuration = 60


$startFileTime = $startWindow.ToFileTime()

$endFileTime   = $endWindow.ToFileTime()


# ==========================

# BUSQUEDA

# ==========================

$lockedUsers = Get-ADUser -Server $PDC `

-Filter {lockoutTime -gt 0} `

-Properties lockoutTime,mail,displayName,lastLogonDate,lastBadPasswordAttempt,passwordLastSet


foreach ($user in $lockedUsers) {


    if ($user.lockoutTime -gt 0 -and $user.mail) {


        $lockoutDate = [DateTime]::FromFileTime($user.lockoutTime)


        if ($user.lockoutTime -ge $startFileTime -and $user.lockoutTime -le $endFileTime) {


            if ($lockoutDate.AddMinutes($lockoutDuration) -gt $now) {


                # ==========================

                # CONVERSIONES SEGURAS

                # ==========================

                $LastLogon = if ($user.LastLogonDate) { $user.LastLogonDate } else { "No disponible" }

                $LastBadPwd = if ($user.LastBadPasswordAttempt) { $user.LastBadPasswordAttempt } else { "No disponible" }

                $PwdLastSet = if ($user.PasswordLastSet) { $user.PasswordLastSet } else { "No disponible" }


                $remaining = [math]::Round(($lockoutDate.AddMinutes($lockoutDuration) - $now).TotalMinutes)

                if ($remaining -lt 0) { $remaining = 0 }


                # ==========================

                # ASUNTO CORPORATIVO

                # ==========================

                $fecha = Get-Date -Format "yyyyMMdd - HH:mm"

                $Subject = "$fecha. SE. SEGURIDAD. Cuenta bloqueada: $($user.SamAccountName)"

$mail = $($user.mail)


                # ==========================

                # EMAIL HTML

                # ==========================

                $html = @"

<html>

<body style='font-family:Calibri;font-size:14px'>


<p>Hola $($user.displayName),</p>


<p><b>Su cuenta ha sido bloqueada por un proceso automatico.</b></p>


<p>Motivos habituales:</p>


<ul>

<li>Cambio de password reciente</li>

<li>Password caducado</li>

<li>Sesion activa con credenciales antiguas (muy probable)</li>

<li>Servicios o aplicaciones con password antiguo</li>

</ul>


<p>Revise los equipos donde haya iniciado sesion y vuelva a autenticarse.</p>


<p><b>Datos del bloqueo:</b></p>


<ul>

<li>Usuario: $($user.SamAccountName)</li>

<li>Fecha bloqueo: $lockoutDate</li>

<li>Tiempo restante bloqueo: $remaining minutos</li>

</ul>


<p><b>Informacion adicional:</b></p>


<ul>

<li>Ultimo logon: $LastLogon</li>

<li>Ultimo intento fallido: $LastBadPwd</li>

<li>Password cambiado: $PwdLastSet</li>

</ul>


<p>Contacte con IT de soporte@MiDominio.com si persiste.</p>


</body>

</html>

"@


                Send-MailMessage `

                -SmtpServer $SMTPServer `

                -Port $SMTPPort `

                -From $From `

                -To $mail `

                -Subject $Subject `

                -Body $html `

                -BodyAsHtml


            }

        }

    }

}


 


La tarea programada :




Probar con un usuario:


Para probar y ver pon pantalla y si hay algín caso

Import-Module ActiveDirectory

$pdc = (Get-ADDomain).PDCEmulator

$lockedUsers = Search-ADAccount -LockedOut -Server $pdc

if ($lockedUsers) {

    Write-Host "Hay usuarios bloqueados"

    $lockedUsers | ForEach-Object {
        Write-Host $_.SamAccountName
    }

} else {

    Write-Host "No hay usuarios bloqueados"

}


by GoN | Published: May 2026 | Last Updated:

martes, 26 de mayo de 2026

WINDOWS. GPO. Documento GPO-OU

Propósito:

Tengo que hacer limpieza de un montón de OUs, ya que he movido mis usuarios/Pcs a una estructura nueva. No me he llevado las mismas OUs, ni las mismas GPOs para no arrastrar la "suciedad" y partir de una estructura entendible y bien organizada.

El problema podría venir si me he dejado algo, con lo que antes de limpiar la estructura antigua he preferido hacer una exportación donde relaciono las OU con las GPO, por si necesito recrearlo.

Pasos

El resultado vendría ser un fichero html con el siguiente formato


La ejecución podría tardar un par de minutos, depende lo grande que sea la estructura de OUs y el número de GPOs.

El código es el siguiente:

Import-Module ActiveDirectory -ErrorAction Stop
Import-Module GroupPolicy -ErrorAction Stop

$fecha = Get-Date -Format "yyyyMMdd_HHmm"
$output = "AD_Informe_Completo_$fecha.html"

$html = @()

$html += @"
<html>
<head>
<title>Auditoria AD</title>
<style>
body { font-family: Arial; font-size: 12px; }
h1 { color:#2E86C1; }
h2 { color:#1F618D; }
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
th, td { border:1px solid #ccc; padding:6px; text-align:left; }
th { background-color:#f2f2f2; }

.directa { background-color:#D4EFDF; }
.heredada { background-color:#FDEBD0; }

.ok { color:green; font-weight:bold; }
.warn { color:red; font-weight:bold; }

.small { font-size: 11px; color: #555; }
</style>
</head>
<body>
"@

$html += "<h1>Auditoria OU y GPO</h1>"
$html += "<p>Fecha: $(Get-Date)</p>"

# Obtener todas las OUs
$ous = Get-ADOrganizationalUnit -Filter * | Sort-Object DistinguishedName

# Cache de links directos por OU
$linksPorOU = @{}

foreach ($ou in $ous) {
    try {
        $inh = Get-GPInheritance -Target $ou.DistinguishedName
        $linksPorOU[$ou.DistinguishedName] = $inh.GpoLinks
    } catch {
        $linksPorOU[$ou.DistinguishedName] = @()
    }
}

# Funcion para obtener padres de una OU
function Get-OUChain {
    param ($dn)

    $chain = @()
    $actual = $dn

    while ($actual -match "OU=") {
        $chain += $actual
        $actual = ($actual -split ',',2)[1]
    }

    # añadir dominio (final)
    if ($actual) { $chain += $actual }

    return $chain
}

foreach ($ou in $ous) {

    try {

        $html += "<h2>OU: $($ou.Name)</h2>"
        $html += "<p><b>DN:</b> $($ou.DistinguishedName)</p>"

        $inheritance = Get-GPInheritance -Target $ou.DistinguishedName

        if ($inheritance.GpoInheritanceBlocked) {
            $html += "<p class='warn'>Herencia bloqueada</p>"
        } else {
            $html += "<p class='ok'>Herencia activa</p>"
        }

        $chain = Get-OUChain $ou.DistinguishedName

        $html += "<table>"
        $html += "<tr>
                    <th>GPO</th>
                    <th>Enforced</th>
                    <th>Link Enabled</th>
                    <th>Origen</th>
                  </tr>"

        foreach ($gpo in $inheritance.InheritedGpoLinks) {

            $origen = "No localizado"
            $class = "heredada"

            # Buscar donde esta linkada realmente
            foreach ($nivel in $chain) {

                if ($linksPorOU.ContainsKey($nivel)) {

                    foreach ($link in $linksPorOU[$nivel]) {

                        if ($link.DisplayName -eq $gpo.DisplayName) {

                            if ($nivel -eq $ou.DistinguishedName) {
                                $origen = "Directa"
                                $class = "directa"
                            } else {
                                $origen = "Heredada desde: $nivel"
                                $class = "heredada"
                            }

                            break
                        }
                    }
                }

                if ($origen -ne "No localizado") { break }
            }

            $html += "<tr class='$class'>
                        <td>$($gpo.DisplayName)</td>
                        <td>$($gpo.Enforced)</td>
                        <td>$($gpo.Enabled)</td>
                        <td class='small'>$origen</td>
                      </tr>"
        }

        $html += "</table>"
        $html += "<hr>"

    }
    catch {
        $html += "<p class='warn'>Error en OU: $($ou.DistinguishedName)</p>"
    }
}

$html += "</body></html>"

$html | Out-File $output -Encoding UTF8

Write-Host "Informe generado: $output" -ForegroundColor Green


by GoN | Published: May 2026 | Last Updated:

jueves, 21 de mayo de 2026

WINDOWS. GPO. Asociar extensiones

Propósito:

Crear una GPO que asocie ciertas extensiones para que se abran con Notepad. Lo hago por motivos de seguridad: las extensiones son SCRIPTS/ejecutables y quiero impedir que puedan realizar acciones de forma sencilla.

Pasos

Antes de aplicar la GPO, me invento una extensión para ver como reacciona mi PC



Una vez ejecutada el resultado es evidente


La GPO puedo poner tantas asociaciones como quiera, aquí en el ejemplo se pueden ver un par:




by GoN | Published: May 2026 | Last Updated:

viernes, 15 de mayo de 2026

SEGURIDAD. Pentesting opciones


El propósito de este post es establecer una base en la que basarnos para pedir una valoración de pentesting para nuestro negocio. Expondré algunas opciones de las muchas posibles, resumidas de manera que pueda ser un buen punto de partida.

Todo va en base a lo que se quiere conocer, el diseño de nuestra infraestructura, nube o On Premise, AD/Linux, el presupuesto, etc... 

La idea sería la ampliación o combinación de varias.

Debería ir acompañado de un contexto de como es la empresa, que tenemos, hacia donde vamos...

Alla voy


Opción 1
Perímetro Exterior de Data Centers
Objetivo
Evaluar la exposición de sistemas no diseñados para ser públicos o con acceso restringido y detectar vectores de entrada críticos
Alcance
  • IPs públicas:
    • CPD en hosting
    • VPN
    • Backup
    • Infraestructura CPD on Premise
  • Servicios:
    • RDP, SSH, administración remota
    • APIs internas expuestas
    • servicios legacy
    • Servicios restringidos
    • FTP, SFTP, FTPS, SSH, Http, Https, etc...
 Pruebas a realizar
  • Escaneo completo de superficie externa
  • Identificación de servicios no publicados
  • Vulnerabilidades de servicios expuestos
  • Detección de accesos administrativos expuestos
  • Password spraying / brute force controlado
  • Banner grabbing / fingerprinting
  • TLS / certificados
Valor de negocio

Este escenario identificaríamos:
  • Accesos “ocultos” mal expuestos
  • Riesgo de acceso directo a infraestructura crítica
  • Posibles vectores de ransomware inicial




Opción 2
Perímetro Exterior Web
Objetivo
Evaluar la seguridad de todos los servicios públicos accesibles por clientes/usuarios
Alcance
  • Webs corporativas o más importantes
  • Portales
  • Aplicaciones SaaS / proveedor
  • APIs públicas
  • dominios principales y subdominios
Pruebas
  • OWASP Top 10 completo:
    • XSS, SQLi, SSRF, etc.
  • Enumeración de subdominios
  • Seguridad de autenticación
  • Gestión de sesiones
  • FTP, SFTP, FTPS, SSH, Http, Https, etc...
  • Seguridad de autenticación (MFA test)
  • APIs:
    • autenticación
    • exposición de datos
Valor de negocio
Riesgo directo:
  • Exposición de datos de clientes
  • Compromiso reputacional
  • punto de entrada externo más probable


Opción 3
Perímetro Interno (LAN corporativa)
Objetivo
Simular compromiso interno desde usuario legítimo
Escenario
  • Usuario de dominio estándar
  • Acceso desde:
    • red servidores
    • red sede
 Pruebas
  • Enumeración AD (BloodHound)
  • Escalado de privilegios
  • Movimiento lateral:
    • SMB, WinRM, RDP
  • Ataques Kerberos:
    • Kerberoasting
    • AS-REP Roasting
  • Simulación ransomware lateral
  • FTP, SFTP, FTPS, SSH, Http, Https, etc...
  • Identificación de “caminos a Domain Admin”
  • Revisión de permisos:
    • shares
    • GPO
    • ACLs
Valor de negocio
Responde a:
  • Saber si un usuario comprometer el dominio
  • Qué impacto y alcance tendría un phishing o cifrado exitoso



Opción 4
Perímetro Interno WiFi
Objetivo
Evaluar el acceso a la red desde redes inalámbricas, con un usuario válido y otro sin atenticar.
Alcance
  • WiFi corporativo
  • WiFi invitados
  • SSID internos
 Pruebas
  • Ataques WPA/WPA2/WPA3
  • Rogue AP
  • Captura handshakes
  • Intentos de pivot a LAN
  • Segmentación de red
  • Pruebas desde la calle / perímetro físico
  • Validación NAC / acceso 802.1X

Valor de negocio
Evalúa:
  • Acceso desde visitante o atacante físico
  • Segmentación real de red


Opción 5
Perímetro Correo
Objetivo
Evaluar la seguridad del sistema de correo como vector de ataque principal
Alcance
  • Infraestructura de email
  • Gateway  o reenviadores de correo
 Pruebas
  • Simulación phishing controlado
  • SPF / DKIM / DMARC
  • suplantación dominio
  • bypass de filtros
  • análisis de links y adjuntos
  • análisis de MFA bypass
 Valor de negocio
 Evitar ataques reales como:
  • robo de credenciales
  • malware
  • fraude o suplantación

Espero os haya sido útil

by GoN | Published: May 2026 | Last Updated: