Refactor(ADN): Migración de tools/adn a adn/tools en busca de la Armonía Integral del DIIAA

This commit is contained in:
Ricardo Monla
2026-03-10 22:25:01 -03:00
parent 174ef7d53c
commit 78006032eb
69 changed files with 432 additions and 671 deletions
+60
View File
@@ -0,0 +1,60 @@
# 🧟 W-Zombi: Toolkit de Inyección Lateral para Windows VMs
> Técnica documentada en la bitácora del [25/02/2026](../../bitacoras/2026-02-25.md) (Hito DB01).
## Problema
La consola HTML5/VNC de Proxmox **no soporta copiar/pegar** texto. Esto impide provisionar comandos complejos en VMs Windows recién creadas que aún no tienen SSH.
## Solución
Arquitectura C2 liviana que sortea la limitación:
```
srv-ns8 (10.0.10.8) VM Windows (VNC)
┌──────────────────────┐ ┌──────────────────────┐
│ servidor.rb │ ← HTTP :8000 ── │ zombi.ps1 (loop) │
│ (Ruby / WEBrick) │ │ Consulta activo.ps1 │
│ │ ── payload ────→ │ cada 10 seg │
│ payloads/activo.ps1 │ │ Ejecuta y reporta │
│ (hot-swap por IA) │ ← POST /log ── │ telemetría al server │
└──────────────────────┘ └──────────────────────┘
```
## Uso
### 1. En srv-ns8 (la IA o el operador):
```bash
ruby tools/w-zombi/servidor.rb
```
### 2. En la VM Windows (tipear manualmente en VNC — PowerShell Admin):
```powershell
iwr 10.0.10.8:8000/zombi.ps1 -useb|iex
```
### 3. Cambiar payload activo:
```bash
# Copiar un payload pre-armado
cp tools/w-zombi/payloads/install_ssh.ps1 tools/w-zombi/payloads/activo.ps1
# O la IA modifica activo.ps1 directamente
```
## Estructura
```
tools/w-zombi/
├── servidor.rb ← Servidor HTTP Ruby (WEBrick)
├── README.md ← Este archivo
└── payloads/
├── activo.ps1 ← Payload que ejecuta la VM (hot-swap)
├── install_ssh.ps1 ← Instalar OpenSSH Server nativo
└── sonda.ps1 ← Diagnóstico y relevamiento del sistema
```
## Seguridad
- Servidor **efímero** — solo se levanta durante la operación.
- Solo escucha en la red interna (`10.0.10.x` / `10.0.100.x`).
- Una vez instalado SSH, este mecanismo deja de ser necesario.
+1
View File
@@ -0,0 +1 @@
OpenSSH-Win64.zip
+115
View File
@@ -0,0 +1,115 @@
$ErrorActionPreference = "Continue"
$ZOMBI_URL = "http://10.0.10.8:8000"
function Log($msg) {
$ts = Get-Date -Format "HH:mm:ss"
Write-Host "[$ts] $msg" -ForegroundColor Cyan
try { Invoke-RestMethod -Uri "$ZOMBI_URL/log" -Method Post -Body @{msg="[$env:COMPUTERNAME] $msg"} -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null } catch {}
}
$DEST = "C:\SysDasuten"
$ZIP = "C:\TEMP\runSysDasuten.zip"
$SQL_SERVER = "sql-dasuten"
$SQL_DB = "sysdasuten"
Log "=========================================="
Log "DEPLOY SYSDASUTEN — $env:COMPUTERNAME"
Log "=========================================="
# --- PASO 1: Descargar ZIP ---
if (-not (Test-Path $DEST\Sistema\DasutenSQL.exe)) {
Log "PASO 1: Descargando runSysDasuten.zip (~268 MB)..."
New-Item -ItemType Directory -Path "C:\TEMP" -Force | Out-Null
try {
Invoke-WebRequest -Uri "$ZOMBI_URL/payloads/runSysDasuten.zip" -OutFile $ZIP -UseBasicParsing -ErrorAction Stop
Log "Descarga completada: $(((Get-Item $ZIP).Length / 1MB).ToString('N0')) MB"
} catch {
Log "ERROR descarga: $($_.Exception.Message)"
return
}
# --- PASO 2: Extraer ---
Log "PASO 2: Extrayendo a $DEST..."
New-Item -ItemType Directory -Path $DEST -Force | Out-Null
Expand-Archive -Path $ZIP -DestinationPath "C:\" -Force
# El zip contiene runSysDasuten/ como raiz, renombrar
if (Test-Path "C:\runSysDasuten") {
Copy-Item -Path "C:\runSysDasuten\*" -Destination $DEST -Recurse -Force
Remove-Item "C:\runSysDasuten" -Recurse -Force
}
Remove-Item $ZIP -Force -ErrorAction SilentlyContinue
Log "Extraido OK."
} else {
Log "PASO 1-2: SKIP — Ya existe $DEST\Sistema\DasutenSQL.exe"
}
# --- PASO 3: Modificar Kermet.ini ---
Log "PASO 3: Configurando Kermet.ini..."
$iniPaths = @("$DEST\Kermet.ini", "$DEST\Sistema\Kermet.ini")
foreach ($ini in $iniPaths) {
if (Test-Path $ini) {
$content = Get-Content $ini -Raw
$content = $content -replace 'SERVER=srvFENIX', "SERVER=$SQL_SERVER"
$content = $content -replace 'SERVER=172\.16\.9\.204\\SQL2017', "SERVER=$SQL_SERVER"
Set-Content $ini $content -Force
Log "Actualizado: $ini -> SERVER=$SQL_SERVER"
}
}
# --- PASO 4: Instalar Fuentes ---
Log "PASO 4: Instalando fuentes..."
$fontDir = "$DEST\Fonts\Fonts"
if (Test-Path $fontDir) {
$shell = New-Object -ComObject Shell.Application
$fontsFolder = $shell.NameSpace(0x14) # Windows Fonts folder
Get-ChildItem "$fontDir\*.ttf" | ForEach-Object {
$fontPath = $_.FullName
$fontName = $_.Name
if (-not (Test-Path "C:\Windows\Fonts\$fontName")) {
Copy-Item $fontPath "C:\Windows\Fonts\" -Force
$regKey = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
New-ItemProperty -Path $regKey -Name $fontName -Value $fontName -PropertyType String -Force | Out-Null
Log "Fuente instalada: $fontName"
} else {
Log "Fuente ya existe: $fontName"
}
}
} else {
Log "WARN: No se encontro directorio de fuentes"
}
# --- PASO 5: Verificar firewall SQL en sql-dasuten ---
Log "PASO 5: Probando conectividad SQL..."
$tcpTest = Test-NetConnection -ComputerName $SQL_SERVER -Port 1433 -WarningAction SilentlyContinue
if ($tcpTest.TcpTestSucceeded) {
Log "Conexion TCP a ${SQL_SERVER}:1433 OK"
} else {
Log "WARN: No se puede conectar a ${SQL_SERVER}:1433 — verificar firewall"
}
# --- PASO 6: Probar conexion SQL con sqlcmd (si existe) ---
$sqlcmd = Get-Command sqlcmd -ErrorAction SilentlyContinue
if ($sqlcmd) {
Log "PASO 6: Probando SQL con sqlcmd..."
$result = sqlcmd -S $SQL_SERVER -E -Q "SELECT DB_NAME() AS db_actual; SELECT name FROM sys.databases WHERE name = '$SQL_DB'" -W 2>&1
Log "SQL Result: $result"
} else {
Log "PASO 6: SKIP — sqlcmd no disponible (normal en Win10), probar con DasutenSQL.exe"
}
# --- PASO 7: Crear acceso directo ---
Log "PASO 7: Creando acceso directo en Escritorio..."
$desktopPath = [Environment]::GetFolderPath('CommonDesktopDirectory')
$shortcutPath = "$desktopPath\DasutenSQL.lnk"
$WshShell = New-Object -ComObject WScript.Shell
$shortcut = $WshShell.CreateShortcut($shortcutPath)
$shortcut.TargetPath = "$DEST\Sistema\DasutenSQL.exe"
$shortcut.WorkingDirectory = "$DEST\Sistema"
$shortcut.Description = "Sistema DASUTEN"
$shortcut.Save()
Log "Acceso directo creado: $shortcutPath"
Log "=========================================="
Log "DEPLOY SYSDASUTEN COMPLETADO"
Log "Ejecutar desde: $DEST\Sistema\DasutenSQL.exe"
Log "=========================================="
@@ -0,0 +1,115 @@
$ErrorActionPreference = "Continue"
$ZOMBI_URL = "http://10.0.10.8:8000"
function Log($msg) {
$ts = Get-Date -Format "HH:mm:ss"
Write-Host "[$ts] $msg" -ForegroundColor Cyan
try { Invoke-RestMethod -Uri "$ZOMBI_URL/log" -Method Post -Body @{msg="[$env:COMPUTERNAME] $msg"} -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null } catch {}
}
$DEST = "C:\SysDasuten"
$ZIP = "C:\TEMP\runSysDasuten.zip"
$SQL_SERVER = "sql-dasuten"
$SQL_DB = "sysdasuten"
Log "=========================================="
Log "DEPLOY SYSDASUTEN — $env:COMPUTERNAME"
Log "=========================================="
# --- PASO 1: Descargar ZIP ---
if (-not (Test-Path $DEST\Sistema\DasutenSQL.exe)) {
Log "PASO 1: Descargando runSysDasuten.zip (~268 MB)..."
New-Item -ItemType Directory -Path "C:\TEMP" -Force | Out-Null
try {
Invoke-WebRequest -Uri "$ZOMBI_URL/payloads/runSysDasuten.zip" -OutFile $ZIP -UseBasicParsing -ErrorAction Stop
Log "Descarga completada: $(((Get-Item $ZIP).Length / 1MB).ToString('N0')) MB"
} catch {
Log "ERROR descarga: $($_.Exception.Message)"
return
}
# --- PASO 2: Extraer ---
Log "PASO 2: Extrayendo a $DEST..."
New-Item -ItemType Directory -Path $DEST -Force | Out-Null
Expand-Archive -Path $ZIP -DestinationPath "C:\" -Force
# El zip contiene runSysDasuten/ como raiz, renombrar
if (Test-Path "C:\runSysDasuten") {
Copy-Item -Path "C:\runSysDasuten\*" -Destination $DEST -Recurse -Force
Remove-Item "C:\runSysDasuten" -Recurse -Force
}
Remove-Item $ZIP -Force -ErrorAction SilentlyContinue
Log "Extraido OK."
} else {
Log "PASO 1-2: SKIP — Ya existe $DEST\Sistema\DasutenSQL.exe"
}
# --- PASO 3: Modificar Kermet.ini ---
Log "PASO 3: Configurando Kermet.ini..."
$iniPaths = @("$DEST\Kermet.ini", "$DEST\Sistema\Kermet.ini")
foreach ($ini in $iniPaths) {
if (Test-Path $ini) {
$content = Get-Content $ini -Raw
$content = $content -replace 'SERVER=srvFENIX', "SERVER=$SQL_SERVER"
$content = $content -replace 'SERVER=172\.16\.9\.204\\SQL2017', "SERVER=$SQL_SERVER"
Set-Content $ini $content -Force
Log "Actualizado: $ini -> SERVER=$SQL_SERVER"
}
}
# --- PASO 4: Instalar Fuentes ---
Log "PASO 4: Instalando fuentes..."
$fontDir = "$DEST\Fonts\Fonts"
if (Test-Path $fontDir) {
$shell = New-Object -ComObject Shell.Application
$fontsFolder = $shell.NameSpace(0x14) # Windows Fonts folder
Get-ChildItem "$fontDir\*.ttf" | ForEach-Object {
$fontPath = $_.FullName
$fontName = $_.Name
if (-not (Test-Path "C:\Windows\Fonts\$fontName")) {
Copy-Item $fontPath "C:\Windows\Fonts\" -Force
$regKey = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
New-ItemProperty -Path $regKey -Name $fontName -Value $fontName -PropertyType String -Force | Out-Null
Log "Fuente instalada: $fontName"
} else {
Log "Fuente ya existe: $fontName"
}
}
} else {
Log "WARN: No se encontro directorio de fuentes"
}
# --- PASO 5: Verificar firewall SQL en sql-dasuten ---
Log "PASO 5: Probando conectividad SQL..."
$tcpTest = Test-NetConnection -ComputerName $SQL_SERVER -Port 1433 -WarningAction SilentlyContinue
if ($tcpTest.TcpTestSucceeded) {
Log "Conexion TCP a ${SQL_SERVER}:1433 OK"
} else {
Log "WARN: No se puede conectar a ${SQL_SERVER}:1433 — verificar firewall"
}
# --- PASO 6: Probar conexion SQL con sqlcmd (si existe) ---
$sqlcmd = Get-Command sqlcmd -ErrorAction SilentlyContinue
if ($sqlcmd) {
Log "PASO 6: Probando SQL con sqlcmd..."
$result = sqlcmd -S $SQL_SERVER -E -Q "SELECT DB_NAME() AS db_actual; SELECT name FROM sys.databases WHERE name = '$SQL_DB'" -W 2>&1
Log "SQL Result: $result"
} else {
Log "PASO 6: SKIP — sqlcmd no disponible (normal en Win10), probar con DasutenSQL.exe"
}
# --- PASO 7: Crear acceso directo ---
Log "PASO 7: Creando acceso directo en Escritorio..."
$desktopPath = [Environment]::GetFolderPath('CommonDesktopDirectory')
$shortcutPath = "$desktopPath\DasutenSQL.lnk"
$WshShell = New-Object -ComObject WScript.Shell
$shortcut = $WshShell.CreateShortcut($shortcutPath)
$shortcut.TargetPath = "$DEST\Sistema\DasutenSQL.exe"
$shortcut.WorkingDirectory = "$DEST\Sistema"
$shortcut.Description = "Sistema DASUTEN"
$shortcut.Save()
Log "Acceso directo creado: $shortcutPath"
Log "=========================================="
Log "DEPLOY SYSDASUTEN COMPLETADO"
Log "Ejecutar desde: $DEST\Sistema\DasutenSQL.exe"
Log "=========================================="
+40
View File
@@ -0,0 +1,40 @@
function Log-Msg {
param([string]$Message)
Write-Host $Message -ForegroundColor Cyan
try {
Invoke-RestMethod -Uri "http://10.0.10.8:8000/log" -Method Post -Body @{msg=$Message} -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null
} catch {}
}
Log-Msg "=========================================================="
Log-Msg "INSTALANDO Y HABILITANDO OPENSSH SERVER NATIVO"
Log-Msg "=========================================================="
try {
$sshCheck = Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*'
if ($sshCheck.State -ne 'Installed') {
Log-Msg "Instalando caracteristica OpenSSH.Server (esto puede tomar un minuto)..."
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 | Out-Null
} else {
Log-Msg "OpenSSH.Server ya se encuentra instalado."
}
Log-Msg "Configurando servicio sshd en Inicio Automatico..."
Set-Service -Name sshd -StartupType 'Automatic'
Start-Service sshd -ErrorAction SilentlyContinue
if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue)) {
Log-Msg "Abriendo puerto TCP 22 en el Firewall local..."
New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 | Out-Null
}
$ip = (Get-NetIPAddress -AddressFamily IPv4 | Where-Object InterfaceAlias -notmatch 'Loopback').IPAddress
Log-Msg "OpenSSH Server instalado y corriendo en el puerto 22."
Log-Msg "Conectar via: ssh Administrador@$($ip[0])"
} catch {
Log-Msg "Fallo al instalar SSH: $_"
}
Log-Msg "=========================================================="
Log-Msg "OPERACION COMPLETADA. Ahora SSH esta disponible."
Log-Msg "=========================================================="
return
@@ -0,0 +1,49 @@
function Log-Msg {
param([string]$Message)
Write-Host $Message -ForegroundColor Cyan
try {
Invoke-RestMethod -Uri "http://10.0.10.8:8000/log" -Method Post -Body @{msg=$Message} -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null
} catch {}
}
Log-Msg "=========================================================="
Log-Msg "INSTALANDO OPENSSH (con fix TEMP)"
Log-Msg "=========================================================="
try {
# Fix TEMP: usar carpeta de sistema en vez de la del usuario
$env:TEMP = "C:\Windows\Temp"
$env:TMP = "C:\Windows\Temp"
Log-Msg "TEMP redirigido a C:\Windows\Temp"
$cap = Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*'
if ($cap.State -eq 'Installed') {
Log-Msg "OpenSSH Server ya instalado."
} else {
Log-Msg "Instalando OpenSSH.Server..."
Add-WindowsCapability -Online -Name $cap.Name
Log-Msg "Instalado OK."
}
Set-Service sshd -StartupType Automatic
Start-Service sshd -ErrorAction SilentlyContinue
Log-Msg "Servicio sshd configurado e iniciado."
# Puerto 7022
$cfg = "C:\ProgramData\ssh\sshd_config"
$lines = Get-Content $cfg | Where-Object { $_ -notmatch '^\s*#?\s*Port\s+\d+' }
@("Port 7022") + $lines | Set-Content $cfg -Force
New-NetFirewallRule -Name 'SSH-7022' -DisplayName 'SSH 7022' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 7022 -ErrorAction SilentlyContinue | Out-Null
Stop-Service sshd -Force
Start-Sleep -Seconds 2
Start-Service sshd
Log-Msg "OpenSSH configurado en puerto 7022"
} catch {
Log-Msg "ERROR: $($_.Exception.Message)"
}
Log-Msg "=========================================================="
Log-Msg "OPERACION COMPLETADA"
Log-Msg "=========================================================="
@@ -0,0 +1,83 @@
$ErrorActionPreference = "Continue"
$logFile = "C:\ssh_install.log"
function Log($msg) {
$ts = Get-Date -Format "HH:mm:ss"
$line = "[$ts] $msg"
Write-Host $line -ForegroundColor Cyan
Add-Content -Path $logFile -Value $line
try { (New-Object Net.WebClient).DownloadString("http://10.0.10.8:8000/log?msg=$msg") | Out-Null } catch {}
}
Log "=========================================="
Log "OPENSSH LEGACY INSTALLER (2008R2+)"
Log "=========================================="
$sshDir = "C:\OpenSSH-Win64"
$zipPath = "C:\OpenSSH-Win64.zip"
$sshUrl = "http://10.0.10.8:8000/payloads/OpenSSH-Win64.zip"
try {
if (Test-Path "$sshDir\sshd.exe") {
Log "OpenSSH ya instalado en $sshDir"
} else {
Log "Paso 1: Descargando ZIP..."
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($sshUrl, $zipPath)
Log "Descarga OK ($zipPath)"
Log "Paso 2: Extrayendo ZIP..."
$shell = New-Object -ComObject Shell.Application
$zip = $shell.NameSpace($zipPath)
$dest = $shell.NameSpace("C:\")
$dest.CopyHere($zip.Items(), 0x14)
Log "Extraido en $sshDir"
}
Log "Paso 3: Instalando servicio sshd..."
cd $sshDir
powershell -ExecutionPolicy Bypass -File "$sshDir\install-sshd.ps1"
Log "Servicio instalado"
Log "Paso 4: Generando host keys..."
if (!(Test-Path "$sshDir\ssh_host_rsa_key")) {
& "$sshDir\ssh-keygen.exe" -A
Log "Host keys generadas"
} else {
Log "Host keys ya existen"
}
Log "Paso 5: Configurando puerto 7022..."
$cfg = "$sshDir\sshd_config"
if (Test-Path "$sshDir\sshd_config_default") {
Copy-Item "$sshDir\sshd_config_default" $cfg -Force
}
if (Test-Path $cfg) {
$lines = Get-Content $cfg | Where-Object { $_ -notmatch '^\s*#?\s*Port\s+\d+' }
$newContent = @("Port 7022") + $lines
$newContent | Set-Content $cfg -Force
} else {
"Port 7022" | Set-Content $cfg
}
Log "Config: Port 7022"
Log "Paso 6: Firewall..."
netsh advfirewall firewall add rule name="SSH-7022" dir=in action=allow protocol=TCP localport=7022
Log "Firewall actualizado"
Log "Paso 7: Iniciando servicio..."
Set-Service sshd -StartupType Automatic -ErrorAction SilentlyContinue
Start-Service sshd -ErrorAction Stop
Log "Servicio sshd INICIADO en puerto 7022"
} catch {
Log "ERROR: $($_.Exception.Message)"
Log "LINEA: $($_.InvocationInfo.ScriptLineNumber)"
}
Log "=========================================="
Log "FIN. Log guardado en $logFile"
Log "=========================================="
Write-Host ""
Write-Host "Presiona ENTER para cerrar..." -ForegroundColor Yellow
Read-Host
+106
View File
@@ -0,0 +1,106 @@
$ErrorActionPreference = "Continue"
$env:TEMP = "C:\Windows\Temp"
$env:TMP = "C:\Windows\Temp"
function Log($msg) {
$ts = Get-Date -Format "HH:mm:ss"
Write-Host "[$ts] $msg" -ForegroundColor Cyan
try { Invoke-RestMethod -Uri "http://10.0.10.8:8000/log" -Method Post -Body @{msg="[$env:COMPUTERNAME] $msg"} -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null } catch {}
}
$hn = $env:COMPUTERNAME
$os = (Get-WmiObject Win32_OperatingSystem).Caption
$sshPort = 7022
Log "=========================================="
Log "W-ZOMBI SETUP — $hn"
Log "OS: $os"
Log "=========================================="
# --- PASO 1: Detectar e instalar OpenSSH ---
$sshInstalled = $false
# Verificar si sshd ya existe
if (Get-Service sshd -ErrorAction SilentlyContinue) {
Log "PASO 1: sshd ya existe."
$sshInstalled = $true
} elseif ($os -match "200[38]|Vista|XP") {
# Legacy: Win32-OpenSSH manual
Log "PASO 1: OS Legacy ($os) — instalando Win32-OpenSSH..."
$zip = "C:\OpenSSH-Win64.zip"
$dir = "C:\OpenSSH-Win64"
try {
(New-Object Net.WebClient).DownloadFile("http://10.0.10.8:8000/payloads/OpenSSH-Win64.zip", $zip)
$shell = New-Object -ComObject Shell.Application
$shell.NameSpace("C:\").CopyHere($shell.NameSpace($zip).Items(), 0x14)
& "$dir\install-sshd.ps1" 2>&1 | Out-Null
& "$dir\ssh-keygen.exe" -A 2>&1 | Out-Null
Set-Service sshd -StartupType Automatic
$sshInstalled = $true
Log "Win32-OpenSSH instalado OK."
} catch {
Log "ERROR Legacy: $($_.Exception.Message)"
}
} else {
# Moderno: Add-WindowsCapability (Win10/2016+)
Log "PASO 1: OS Moderno — instalando via Add-WindowsCapability..."
try {
$cap = Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*'
if ($cap.State -ne 'Installed') {
Add-WindowsCapability -Online -Name $cap.Name | Out-Null
}
Set-Service sshd -StartupType Automatic
Start-Service sshd -ErrorAction SilentlyContinue
$sshInstalled = $true
Log "OpenSSH Server instalado OK."
} catch {
Log "ERROR Moderno: $($_.Exception.Message)"
}
}
if (-not $sshInstalled) {
Log "FALLO: No se pudo instalar SSH. Abortando."
return
}
# --- PASO 2: Configurar puerto 7022 ---
Log "PASO 2: Configurando puerto $sshPort..."
$cfgPaths = @("C:\ProgramData\ssh\sshd_config", "C:\OpenSSH-Win64\sshd_config")
$cfgPath = $cfgPaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($cfgPath) {
$lines = Get-Content $cfgPath | Where-Object { $_ -notmatch '^\s*#?\s*Port\s+\d+' }
@("Port $sshPort") + $lines | Set-Content $cfgPath -Force
Log "Config: $cfgPath -> Port $sshPort"
} else {
Log "WARN: No se encontro sshd_config"
}
# --- PASO 3: Firewall ---
Log "PASO 3: Firewall..."
try {
New-NetFirewallRule -Name "SSH-$sshPort" -DisplayName "SSH $sshPort" -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort $sshPort -ErrorAction SilentlyContinue | Out-Null
} catch {
netsh advfirewall firewall add rule name="SSH-$sshPort" dir=in action=allow protocol=TCP localport=$sshPort 2>&1 | Out-Null
}
Log "Firewall: puerto $sshPort abierto"
# --- PASO 4: Reiniciar sshd ---
Log "PASO 4: Reiniciando sshd..."
Stop-Service sshd -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
Start-Service sshd -ErrorAction SilentlyContinue
Log "sshd reiniciado."
# --- PASO 5: Verificar ---
$listening = netstat -an | Select-String ":$sshPort\s+.*LISTEN"
if ($listening) {
Log "VERIFICADO: $hn escuchando en puerto $sshPort"
} else {
Log "WARN: Puerto $sshPort no detectado en netstat"
}
Log "=========================================="
Log "SETUP COMPLETADO — $hn : $sshPort"
Log "=========================================="
+48
View File
@@ -0,0 +1,48 @@
function Log-Msg {
param([string]$Message)
Write-Host $Message -ForegroundColor Cyan
try {
Invoke-RestMethod -Uri "http://10.0.10.8:8000/log" -Method Post -Body @{msg=$Message} -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null
} catch {}
}
Log-Msg "=========================================================="
Log-Msg "SONDA DE DIAGNOSTICO — RELEVAMIENTO DEL SISTEMA"
Log-Msg "=========================================================="
Log-Msg "Hostname: $env:COMPUTERNAME"
Log-Msg "OS: $((Get-CimInstance Win32_OperatingSystem).Caption)"
Log-Msg "Arquitectura: $((Get-CimInstance Win32_OperatingSystem).OSArchitecture)"
# Red
$ips = Get-NetIPAddress -AddressFamily IPv4 | Where-Object InterfaceAlias -notmatch 'Loopback'
foreach ($ip in $ips) {
Log-Msg "Red [$($ip.InterfaceAlias)]: $($ip.IPAddress)/$($ip.PrefixLength)"
}
# Dominio
try {
$domain = (Get-CimInstance Win32_ComputerSystem).Domain
$inDomain = (Get-CimInstance Win32_ComputerSystem).PartOfDomain
Log-Msg "Dominio: $domain (Unido: $inDomain)"
} catch {
Log-Msg "Dominio: No disponible"
}
# Servicios clave
$servicios = @('sshd', 'MSSQLSERVER', 'NTDS', 'DNS')
foreach ($svc in $servicios) {
$s = Get-Service -Name $svc -ErrorAction SilentlyContinue
if ($s) {
Log-Msg "Servicio [$svc]: $($s.Status)"
}
}
# Firewall SSH
$rule = Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue
Log-Msg "Firewall SSH (TCP 22): $(if ($rule) { 'Habilitado' } else { 'No configurado' })"
Log-Msg "=========================================================="
Log-Msg "SONDA COMPLETADA."
Log-Msg "=========================================================="
exit
@@ -0,0 +1,42 @@
$ErrorActionPreference = "Continue"
function Log($msg) {
$ts = Get-Date -Format "HH:mm:ss"
Write-Host "[$ts] $msg" -ForegroundColor Cyan
try { Invoke-RestMethod -Uri "http://10.0.10.8:8000/log" -Method Post -Body @{msg="[$env:COMPUTERNAME] $msg"} -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null } catch {}
}
Log "=========================================="
Log "SQL SYSADMIN FIX — Agregando DASUTEN\admindasu"
Log "=========================================="
# Paso 1: Parar SQL Server
Log "PASO 1: Deteniendo MSSQLSERVER..."
net stop MSSQLSERVER /y 2>&1 | Out-Null
Start-Sleep -Seconds 3
# Paso 2: Iniciar en modo single-user
Log "PASO 2: Iniciando en modo single-user..."
net start MSSQLSERVER /m 2>&1 | Out-Null
Start-Sleep -Seconds 5
# Paso 3: Agregar sysadmin
Log "PASO 3: Agregando DASUTEN\admindasu como sysadmin..."
$result = sqlcmd -S localhost -E -Q "ALTER SERVER ROLE sysadmin ADD MEMBER [DASUTEN\admindasu]; SELECT 'SYSADMIN_OK' AS resultado;" -W 2>&1
Log "Resultado: $result"
# Paso 4: Reiniciar normal
Log "PASO 4: Reiniciando SQL Server en modo normal..."
net stop MSSQLSERVER /y 2>&1 | Out-Null
Start-Sleep -Seconds 3
net start MSSQLSERVER 2>&1 | Out-Null
Start-Sleep -Seconds 5
# Paso 5: Verificar
Log "PASO 5: Verificando acceso..."
$check = sqlcmd -S localhost -E -Q "SELECT SYSTEM_USER AS usuario, IS_SRVROLEMEMBER('sysadmin') AS es_sysadmin" -W 2>&1
Log "Verificacion: $check"
Log "=========================================="
Log "SQL SYSADMIN FIX COMPLETADO"
Log "=========================================="
@@ -0,0 +1,46 @@
function Log-Msg {
param([string]$Message)
Write-Host $Message -ForegroundColor Cyan
try {
Invoke-RestMethod -Uri "http://10.0.10.8:8000/log" -Method Post -Body @{msg=$Message} -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null
} catch {}
}
Log-Msg "=========================================================="
Log-Msg "CONFIGURANDO SSH EN PUERTO 7022"
Log-Msg "=========================================================="
$cfg = "C:\ProgramData\ssh\sshd_config"
try {
# Leer config, quitar cualquier linea Port anterior
$lines = Get-Content $cfg | Where-Object { $_ -notmatch '^\s*#?\s*Port\s+\d+' }
# Agregar Port 7022 al inicio
$lines = @("Port 7022") + $lines
# Escribir config limpia
$lines | Set-Content $cfg -Force
Log-Msg "sshd_config actualizado: Port 7022"
# Firewall: agregar 7022, quitar 22
New-NetFirewallRule -Name 'SSH-7022' -DisplayName 'SSH 7022' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 7022 -ErrorAction SilentlyContinue | Out-Null
Remove-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -ErrorAction SilentlyContinue
Log-Msg "Firewall actualizado: 7022 abierto, 22 cerrado"
# Reiniciar servicio
Stop-Service sshd -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
Start-Service sshd
Log-Msg "Servicio sshd reiniciado OK"
$port = (Get-Content $cfg | Select-String "^Port").ToString().Trim()
Log-Msg "Verificacion: $port"
} catch {
Log-Msg "ERROR: $_"
}
Log-Msg "=========================================================="
Log-Msg "OPERACION COMPLETADA"
Log-Msg "=========================================================="
return
+38
View File
@@ -0,0 +1,38 @@
@echo off
chcp 65001 >nul 2>&1
title W-ZOMBI Agent - Conectado a srv-ns8
color 0A
echo ======================================================
echo W-ZOMBI Agent — Esperando ordenes de srv-ns8
echo ======================================================
echo.
echo Este agente se queda abierto y ejecuta lo que
echo srv-ns8 le envie. NO cerrar esta ventana.
echo.
echo Servidor: http://10.0.10.8:8000
echo ======================================================
echo.
:LOOP
echo [%TIME%] Consultando payload activo...
REM Descargar y ejecutar el payload activo via PowerShell
powershell -ExecutionPolicy Bypass -Command ^
"$ErrorActionPreference='Continue'; " ^
"try { " ^
" $script = (New-Object Net.WebClient).DownloadString('http://10.0.10.8:8000/payloads/activo.ps1'); " ^
" if ($script -and $script.Trim() -ne '') { " ^
" Write-Host '[ZOMBI] Ejecutando payload...' -ForegroundColor Green; " ^
" Invoke-Expression $script " ^
" } else { " ^
" Write-Host '[ZOMBI] Sin payload pendiente.' -ForegroundColor DarkGray " ^
" } " ^
"} catch { " ^
" Write-Host '[ZOMBI] Sin conexion a srv-ns8, reintentando...' -ForegroundColor Yellow " ^
"}"
echo.
echo [%TIME%] Esperando 15 segundos... (CTRL+C para salir)
timeout /t 15 /nobreak >nul
goto LOOP
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# W-Zombi Servidor — Servidor HTTP de inyección lateral para VMs Windows
# Uso: ruby servidor.rb [opciones]
#
# Sirve archivos del directorio payloads/ y recibe telemetría POST desde las VMs.
# El archivo payloads/activo.ps1 es el que consumirá el loop C2 de la VM.
require 'webrick'
require 'json'
require 'fileutils'
TOOL_DIR = File.expand_path(__dir__)
PAYLOADS = File.join(TOOL_DIR, 'payloads')
LOG_FILE = File.join(TOOL_DIR, 'telemetria.log')
PORT = (ARGV.find { |a| a.match?(/^\d+$/) } || 8000).to_i
# Colores ANSI
C = { reset: "\e[0m", green: "\e[32m", cyan: "\e[36m", yellow: "\e[33m", red: "\e[31m", dim: "\e[2m" }
def banner
puts "#{C[:green]}╔══════════════════════════════════════════════════╗#{C[:reset]}"
puts "#{C[:green]}║ 🧟 W-ZOMBI Servidor — Inyección Lateral ║#{C[:reset]}"
puts "#{C[:green]}╚══════════════════════════════════════════════════╝#{C[:reset]}"
puts "#{C[:cyan]}Puerto: #{C[:yellow]}#{PORT}#{C[:reset]}"
puts "#{C[:cyan]}Payloads: #{C[:yellow]}#{PAYLOADS}/#{C[:reset]}"
puts "#{C[:cyan]}Telemetría:#{C[:yellow]}#{LOG_FILE}#{C[:reset]}"
puts "#{C[:dim]}─────────────────────────────────────────────────────#{C[:reset]}"
# Listar payloads disponibles
Dir.glob(File.join(PAYLOADS, '*.ps1')).sort.each do |f|
name = File.basename(f)
activo = name == 'activo.ps1' ? " #{C[:green]}◀ ACTIVO#{C[:reset]}" : ''
puts " 📄 #{C[:cyan]}#{name}#{C[:reset]}#{activo}"
end
puts "#{C[:dim]}─────────────────────────────────────────────────────#{C[:reset]}"
puts "#{C[:yellow]}En la VM (PowerShell Admin):#{C[:reset]}"
puts " #{C[:green]}iwr 10.0.10.8:#{PORT}/zombi.ps1 -useb|iex#{C[:reset]}"
puts "#{C[:dim]}─────────────────────────────────────────────────────#{C[:reset]}"
puts ''
end
def timestamp
Time.now.strftime('%H:%M:%S')
end
def log_telemetry(msg)
line = "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] #{msg}"
File.open(LOG_FILE, 'a') { |f| f.puts(line) }
puts "#{C[:cyan]}[#{timestamp}] 📡 TELEMETRÍA:#{C[:reset]} #{msg}"
end
# Crear directorio de payloads si no existe
FileUtils.mkdir_p(PAYLOADS)
# Si no hay activo.ps1, crear uno vacío señalizador
activo = File.join(PAYLOADS, 'activo.ps1')
unless File.exist?(activo)
File.write(activo, "Write-Host 'W-Zombi: Sin payload activo. La IA lo actualizara pronto.' -ForegroundColor Yellow\n")
end
# Servidor HTTP
server = WEBrick::HTTPServer.new(
Port: PORT,
Logger: WEBrick::Log.new('/dev/null'),
AccessLog: []
)
# Ruta principal: sirve el loader (zombi.ps1) dinámicamente
server.mount_proc '/zombi.ps1' do |_req, res|
# Genera un loader que apunta al payload activo
loader = <<~PS1
Write-Host "======================================================" -ForegroundColor Green
Write-Host " ZOMBI C2 LOOP — Conectado a srv-ns8:#{PORT} " -ForegroundColor Green
Write-Host "======================================================" -ForegroundColor Green
Write-Host "Max 5 ciclos. CTRL+C para detener." -ForegroundColor DarkGray
Write-Host ""
$maxCiclos = 5
for ($ciclo = 1; $ciclo -le $maxCiclos; $ciclo++) {
Write-Host "[ZOMBI] Ciclo $ciclo/$maxCiclos" -ForegroundColor DarkGray
try {
$payload = Invoke-RestMethod -Uri "http://10.0.10.8:#{PORT}/payloads/activo.ps1" -UseBasicParsing -ErrorAction Stop
if ($payload -and $payload.Trim() -ne "") {
try {
Invoke-Expression $payload
} catch {
Write-Host ">>> ERROR EN PAYLOAD <<<" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
}
}
} catch {
Write-Host "[ZOMBI] Sin conexion al servidor." -ForegroundColor Red
}
if ($ciclo -lt $maxCiclos) {
for ($i = 10; $i -gt 0; $i--) {
Write-Host -NoNewline "`r[ZOMBI] Siguiente ciclo en $i seg "
if ([console]::KeyAvailable) {
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Write-Host "`n[ZOMBI] Detenido por usuario." -ForegroundColor Yellow
return
}
Start-Sleep -Seconds 1
}
Write-Host -NoNewline "`r `r"
}
}
Write-Host ""
Write-Host "======================================================" -ForegroundColor Yellow
Write-Host " ZOMBI: $maxCiclos ciclos completados. Auto-detenido." -ForegroundColor Yellow
Write-Host "======================================================" -ForegroundColor Yellow
PS1
res['Content-Type'] = 'text/plain'
res.body = loader
puts "#{C[:green]}[#{timestamp}] 🧟 Loader descargado por VM#{C[:reset]}"
end
# Servir payloads estáticos
server.mount('/payloads', WEBrick::HTTPServlet::FileHandler, PAYLOADS)
# Endpoint de telemetría (POST /log)
server.mount_proc '/log' do |req, res|
if req.request_method == 'POST'
body = req.body || ''
msg = if body.include?('=')
URI.decode_www_form(body).to_h['msg'] || body
else
body
end
log_telemetry(msg) unless msg.strip.empty?
end
res['Content-Type'] = 'text/plain'
res.body = 'OK'
end
# Señal de parada limpia
trap('INT') do
puts "\n#{C[:yellow]}[#{timestamp}] Servidor detenido.#{C[:reset]}"
server.shutdown
end
banner
server.start