[Tools] W-Zombi refactorizado: Ruby servidor, payloads modulares, migrado a tools/
This commit is contained in:
@@ -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.
|
|
||||||
@@ -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
|
|
||||||
@@ -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()
|
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Write-Host 'W-Zombi: Sin payload activo. La IA lo actualizara pronto.' -ForegroundColor Yellow
|
||||||
@@ -6,35 +6,16 @@ function Log-Msg {
|
|||||||
} catch {}
|
} 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 "=========================================================="
|
||||||
Log-Msg "INSTALANDO Y HABILITANDO OPENSSH SERVER NATIVO"
|
Log-Msg "INSTALANDO Y HABILITANDO OPENSSH SERVER NATIVO"
|
||||||
Log-Msg "=========================================================="
|
Log-Msg "=========================================================="
|
||||||
try {
|
try {
|
||||||
$sshCheck = Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*'
|
$sshCheck = Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*'
|
||||||
if ($sshCheck.State -ne 'Installed') {
|
if ($sshCheck.State -ne 'Installed') {
|
||||||
Log-Msg "Instalando caracteristica OpenSSH.Server..."
|
Log-Msg "Instalando caracteristica OpenSSH.Server (esto puede tomar un minuto)..."
|
||||||
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 | Out-Null
|
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..."
|
Log-Msg "Configurando servicio sshd en Inicio Automatico..."
|
||||||
@@ -42,14 +23,18 @@ try {
|
|||||||
Start-Service sshd -ErrorAction SilentlyContinue
|
Start-Service sshd -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -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
|
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 "OpenSSH Server instalado y corriendo en el puerto 22."
|
||||||
|
Log-Msg "Conectar via: ssh Administrador@$($ip[0])"
|
||||||
} catch {
|
} catch {
|
||||||
Log-Msg "Fallo al instalar SSH: $_"
|
Log-Msg "Fallo al instalar SSH: $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
Log-Msg "=========================================================="
|
Log-Msg "=========================================================="
|
||||||
Log-Msg "OPERACION COMPLETADA. LA IA AHORA TOMARA CONTROL VIA SSH DIRECTAMENTE."
|
Log-Msg "OPERACION COMPLETADA. Ahora SSH esta disponible."
|
||||||
Log-Msg "=========================================================="
|
Log-Msg "=========================================================="
|
||||||
exit
|
exit
|
||||||
@@ -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
|
||||||
Executable
+135
@@ -0,0 +1,135 @@
|
|||||||
|
#!/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 "CTRL+C para detener." -ForegroundColor DarkGray
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
while ($true) {
|
||||||
|
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
|
||||||
|
Start-Sleep -Seconds 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
for ($i = 10; $i -gt 0; $i--) {
|
||||||
|
Write-Host -NoNewline "`r[ZOMBI] Esperando payload... $i seg "
|
||||||
|
if ([console]::KeyAvailable) {
|
||||||
|
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
Start-Sleep -Seconds 1
|
||||||
|
}
|
||||||
|
Write-Host -NoNewline "`r `r"
|
||||||
|
}
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user