[Tools] W-Zombi refactorizado: Ruby servidor, payloads modulares, migrado a tools/

This commit is contained in:
Ricardo Monla
2026-02-27 21:34:52 -03:00
parent 1d62cc278e
commit 73aa405627
10 changed files with 254 additions and 275 deletions
-55
View File
@@ -1,55 +0,0 @@
# 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, lo que impide al operador provisionar comandos complejos (instalaciones, configuraciones) en las VMs Windows recién creadas que aún no tienen SSH.
## Solución: C2 Polling Loop
Se utiliza una arquitectura de "Command & Control" liviana que sortea la limitación del portapapeles:
```
srv-ns8 (10.0.10.8) VM Windows (VNC)
┌──────────────────┐ ┌────────────────────┐
│ logger.py │ ← HTTP :8000 ←── │ s.txt (loop) │
│ (servidor HTTP) │ │ Consulta payload │
│ │ ── payload.ps1 ──→ │ cada 15 seg │
│ │ │ Ejecuta y reporta │
└──────────────────┘ └────────────────────┘
```
## Flujo Operativo
### 1. En srv-ns8 (la IA):
```bash
cd scripts/w-zombi
python3 logger.py
```
Esto levanta un servidor HTTP en el puerto 8000 que sirve los archivos del directorio.
### 2. En la VM Windows (el operador tipea manualmente en VNC):
```powershell
iwr 10.0.10.8:8000/s.txt -useb|iex
```
Este one-liner de ~35 caracteres es lo único que el operador necesita tipear a mano.
### 3. La IA modifica `payload.ps1`:
Cada vez que el loop de la VM consulta, ejecuta lo que esté en `payload.ps1`. La IA puede cambiar su contenido dinámicamente para inyectar cualquier secuencia de comandos.
## Archivos
| Archivo | Tipo | Descripción |
| :--- | :--- | :--- |
| `s.txt` | PowerShell | C2 polling loop (se ejecuta en la VM, consulta payload cada 15s). |
| `payload.ps1` | PowerShell | Carga útil dinámica que la IA modifica según la tarea. |
| `install_ssh.ps1` | PowerShell | Payload especializado: instala OpenSSH Server nativo. |
| `sonda_windows.ps1` | PowerShell | Payload especializado: diagnóstico y relevamiento del sistema. |
| `logger.py` | Python 3 | Servidor HTTP efímero con logging de requests entrantes. |
## Seguridad
- El servidor HTTP es **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 en la VM, este mecanismo deja de ser necesario.
-30
View File
@@ -1,30 +0,0 @@
Write-Host "==========================================================" -ForegroundColor Cyan
Write-Host "INSTALANDO Y HABILITANDO OPENSSH SERVER NATIVO EN DC01" -ForegroundColor Cyan
Write-Host "==========================================================" -ForegroundColor Cyan
try {
$sshCheck = Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*'
if ($sshCheck.State -ne 'Installed') {
Write-Host "Instalando caracteristica OpenSSH.Server (esto puede tomar un minuto)..." -ForegroundColor Yellow
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 | Out-Null
} else {
Write-Host "OpenSSH.Server ya se encuentra instalado." -ForegroundColor Green
}
Write-Host "Configurando servicio sshd en Inicio Automatico..." -ForegroundColor Yellow
Set-Service -Name sshd -StartupType 'Automatic'
Start-Service sshd -ErrorAction SilentlyContinue
if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue)) {
Write-Host "Abriendo puerto TCP 22 en el Firewall local..." -ForegroundColor Yellow
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
Write-Host "OpenSSH Server instalado y corriendo exitosamente en el puerto 22." -ForegroundColor Green
Write-Host "La IA ya puede conectarse a este nodo mediante: ssh Administrator@$($ip[0])" -ForegroundColor Green
} catch {
Write-Host "Fallo al instalar SSH: $_" -ForegroundColor Red
}
Write-Host "==========================================================" -ForegroundColor Cyan
-29
View File
@@ -1,29 +0,0 @@
import http.server
import socketserver
import urllib.parse
from datetime import datetime
PORT = 8000
class CustomHandler(http.server.SimpleHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers.get('Content-Length', 0))
post_data = self.rfile.read(content_length).decode('utf-8')
parsed = urllib.parse.parse_qs(post_data)
msg = parsed.get('msg', [''])[0]
if msg:
with open('sql-install-telemetry.log', 'a') as f:
f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {msg}\n")
# Print to standard output so I can read it with command_status
print(f"DB01 TELEMETRY: {msg}", flush=True)
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(b"OK")
socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(("", PORT), CustomHandler) as httpd:
print(f"Logging server listening intensely on port {PORT}...", flush=True)
httpd.serve_forever()
-55
View File
@@ -1,55 +0,0 @@
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 "INICIANDO VERIFICACION DE SQL SERVER"
Log-Msg "=========================================================="
try {
$svc = Get-Service -Name "MSSQLSERVER"
Log-Msg "Servicio MSSQLSERVER detectado. Estado: $($svc.Status)"
if ($svc.Status -eq 'Running') {
Log-Msg "Ejecutando Test de Conexion Local via sqlcmd..."
$sqlResult = Invoke-Command -ScriptBlock { sqlcmd -Q "SELECT @@VERSION;" }
Log-Msg "Resultado SQLCMD: $sqlResult"
} else {
Log-Msg "Iniciando servicio MSSQLSERVER..."
Start-Service -Name "MSSQLSERVER" -ErrorAction Stop
$svc.Refresh()
Log-Msg "Nuevo Estado: $($svc.Status)"
}
} catch {
Log-Msg "Fallo al verificar el servicio: $_"
}
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..."
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 | Out-Null
}
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)) {
New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 | Out-Null
}
Log-Msg "OpenSSH Server instalado y corriendo en el puerto 22."
} catch {
Log-Msg "Fallo al instalar SSH: $_"
}
Log-Msg "=========================================================="
Log-Msg "OPERACION COMPLETADA. LA IA AHORA TOMARA CONTROL VIA SSH DIRECTAMENTE."
Log-Msg "=========================================================="
exit
-42
View File
@@ -1,42 +0,0 @@
Write-Host "=====================================================" -ForegroundColor Green
Write-Host " INICIANDO C2 POLLING LOOP - NODO DB01 ZOMBI " -ForegroundColor Green
Write-Host "=====================================================" -ForegroundColor Green
Write-Host "Este script pedira instrucciones a la IA automaticamente. Deja la consola abierta." -ForegroundColor Yellow
Write-Host "Presiona CTRL+C en cualquier momento para detenerlo." -ForegroundColor DarkGray
Write-Host ""
while ($true) {
try {
$payload = Invoke-RestMethod -Uri "http://10.0.10.8:8000/payload.ps1" -UseBasicParsing -ErrorAction Stop
if ($payload -and $payload.Trim() -ne "") {
try {
Invoke-Expression $payload
} catch {
Write-Host ""
Write-Host ">>> ERROR FATAL EN EL SCRIPT DE LA IA <<<" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
Write-Host ">>> LA PANTALLA SE PAUSARA POR 30 SEGUNDOS PARA QUE PUEDAS LEER <<<" -ForegroundColor Yellow
Start-Sleep -Seconds 30
}
}
} catch {
# Fallo silencioso si el C2 server no responde
}
for ($i = 15; $i -gt 0; $i--) {
Write-Host -NoNewline "`r[C2] Esperando IA... $i seg (ENTRAR: apurar | P: pausar pantalla)... "
if ([console]::KeyAvailable) {
$key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown").Character.ToString().ToUpper()
if ($key -eq 'P') {
Write-Host "`n[C2] PANTALLA PAUSADA. Presiona cualquier tecla para continuar..." -ForegroundColor Yellow
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null
break
} else {
break
}
}
Start-Sleep -Seconds 1
}
# Limpiar línea
Write-Host -NoNewline "`r `r"
}
-94
View File
@@ -1,94 +0,0 @@
<#
.SYNOPSIS
Sonda de Relevamiento para Nodos Windows (Compatible con Agente srv-NS8)
Versión: 1.0.0
Autor: Ricardo MONLA (Generado por Agente)
.DESCRIPTION
Recolecta información de Hardware, Red, Sistema Operativo y Servicios.
Genera un objeto JSON compatible con la Ontología del proyecto.
.EXAMPLE
.\sonda_windows.ps1
#>
$ErrorActionPreference = "SilentlyContinue"
# --- 1. Sistema Operativo ---
$os = Get-CimInstance Win32_OperatingSystem
$cs = Get-CimInstance Win32_ComputerSystem
# --- 2. Hardware: CPU ---
$cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
$cpuName = $cpu.Name.Trim()
$cores = $cpu.NumberOfCores
$threads = $cpu.NumberOfLogicalProcessors
# --- 3. Hardware: RAM ---
$ramBytes = $cs.TotalPhysicalMemory
$ramGB = [Math]::Round($ramBytes / 1GB, 2)
# --- 4. Hardware: Discos ---
$disks = Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 } | ForEach-Object {
@{
id = $_.DeviceID
size_gb = [Math]::Round($_.Size / 1GB, 2)
used_gb = [Math]::Round(($_.Size - $_.FreeSpace) / 1GB, 2)
free_gb = [Math]::Round($_.FreeSpace / 1GB, 2)
fs_type = $_.FileSystem
mount_point = $_.DeviceID
}
}
# --- 5. Red ---
$netAdapters = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | ForEach-Object {
$ipInfo = Get-NetIPAddress -InterfaceAlias $_.Name -AddressFamily IPv4 | Select-Object -First 1
@{
interface = $_.Name
mac = $_.MacAddress
ip = if ($ipInfo) { $ipInfo.IPAddress } else { "N/A" }
state = "UP"
speed_mbps = [Math]::Round($_.LinkSpeed / 1MB, 0)
}
}
# --- 6. Software Detectado (Servicios Clave) ---
$servicesToCheck = @("Tailscale", "RustDesk", "AnyDesk", "TeamViewer", "ssh-agent", "sshd")
$detectedServices = Get-Service | Where-Object { $servicesToCheck -contains $_.Name -or $servicesToCheck -contains $_.DisplayName } | ForEach-Object {
@{
name = $_.Name
status = $_.Status.ToString()
startup = $_.StartType.ToString()
}
}
# --- 7. Construcción del JSON ---
$sondaData = @{
timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
node = @{
hostname = $cs.DNSHostName
os = "$($os.Caption) ($($os.OSArchitecture))"
version = $os.Version
domain = $cs.Domain
model = $cs.Model
}
hardware = @{
cpu = @{
model = $cpuName
cores = $cores
threads = $threads
}
ram_gb = $ramGB
disks = $disks
}
network = $netAdapters
services = $detectedServices
meta = @{
agent_version = "1.0.0-win"
type = "probe_windows"
}
}
# --- 8. Salida ---
$jsonOutput = $sondaData | ConvertTo-Json -Depth 5
Write-Output $jsonOutput