Laboratorio docente para demostrar en vivo los riesgos de conectarse a redes WiFi no confiables, cómo se captura e intercepta tráfico, la diferencia real entre WiFi abierta y WPA, y cómo detectar si tu tráfico está siendo inspeccionado.
Solo para uso en entornos controlados con consentimiento explícito de los participantes. Informa a los alumnos antes de empezar y prohíbe credenciales personales reales en el laboratorio.
- Requisitos de hardware
- Topología
- Instalación y primer arranque
- Variables de configuración
- Modos de demostración
- Servidor web HTTP/HTTPS de demostración
- Scripts operativos
- Flujo de clase recomendado
- Filtrado selectivo del proxy
- Demo de modificación de contenido
- Distribución del certificado CA
- Verificación rápida
- Resolución de problemas
| Componente | Detalle |
|---|---|
| Raspberry Pi 3B / 3B+ | El chip WiFi integrado soporta modo AP |
| Raspberry Pi OS Lite | Bullseye (Debian 11), Bookworm (12) o Trixie (13) |
| Tarjeta microSD | ≥ 8 GB |
| Router 4G Huawei E3372 | eth1 — uplink a internet (192.168.8.1/24) |
| Cable Ethernet (admin) | eth0 — red de gestión estática 192.168.0.1/24 |
| Alimentación | 5V / 2.5A mínimo |
Alternativa sin router 4G: conectar
eth0directamente a un router de aula con DHCP y lanzar consudo UPLINK_IFACE=eth0 ./enterprise_lab.sh.
El script ha sido probado sobre Debian 13 (trixie), kernel 6.12.75, con NetworkManager activo.
4G Internet
|
[E3372 router] ── eth1 (192.168.8.100) ──┐
[Raspberry Pi 3]
[Portátil instructor] ── eth0 (192.168.0.1) ──┘
(192.168.0.2) |
wlan0 (AP)
CorpNet-Enterprise
192.168.50.1/24
/ \
Móvil A Portátil B
192.168.50.10 192.168.50.11
- eth1 — uplink a internet, IP
192.168.8.100por DHCP del router 4G E3372 - eth0 — interfaz de administración, IP estática
192.168.0.1/24(acceso SSH del instructor) - wlan0 — Access Point para alumnos, IP estática
192.168.50.1/24 - Todo el tráfico HTTP/HTTPS de los alumnos pasa por mitmproxy (puerto 8080) antes de salir por eth1
# Clonar el repositorio en el Pi
git clone https://github.com/lobobinario/wifi-lab-public.git
cd wifi-lab-public
# Hacer ejecutables todos los scripts
chmod +x *.sh
# Lanzar el setup principal (requiere root)
sudo ./enterprise_lab.sh
# Instalar el servidor web HTTP/HTTPS de demostración
sudo ./setup_webserver.shEl script hace automáticamente:
- Instala los paquetes necesarios (
hostapd,dnsmasq,mitmproxyvía pipx si no está disponible en apt,tcpdump,iptables-persistent,iw, etc.) - Dice a NetworkManager que no gestione
wlan0 - Asigna la IP estática
192.168.50.1/24awlan0(inmediato + persistente) - Escribe la configuración de
hostapdydnsmasq - Activa el reenvío IP y configura las reglas iptables (NAT + redirect al proxy)
- Crea y activa el servicio systemd
enterprise.service(mitmdump transparente) - Espera a que se genere el certificado CA de mitmproxy y muestra su ruta
Al terminar, verás:
[+] Setup complete!
[i] SSID : CorpNet-Enterprise (open)
[i] Gateway : 192.168.50.1
[i] DHCP range : 192.168.50.10 – 192.168.50.200
[i] Proxy port : 8080
[i] CA cert : /root/.mitmproxy/mitmproxy-ca-cert.pem
[i] Next step : sudo ./lab_precheck.sh
Ejecuta el precheck para confirmar que todo está verde:
sudo ./lab_precheck.shTodas tienen valores por defecto. Se pasan como variables de entorno antes del script.
| Variable | Por defecto | Descripción |
|---|---|---|
AP_IFACE |
wlan0 |
Interfaz WiFi para el Access Point |
UPLINK_IFACE |
eth1 |
Interfaz de salida a internet (router 4G); usar eth0 con red cableada |
LAB_GATEWAY |
192.168.50.1 |
IP del Pi en la red del laboratorio |
LAB_NETMASK |
255.255.255.0 |
Máscara de red |
LAB_DHCP_RANGE_START |
192.168.50.10 |
Inicio del rango DHCP |
LAB_DHCP_RANGE_END |
192.168.50.200 |
Fin del rango DHCP |
LAB_SSID |
CorpNet-Enterprise |
Nombre de la red WiFi |
LAB_CHANNEL |
6 |
Canal WiFi (1–13) |
LAB_COUNTRY |
ES |
Código de país para regulación WiFi |
LAB_MODE |
open |
open o wpa2 |
LAB_WPA2_PASSPHRASE |
ChangeMe123! |
Contraseña si LAB_MODE=wpa2 |
| Variable | Por defecto | Descripción |
|---|---|---|
PROXY_PORT |
8080 |
Puerto donde escucha mitmproxy |
MITMPROXY_BIN |
mitmdump |
Binario de mitmproxy a usar |
PROXY_IGNORE_HOSTS |
Apple + Facebook | Regex de hosts que pasan sin interceptar |
PROXY_ALLOW_HOSTS |
(vacío = todos) | Si se define, intercepta SOLO estos hosts |
PROXY_DEMO_MODIFY |
0 |
1 activa el addon de modificación de contenido |
PROXY_MODIFY_SCRIPT |
./mitm_demo_modify.py |
Ruta al script de modificación |
# Red WPA2 con contraseña personalizada
sudo LAB_MODE=wpa2 LAB_WPA2_PASSPHRASE='Clase2025!' ./enterprise_lab.sh
# Solo interceptar los dominios de demostración (resto pasa limpio)
sudo PROXY_ALLOW_HOSTS='(example\.com|badssl\.com)' ./enterprise_lab.sh
# Interceptar todo excepto Apple
sudo PROXY_IGNORE_HOSTS='(.+\.apple\.com|.+\.icloud\.com)' ./enterprise_lab.sh
# Activar modificación de contenido (Demo C, requiere CA confiada en cliente)
sudo PROXY_DEMO_MODIFY=1 ./enterprise_lab.sh
# País distinto (ajusta potencia y canales permitidos)
sudo LAB_COUNTRY=DE ./enterprise_lab.shConfiguración por defecto al ejecutar enterprise_lab.sh.
- El proxy intercepta todo el tráfico HTTP y HTTPS
- Los navegadores muestran "Tu conexión no es privada" al visitar HTTPS
- Las apps con certificate pinning fallan silenciosamente (no muestran aviso)
- El instructor puede ver todos los intentos en el log en tiempo real
sudo journalctl -u enterprise.service -f --no-pagerObjetivo pedagógico: aprender a leer alertas de certificado y reconocer señales de inspección activa.
Solo en dispositivos de laboratorio designados. Nunca en dispositivos personales.
-
Distribuir el certificado CA:
# Servir el cert por HTTP desde el Pi sudo python3 -m http.server 80 --directory /root/.mitmproxy & # Alumnos descargan: http://192.168.50.1/mitmproxy-ca-cert.pem
-
Instalar la CA en el dispositivo de laboratorio (ver sección 11)
-
Navegar por los mismos sitios HTTPS — el navegador muestra candado normal, mitmproxy registra el contenido completo
Objetivo pedagógico: comprender qué visibilidad tiene una empresa sobre el tráfico de dispositivos gestionados, y distinguirlo de un ataque.
Requiere Fase B activa (CA confiada en el cliente).
sudo PROXY_DEMO_MODIFY=1 ./enterprise_lab.shO activar en caliente sin re-ejecutar el setup:
sudo cp mitm_demo_modify.py /usr/local/bin/
# Editar /usr/local/bin/start_proxy.sh, añadir: -s /usr/local/bin/mitm_demo_modify.py
sudo systemctl restart enterprise.servicePor defecto sustituye Google → Gooogle en respuestas HTML/JS/SVG.
Personalizable editando REPLACEMENTS en mitm_demo_modify.py.
Objetivo pedagógico: demostrar que un MITM con CA confiada no solo observa, sino que puede modificar cualquier contenido en tránsito sin que el usuario reciba ninguna señal de alerta.
El laboratorio incluye un servidor web ligero (lab_webserver.py) que expone el
mismo formulario en HTTP y en HTTPS de forma simultánea. El objetivo es hacer
visible de forma inmediata y concreta la diferencia entre ambos protocolos:
los alumnos envían datos y el instructor proyecta el tráfico capturado con
tcpdump en tiempo real.
sudo ./setup_webserver.shEl script instala el servidor en /opt/lab-webserver/, genera un certificado
autofirmado para 192.168.50.1 y registra el servicio systemd lab-webserver.
| URL | Protocolo | Descripción |
|---|---|---|
http://192.168.50.1/send |
HTTP :80 | Formulario con banner rojo — datos en claro |
https://192.168.50.1/send |
HTTPS :443 | Formulario con banner verde — datos cifrados |
http://192.168.50.1/visualize |
HTTP :80 | Tabla de todos los envíos (IP, proto, usuario, mensaje) |
Cada página incluye un enlace directo para cambiar de protocolo, lo que
facilita la comparación instantánea. La tabla /visualize colorea en rojo
las filas enviadas por HTTP y en verde las de HTTPS.
El proxy transparente (enterprise.service) redirige el tráfico de los
clientes en los puertos 80 y 443 hacia mitmproxy. La regla iptables
excluye explícitamente la IP del propio Pi (! -d 192.168.50.1), por lo
que el tráfico al servidor web llega directamente al proceso Python sin
pasar por el proxy. El resto del tráfico de los alumnos sigue siendo
interceptado con normalidad.
# Estado
systemctl status lab-webserver
# Log de envíos en tiempo real
tail -f /var/log/lab_webserver.log
# Reiniciar
sudo systemctl restart lab-webserverProyectar tcpdump mientras los alumnos envían el formulario por HTTP:
sudo tcpdump -i wlan0 -A -s 0 'tcp port 80 and dst host 192.168.50.1' 2>/dev/null \
| grep -E --line-buffered "POST|user=|message="El tráfico HTTPS al mismo destino aparece cifrado e ilegible bajo el mismo
filtro. Abrir /visualize en el proyector para mostrar ambas entradas
lado a lado con sus etiquetas HTTP/HTTPS.
| Script | Función |
|---|---|
enterprise_lab.sh |
Setup completo del laboratorio (AP, proxy, iptables) |
setup_webserver.sh |
Instala el servidor web HTTP/HTTPS de demostración |
lab_precheck.sh |
Verifica todos los servicios, reglas y conectividad antes de clase |
lab_addon_toggle.sh |
Activa/desactiva addons de mitmproxy en caliente |
teacher_mode.sh |
Un comando para precheck + estado + captura opcional |
soc_monitor.sh |
Captura PCAP con duración y rotación configurables |
lab_teardown.sh |
Deshace toda la configuración (incluido el servidor web) y restaura el sistema |
captive-portal.sh |
(opcional) Toggle del bloque "portal cautivo" — landing de bienvenida con descarga de la CA |
mitm_demo_modify.py |
Addon mitmproxy para modificación activa de contenido |
mitm_credential_logger.py |
Addon mitmproxy para captura de credenciales en formularios |
Pensado para ejecutar justo antes de que lleguen los alumnos:
# Solo precheck y captura (sin re-ejecutar setup)
sudo RUN_SETUP=0 RUN_CAPTURE=1 CAPTURE_DURATION=3600 ./teacher_mode.sh
# Setup completo + precheck + captura
sudo RUN_SETUP=1 RUN_CAPTURE=1 ./teacher_mode.sh# Captura 30 minutos en wlan0
sudo IFACE=wlan0 DURATION_SEC=1800 LOG_DIR=/var/log/enterprise ./soc_monitor.sh
# Ver capturas guardadas
ls -lh /var/log/enterprise/Elimina toda la configuración del laboratorio: servicios, reglas iptables,
drop-ins de dnsmasq/hostapd/NetworkManager/interfaces.d, dhcpcd.conf blocks,
y restaura NetworkManager sobre wlan0.
sudo ./lab_teardown.sh
sudo reboot # recomendado para limpiar estado de red completamente 0–15 min Contexto: modelo de amenaza, WiFi abierta vs WPA
15–30 min Sniffing en vivo: HTTP expuesto, metadatos HTTPS visibles
30–40 min Demo web: formulario HTTP vs HTTPS — comparación directa con tcpdump
40–60 min Demo A: MITM activo, alertas de certificado, cómo detectar proxy
60–70 min Demo B: CA confiada, inspección silenciosa, log en tiempo real
70–80 min Demo C (opcional): modificación de contenido en tránsito
80–90 min Análisis PCAP + debate + buenas prácticas
Ver enterprise_wifi_course_material/guion_instructor.md para el guion
detallado de cada bloque con los comandos exactos a ejecutar.
Por defecto el proxy intercepta todo el tráfico HTTP/HTTPS procedente de wlan0
excepto los dominios de Apple y Facebook (para que las apps del móvil sigan
funcionando durante la demo).
sudo PROXY_ALLOW_HOSTS='(example\.com|badssl\.com|wikipedia\.org)' ./enterprise_lab.shTodo lo demás (Google, redes sociales, apps) pasa sin interceptar. Ideal para una demo focalizada.
sudo PROXY_IGNORE_HOSTS='(.+\.apple\.com|.+\.icloud\.com|.+\.google\.com|.+\.whatsapp\.net)' \
./enterprise_lab.shsudo PROXY_IGNORE_HOSTS='' ./enterprise_lab.shEditar /usr/local/bin/start_proxy.sh y luego:
sudo systemctl restart enterprise.serviceEl archivo mitm_demo_modify.py es un addon Python para mitmproxy que reescribe
respuestas en tránsito. Funciona sobre HTML, JavaScript y SVG, y gestiona
automáticamente la compresión gzip/brotli.
Editar la lista REPLACEMENTS al inicio del archivo:
REPLACEMENTS: list[tuple[bytes, bytes]] = [
(b"Google", b"Gooogle"), # Demo clásico: 3 'o'
(b"Search", b"Snoop"), # Mostrar intención del atacante
(b"Secure", b"COMPROMETIDO"),# Efecto dramático
# Añadir las que quieras
]text/html— páginas webtext/javascript/application/javascript— scriptsimage/svg+xml— logos e iconos vectoriales
# Activar
sudo sed -i 's|--set tls_version_client_min|-s /usr/local/bin/mitm_demo_modify.py \\\n --set tls_version_client_min|' \
/usr/local/bin/start_proxy.sh
sudo systemctl restart enterprise.service
# Desactivar
sudo sed -i '/-s \/usr\/local\/bin\/mitm_demo_modify/d' /usr/local/bin/start_proxy.sh
sudo systemctl restart enterprise.serviceO simplemente editar /usr/local/bin/start_proxy.sh a mano.
# Ver args del proceso mitmdump
cat /proc/$(systemctl show -p MainPID enterprise.service | cut -d= -f2)/cmdline \
| tr '\0' ' '
# Debe incluir: -s /usr/local/bin/mitm_demo_modify.pyEl certificado de mitmproxy se genera en el primer arranque del servicio:
/root/.mitmproxy/mitmproxy-ca-cert.pem
Para distribuirlo a los dispositivos de laboratorio:
# Opción A: servidor HTTP temporal
sudo python3 -m http.server 80 --directory /root/.mitmproxy &
# Alumnos: http://192.168.50.1/mitmproxy-ca-cert.pemAndroid:
- Descargar el
.pemdesde el navegador - Ajustes → Seguridad → Instalar certificado → CA
- Nombre:
Lab mitmproxy
iOS / iPadOS:
- Abrir la URL — iOS lo reconoce como perfil
- Ajustes → General → VPN y gestión del dispositivo → Instalar
- Ajustes → General → Información → Ajustes de confianza de certificado → Activar la CA
macOS:
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain mitmproxy-ca-cert.pemLinux:
sudo cp mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/mitmproxy.crt
sudo update-ca-certificatesFirefox (cualquier SO): Preferencias → Privacidad y seguridad → Ver certificados → Importar → marcar "Confiar para identificar sitios web"
Importante: desinstalar siempre al terminar la práctica.
# Precheck completo (recomendado antes de clase)
sudo ./lab_precheck.sh
# Estado de los cuatro servicios clave
sudo systemctl status hostapd dnsmasq enterprise.service lab-webserver --no-pager
# Verificar servidor web
systemctl is-active lab-webserver
curl -so /dev/null -w "%{http_code}" http://192.168.50.1/send
# IP de wlan0 (debe mostrar 192.168.50.1/24)
ip a show wlan0
# Reglas NAT activas
sudo iptables -t nat -S
# Log del proxy en tiempo real
sudo journalctl -u enterprise.service -f --no-pager
# Clientes conectados al AP
arp -an | grep "192.168.50\."sudo journalctl -u hostapd -n 50 --no-pagerCausas habituales:
hostapdenmascarado:sudo systemctl unmask hostapdcountry_codeincorrecto: relanzar conLAB_COUNTRY=XX- NetworkManager todavía controla
wlan0:sudo nmcli device set wlan0 managed no
ip a show wlan0 # debe mostrar 192.168.50.1/24
sudo systemctl status dnsmasq --no-pagerSi wlan0 no tiene IP:
sudo ip addr add 192.168.50.1/24 dev wlan0
sudo ip link set wlan0 up
sudo systemctl restart dnsmasqEsperado. El proxy está activo y el cliente no confía la CA. Las apps con certificate pinning fallan silenciosamente; los navegadores muestran "Tu conexión no es privada". Esto es la demo.
sudo ss -ltnup | grep :53
sudo systemctl disable --now systemd-resolved
sudo systemctl restart dnsmasqsudo journalctl -u enterprise.service -n 30 --no-pager
# Verificar que el binario existe:
which mitmdump || ls /usr/local/bin/mitmdump- Limitar alumnos simultáneos a ≤ 10 dispositivos
- Evitar streaming de video durante la demo
- La modificación de contenido (Fase C) añade carga de CPU por la des/recompresión brotli; desactivarla si hay problemas