lunes, 9 de febrero de 2026

WINDOWS. PS. PCs en la OU de computers

Propósito

Cuando añadimos un PC al dominio se va al contendor "Computers" que no es una OU a la que se puedan asignar GPOs.

Esto implica el riesgo que no se les apliquen ninguna GPO, con lo que hay esta atentos para moverlos a su OU correspondiente.

El script que aquí pondré lo ejecuto una vez al día (Tarea programada) y le envío copia al equipo de Help Desk. Básicamente mira si hay algún hosts en el contenedor de Computers y si lo hay envía un email informando.

No modifica nada, solo es informativa.

Pasos

Básicamente mira si hay algún hosts en el contenedor de Computers y si lo hay envía un email informando

 <#
   Busca equipos en el contenedor CN=Computers.
   Si hay equipos → envía correo con la tabla HTML en el CUERPO.
   Si no hay equipos → no envía correo.
#>

# ==========================
#  VARIABLES DEL USUARIO
# ==========================
$SMTPServer = "miservidor.midominio.com"
$SMTPPort   = 25
$From       = "Servidor1@midominio.com"
$To         = "itsecurity@midominio.com" , "HelpDesk@midominio.com",
$Subject    = "PCs en la Ou de Computers"

# Contenedor donde buscar (CORRECTO)
$SearchBase = "CN=Computers,DC=midominio,DC=com"

# ==========================
#  CARGAR MÓDULO AD
# ==========================
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
    Write-Error "El módulo ActiveDirectory no está disponible. Instala RSAT."
    exit 1
}
Import-Module ActiveDirectory

# ==========================
#  VALIDAR CONTENEDOR
# ==========================
try {
    $null = Get-ADObject -Identity $SearchBase -ErrorAction Stop
}
catch {
    Write-Error "ERROR: No se encontró el contenedor/OU: $SearchBase"
    exit 1
}

# ==========================
#  OBTENER EQUIPOS
# ==========================
$Computers = Get-ADComputer -SearchBase $SearchBase `
    -SearchScope OneLevel `
    -Filter * `
    -Properties Name, DNSHostName, Description, OperatingSystem, Enabled, LastLogonDate, whenCreated, whenChanged

$Count = $Computers.Count
Write-Host "Equipos encontrados: $Count"

if ($Count -eq 0) {
    Write-Host "Contenedor vacío. No se envía correo."
    exit 0
}

# ==========================
#  GENERAR HTML
# ==========================
$Sorted = $Computers | Sort-Object Name

$Table = $Sorted | Select-Object `
    @{n="Nombre (sAM)"; e={$_.Name}},
    @{n="Hostname (DNS)"; e={$_.DNSHostName}},
    @{n="Descripción"; e={ if ($_.Description) { $_.Description } else { "N/D" } }},
    @{n="SO"; e={$_.OperatingSystem}},
    @{n="Habilitado"; e={$_.Enabled}},
    @{n="Último logon"; e={ if ($_.LastLogonDate) { $_.LastLogonDate } else { "N/D" } }},
    @{n="Creado"; e={$_.whenCreated}},
    @{n="Modificado"; e={$_.whenChanged}} |
    ConvertTo-Html -As Table -Fragment

$Style = @"
<style>
body { font-family: Segoe UI, Arial; color:#222; }
table { border-collapse: collapse; width:100%; }
th, td { border: 1px solid #ccc; padding:6px; }
th { background:#f0f0f0; }
tr:nth-child(even) { background:#fafafa; }
.notice { font-weight:bold; color:#b00020; }
</style>
"@

$IntroText = "<p class='notice'>Estos PC deben moverse a la OU correspondiente</p>"
$Header    = "<h2>Equipos encontrados en: $SearchBase</h2>"
$BodyHtml  = ConvertTo-Html -Head $Style -Body "$IntroText $Header $Table"

# ==========================
#  ENVIAR CORREO
# ==========================
try {
    $Mail = New-Object System.Net.Mail.MailMessage
    $Mail.From = $From

    foreach ($recipient in $To) {
        if ($recipient.Trim()) { $Mail.To.Add($recipient.Trim()) }
    }

    $Mail.Subject   = $Subject + " (Total: $Count)"
    $Mail.IsBodyHtml = $true
    $Mail.Body      = $BodyHtml

    $SMTP = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort)
    $SMTP.Send($Mail)

    Write-Host "Correo enviado correctamente."
}
catch {
    Write-Error "Error enviando el correo: $($_.Exception.Message)"
    exit 1

}

El resultado vendría ser algo parecido a lo siguiente


Dificultad: Baja
Utilidad: Alta
by GoN | Published: Feb 2026 | Last Updated:

jueves, 22 de enero de 2026

WINDOWS. Seguridad. Consejo. Cuenta importante

 

En las propiedades de los usuarios podemos encontrar la siguiente característica “la cuenta es importante y no se puede delegar”


Por seguridad, se debería activar para todas las cuentas privilegiadas o que tengan permisos especiales, ya que: 

  • Una cuenta privilegiada NO es una cuenta personal
  • Delegar una cuenta privilegiada es abrir la puerta al desastre
  • Las cuentas privilegiadas deben usarse SOLO para tareas privilegiadas

Activar ese tip es una forma de reforzar una idea clave: Una cuenta privilegiada no es una herramienta compartida, es un activo crítico que requiere control absoluto, uso limitado y responsabilidad individual.


by GoN | Published: Jan 2026 | Last Updated:

miércoles, 21 de enero de 2026

WINDOWS. PS IOC


 Propósito

Dada una palabra o un ejecutable, buscar un IOC (Indicador de compromiso) en un Host.

Introduces las palabras clave y el script analiza:

  • Procesos
  • Servicios
  • Tareas programadas
  • Carpetas sospechosas
  • Claves RUN / RUNONCE
  • Servicios en el registro
  • Búsqueda de binarios en AppData / Temp
  • Exportación CSV
  • Programas instalado
  • Ejecución en RAM

·         Startup del usuario y del sistema


Pasos

Para ejecutar: powershell.exe -ExecutionPolicy Bypass -File .\BuscasIOC.ps1

<#

    Detect-Strings-Extended.ps1

    Autor: Guillermo

    Objetivo:

        Script INFORMÁTICO que analiza el equipo para encontrar cualquier rastro

        de cadenas sospechosas en:

 

            • Programas instalados

            • Procesos en RAM

            • Servicios

            • Tareas programadas

            • Carpetas sospechosas

            • Entradas en RUN / RUNONCE

            • Servicios en el Registro

            • Archivos en AppData / Temp

            • Programas configurados en el inicio de Windows

 

        NO modifica nada. NO detiene nada. NO elimina nada.

#>

 

Write-Host "======================================================" -ForegroundColor Cyan

Write-Host "   ANALIZADOR DE INDICADORES - MODO INFORMATIVO"       -ForegroundColor Cyan

Write-Host "======================================================" -ForegroundColor Cyan

 

# 1) Pedir cadenas al usuario

$input = Read-Host "Introduce las cadenas a buscar (separadas por coma). Ej: updater, malware, test"

$patterns = $input.Split(",") | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }

 

if ($patterns.Count -eq 0) {

    Write-Host "No se han introducido cadenas. Saliendo..." -ForegroundColor Red

    exit

}

 

Write-Host "`nCadenas a buscar:" -ForegroundColor Yellow

$patterns | ForEach-Object { Write-Host " - $_" -ForegroundColor Green }

 

# Convertir a regex OR

$regex = ($patterns -join "|")

 

$results = New-Object System.Collections.Generic.List[object]

 

function Add-Result($Tipo,$Ruta,$Detalle) {

    $results.Add([pscustomobject]@{

        Tipo    = $Tipo

        Ruta    = $Ruta

        Detalle = $Detalle

    })

}

 

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

# [0] PROGRAMAS INSTALADOS (Add/Remove Programs)

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

Write-Host "`n========== [0] PROGRAMAS INSTALADOS ==========" -ForegroundColor Cyan

 

$installedApps = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*, `

                                  HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*, `

                                  HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* `

                    -ErrorAction SilentlyContinue

 

foreach ($app in $installedApps) {

    if ($app.DisplayName -match $regex) {

        Add-Result "Programa Instalado" $app.DisplayName $app.DisplayVersion

        Write-Host "Programa detectado: $($app.DisplayName)" -ForegroundColor Green

    }

}

 

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

# [1] PROCESOS

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

Write-Host "`n========== [1] PROCESOS ==========" -ForegroundColor Cyan

Get-Process -ErrorAction SilentlyContinue |

    Where-Object { $_.Name -match $regex } |

    ForEach-Object {

        Add-Result "Proceso" ($_.Path) ($_.Name)

        $_

    }

 

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

# [2] SERVICIOS

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

Write-Host "`n========== [2] SERVICIOS ==========" -ForegroundColor Cyan

Get-Service -ErrorAction SilentlyContinue |

    Where-Object { $_.Name -match $regex -or $_.DisplayName -match $regex } |

    ForEach-Object {

        Add-Result "Servicio" $_.Name $_.DisplayName

        $_

    }

 

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

# [3] TAREAS PROGRAMADAS

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

Write-Host "`n========== [3] TAREAS PROGRAMADAS ==========" -ForegroundColor Cyan

Get-ScheduledTask -ErrorAction SilentlyContinue |

    Where-Object { $_.TaskName -match $regex -or $_.Description -match $regex } |

    ForEach-Object {

        Add-Result "Tarea Programada" $_.TaskName $_.Description

        $_

    }

 

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

# [4] CARPETAS HABITUALES

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

Write-Host "`n========== [4] CARPETAS HABITUALES ==========" -ForegroundColor Cyan

$paths = @(

    "$env:LOCALAPPDATA",

    "$env:LOCALAPPDATA\Programs",

    "$env:APPDATA",

    "$env:TEMP",

    "$env:ProgramFiles",

    "$env:ProgramFiles(x86)"

)

 

foreach ($base in $paths) {

    if (Test-Path $base) {

        Get-ChildItem $base -Recurse -ErrorAction SilentlyContinue |

            Where-Object { $_.Name -match $regex } |

            ForEach-Object {

                Add-Result "Archivo/Carpeta" $_.FullName "Coincidencia en nombre"

                Write-Host "Encontrado: $($_.FullName)" -ForegroundColor Green

            }

    }

}

 

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

# [5] RUN / RUNONCE

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

Write-Host "`n========== [5] RUN / RUNONCE ==========" -ForegroundColor Cyan

$runKeys = @(

    "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run",

    "HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce",

    "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run",

    "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"

)

 

foreach ($rk in $runKeys) {

    if (Test-Path $rk) {

        $vals = Get-ItemProperty $rk -ErrorAction SilentlyContinue

        foreach ($prop in $vals.PSObject.Properties) {

            if ($prop.Name -notlike "PS*" -and (

                $prop.Name -match $regex -or ($prop.Value -as [string]) -match $regex

            )) {

                Add-Result "RunKey" $rk "$($prop.Name) = $($prop.Value)"

                Write-Host "[RUN] $rk → $($prop.Name) = $($prop.Value)" -ForegroundColor Green

            }

        }

    }

}

 

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

# [6] SERVICIOS DEL REGISTRO

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

Write-Host "`n========== [6] SERVICIOS DEL REGISTRO ==========" -ForegroundColor Cyan

Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Services" -ErrorAction SilentlyContinue |

    Where-Object { $_.Name -match $regex } |

    ForEach-Object {

        Add-Result "RegistroServicio" $_.Name "Coincidencia en nombre de clave"

        $_.Name

    }

 

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

# [7] INICIO DE WINDOWS (Startup)

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

Write-Host "`n========== [7] PROGRAMAS EN EL INICIO ==========" -ForegroundColor Cyan

 

$startupPaths = @(

    "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup",

    "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\Startup"

)

 

foreach ($sp in $startupPaths) {

    if (Test-Path $sp) {

        Get-ChildItem $sp -ErrorAction SilentlyContinue |

            Where-Object { $_.Name -match $regex } |

            ForEach-Object {

                Add-Result "Inicio Windows" $_.FullName "Coincidencia en acceso directo"

                Write-Host "En inicio: $($_.FullName)" -ForegroundColor Yellow

            }

    }

}

 

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

# [8] BINARIOS EN APPDATA / TEMP

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

Write-Host "`n========== [8] BINARIOS APPDATA / TEMP ==========" -ForegroundColor Cyan

$binroots = @("$env:LOCALAPPDATA","$env:APPDATA","$env:TEMP")

 

foreach ($br in $binroots) {

    if (Test-Path $br) {

        Get-ChildItem $br -Recurse -ErrorAction SilentlyContinue |

            Where-Object { $_.Name -match $regex } |

            ForEach-Object {

                Add-Result "Binario" $_.FullName "Coincidencia en nombre"

                Write-Host "Binario detectado: $($_.FullName)" -ForegroundColor Green

            }

    }

}

 

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

# RESUMEN + EXPORTACIÓN

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

 

Write-Host "`n==============================================" -ForegroundColor Cyan

Write-Host "                 RESUMEN" -ForegroundColor Cyan

Write-Host "==============================================" -ForegroundColor Cyan

 

$results | Format-Table -AutoSize

 

# Nombre con fecha y hora

$timestamp = (Get-Date).ToString("yyyy-MM-dd_HH-mm-ss")

$output = "C:\Temp\Deteccion-Resultado_$timestamp.csv"

 

New-Item -ItemType Directory -Path (Split-Path $output) -Force | Out-Null

$results | Export-Csv -Path $output -NoTypeInformation -Encoding UTF8

 

Write-Host "`nArchivo exportado: $output" -ForegroundColor Yellow

Write-Host "`n--- ANÁLISIS FINALIZADO ---" -ForegroundColor Cyan

 






by GoN | Published: Jan 2026 | Last Updated: