Capítulo 17 — Laboratorio contenido II
Mapa de la lección
Lección 17 publicada. Mapa, ejemplo, EX067.
flowchart TD
N0["Resultados de aprendizaje y conexión"]
N1["17.1 Reglas a partir del modelo"]
N2["17.2 Episodios"]
N3["17.3 La cadena se supervisa a sí misma"]
N4["17.5 Rúbrica de evaluación"]
N5["17.6 Actividad práctica"]
N6["Síntesis del capítulo"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6
N6 --> EX["Ejemplo resuelto"]
EX --> Q["Ejercicio EX067"]
Dibujo lógico
flowchart LR
N0["Resultados de aprendizaje y conexión"]
N1["17.1 Reglas a partir del modelo"]
N2["17.2 Episodios"]
N3["17.3 La cadena se supervisa a sí misma"]
N4["17.5 Rúbrica de evaluación"]
N5["17.6 Actividad práctica"]
N6["Síntesis del capítulo"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> Q["EX067"]
[Resultados de aprendizaje y conexión]
|
v
[17.1 Reglas a partir del modelo]
|
v
[17.2 Episodios]
|
v
[17.3 La cadena se supervisa a sí misma]
|
v
[17.5 Rúbrica de evaluación]
|
v
[17.6 Actividad práctica]
|
v
[Síntesis del capítulo]
|
v
[Ejemplo resuelto] --> [Ejercicio EX067]
Resultados de aprendizaje y conexión
Al terminar este capítulo debería ser capaz de:
- integrar las técnicas del curso (decodificación, capítulo 2; validación, capítulo 4; reglas de detección, capítulos 8 y 10; episodios y autocomprobaciones) en una única cadena de detección;
- redactar reglas de detección a partir del modelo del sistema, no del catálogo de un producto: interlocutor nuevo, escritura no autorizada, consulta de identificación;
- agrupar las alertas en episodios y explicar el compromiso que supone la ventana de agrupación;
- hacer que la cadena se supervise a sí misma: cada registro de entrada o se analiza o se cuenta como descartado;
- evaluar la cadena con una rúbrica e identificar sus puntos ciegos.
El capítulo 12 construyó un dispositivo. Este segundo laboratorio evaluado construye el vigilante, de la misma forma contenida: entradas sintéticas fijas, solo la biblioteca estándar, sin red.
17.1 Reglas a partir del modelo
Cada regla se deriva de un enunciado del modelo del sistema:
- NEW-TALKER: el modelo enumera los equipos que pueden hablar Modbus con las estaciones (scada-01, eng-ws-15). Cualquier otro origen es un hallazgo.
- UNAUTH-WRITE: solo la estación de ingeniería puede escribir (capítulo 2).
- DEVICE-ID-QUERY: la función 43 tiene forma de reconocimiento; alertar, con contexto.
Son deliberadamente sencillas y explicables (capítulo 8). MITRE ATT&CK for ICS cataloga las técnicas de los adversarios con las que se relacionan estas reglas, entre ellas el descubrimiento y los mensajes de órdenes no autorizadas (ATT&CK for ICS); úselo para comprobar la cobertura, no como fuente de las reglas.
17.2 Episodios
Un único equipo no autorizado puede generar decenas de alertas en un minuto. Agrupar las alertas con la misma regla y el mismo origen en un episodio (que se prolonga mientras lleguen alertas nuevas dentro de una ventana desde la anterior) da al analista un único elemento con un recuento y un intervalo de tiempo. La ventana es un compromiso: si es demasiado corta, una sola actividad se convierte en muchos episodios; si es demasiado larga, dos actividades distintas se fusionan. Anote la ventana elegida y por qué.
En el Lab 17, la ventana se mide desde la última alerta del episodio, no desde la primera, de modo que un goteo constante de alertas cada cuatro minutos prolongaría un episodio indefinidamente; limite la duración del episodio (por ejemplo, a una hora) o notifique los episodios largos como en curso con su hora más reciente. Una ventana de cinco minutos separa la escritura de las 07:12 en su propio episodio, que es lo que se quiere si, de otro modo, un analista podría pensar que la actividad se había detenido; una ventana mucho más larga la fusionaría con la ráfaga anterior y ocultaría la pausa.
17.3 La cadena se supervisa a sí misma
Un detector que descarta en silencio los registros que no puede analizar está ciego justo donde un atacante envía tráfico mal formado. La cadena debe dar cuenta de cada entrada: registros = analizados + descartados, comprobado con un assert, con los registros descartados enumerados y contados. Las orientaciones del NIST sobre supervisión continua plantean lo mismo a escala de programa: la propia supervisión debe supervisarse (SP 800-137). En el Lab 17, la comprobación es la línea assert parsed + len(dropped) == len(recs), y la línea de resumen imprime la propia lista de descartados: un registro, a las 07:03:03 desde 10.20.0.31, cuyo campo de longitud anuncia seis bytes a continuación cuando solo llegan dos. El descarte se cuenta, pero no genera ninguna alerta, porque las reglas solo se ejecutan sobre las tramas analizadas: la laguna que cierra la actividad práctica. Durante la guardia, trate el recuento de descartes como una señal en sí misma: un sensor que de pronto descarta muchos registros tiene un fallo de captura, un error de análisis o un interlocutor que envía tráfico mal formado a propósito, y cada una de esas causas merece una incidencia.
17.4 Ejemplo resuelto: la actividad de las 07:03 (sintético)
Diez registros del sensor del centro de control del 12 de octubre. Lab 17 (guárdelo como lab17.py y ejecútelo con python3 lab17.py):
# Lab 17 - contained detection pipeline for station traffic (offline, standard library only)
# Synthetic records: (UTC time, source, destination, Modbus/TCP frame hex). Rules come from Chapters 2, 9 and 10.
import struct
from datetime import datetime, timedelta
KNOWN = {"10.20.0.10": "scada-01", "10.20.0.15": "eng-ws-15"}
WRITERS = {"10.20.0.15"}
WRITE_FC = {5, 6, 15, 16}
recs = [
("07:00:00", "10.20.0.10", "10.10.4.2", "000100000006010300000004"),
("07:00:05", "10.20.0.10", "10.10.5.2", "000200000006010300000004"),
("07:02:10", "10.20.0.15", "10.10.4.2", "00030000000601060007012c"),
("07:03:00", "10.20.0.31", "10.10.4.2", "000400000006012b0e010000"),
("07:03:01", "10.20.0.31", "10.10.5.2", "000500000006012b0e010000"),
("07:03:02", "10.20.0.31", "10.10.6.2", "000600000006012b0e010000"),
("07:03:03", "10.20.0.31", "10.10.7.2", "0007000000060103"),
("07:04:40", "10.20.0.31", "10.10.4.2", "00080000000601060007ffff"),
("07:05:30", "10.20.0.31", "10.10.4.2", "00090000000601060007ffff"),
("07:12:00", "10.20.0.31", "10.10.4.2", "000a0000000601060007ffff"),
]
alerts, dropped, parsed = [], [], 0
for t, src, dst, hx in recs:
b = bytes.fromhex(hx)
if len(b) < 8 or struct.unpack(">H", b[4:6])[0] != len(b) - 6:
dropped.append((t, src, "malformed")); continue
parsed += 1; fc = b[7]
if src not in KNOWN: alerts.append((t, "NEW-TALKER", src))
if fc in WRITE_FC and src not in WRITERS: alerts.append((t, "UNAUTH-WRITE", src))
if fc == 43: alerts.append((t, "DEVICE-ID-QUERY", src))
assert parsed + len(dropped) == len(recs), "pipeline lost records"
episodes = [] # merge same rule+source within 5 minutes of the previous alert in the episode
for t, rule, src in alerts:
ts = datetime.strptime(t, "%H:%M:%S")
e = next((e for e in episodes if e["key"] == (rule, src) and ts - e["last"] <= timedelta(minutes=5)), None)
if e: e["count"] += 1; e["last"] = ts
else: episodes.append({"key": (rule, src), "first": ts, "last": ts, "count": 1})
for e in episodes:
print(f"{e['first']:%H:%M:%S}-{e['last']:%H:%M:%S}Z {e['key'][0]:<15} {e['key'][1]:<11} x{e['count']}")
print(f"records {len(recs)} parsed {parsed} dropped {len(dropped)} {dropped} alerts {len(alerts)} episodes {len(episodes)}")
Salida esperada (Python 3.13.5):
07:03:00-07:05:30Z NEW-TALKER 10.20.0.31 x5
07:03:00-07:03:02Z DEVICE-ID-QUERY 10.20.0.31 x3
07:04:40-07:05:30Z UNAUTH-WRITE 10.20.0.31 x2
07:12:00-07:12:00Z NEW-TALKER 10.20.0.31 x1
07:12:00-07:12:00Z UNAUTH-WRITE 10.20.0.31 x1
records 10 parsed 9 dropped 1 [('07:03:03', '10.20.0.31', 'malformed')] alerts 12 episodes 5
Interpretación: un equipo nunca visto, 10.20.0.31, consultó la identificación de dispositivo de tres estaciones en tres segundos, envió una trama mal formada a una cuarta y después escribió 0xFFFF en el registro 7 (el punto de consigna de la bomba) de KW-04 dos veces, y otra vez seis minutos y medio después. Cinco episodios resumen doce alertas. Dos observaciones importan más allá de las propias alertas:
- La escritura de las 07:12 es un episodio aparte porque llegó más de cinco minutos después de la alerta anterior. Un analista que leyera solo los tres primeros episodios podría pensar que la actividad se había detenido.
- La trama mal formada del interlocutor nuevo se descartó y no produjo ninguna alerta; la autocomprobación lo muestra, y es lo primero que hay que corregir: el tráfico no analizable desde un equipo desconocido debería generar por sí mismo NEW-TALKER, ya que la dirección de origen se conoce aunque la carga útil no.
El valor 0xFFFF (65.535) en un registro de punto de consigna cuyo rango de política es 100–400 es el tipo de escritura que el respondedor del Lab 12 rechazaría con la excepción 03, si el dispositivo tuviera esa política. La mayoría no la tiene, así que la detección del lado de la red y el cortafuegos (capítulo 10) son lo único que se interpone entre este equipo y el proceso. En la historia del KVROC, este es el momento de conservar, informar a Kestrel Water y abrir un incidente (capítulo 13).
17.5 Rúbrica de evaluación
| Criterio | 3 | 1 |
|---|---|---|
| Contención | Entradas fijas; sin red; declarada | Lee tráfico en servicio sin alcance definido |
| Procedencia de las reglas | Cada regla cita el enunciado del modelo que aplica | Reglas de una lista sin justificación |
| Autosupervisión | Se da cuenta de los registros; descartados enumerados | Descartes silenciosos |
| Diseño de los episodios | Ventana declarada con su compromiso | Arbitraria o inexistente |
| Puntos ciegos | Enumera lo que el sensor no puede ver (tráfico local de las estaciones, enlaces cifrados) | Afirma una cobertura completa |
| Reproducibilidad | Salida registrada con la versión de Python | Sin registrar |
Puntúe el Lab 17 en cada fila y escriba una mejora para cada puntuación inferior a tres. Los puntos ciegos deben incluir el tráfico local de las estaciones y cualquier ruta que no pase por el sensor del centro de control, como encontró el capítulo 13 para KW-04. Guarde la rúbrica puntuada con la salida del laboratorio y la versión de Python.
17.6 Actividad práctica
Cambie la cadena para que un registro descartado (mal formado) desde un origen desconocido genere NEW-TALKER, y añada una regla que alerte de cualquier escritura cuyo valor esté fuera del rango de política del registro, usando la tabla de política del Lab 12. Anote la nueva salida y cómo cambian los episodios. Antes de cambiar el código, guarde la salida actual como referencia y anote su predicción: qué episodio debería crecer y cuántas alertas debería añadir la regla de rango de valores. Tras el cambio, la línea de registros debe seguir cuadrando en diez (nueve analizados, uno descartado): corregir el descarte ciego cambia lo que produce el registro descartado, no si se cuenta. Pruebe la regla de rango de valores en ambos sentidos (debe dispararse con las escrituras de 0xFFFF y permanecer en silencio con la escritura de 300 (0x012c) de eng-ws-15 a las 07:02:10) y vuelva a puntuar después las filas de autosupervisión y de procedencia de las reglas de 17.5.
17.7 Errores comunes y límites
- Reglas copiadas de un catálogo sin un enunciado del modelo que las respalde.
- Episodios tan largos que la actividad diferenciada desaparece.
- Descartar en silencio los registros mal formados.
- Creer que el sensor lo ve todo: el capítulo 13 encontró una ruta hacia KW-04 que no pasaba por este sensor.
- Las marcas de tiempo de la cadena son las del sensor; corrija su reloj como cualquier otro (capítulo 7).
Comprobaciones que detectan estos errores:
- Para cada regla, cite la línea del modelo del sistema que aplica; una regla sin ella se elimina, o primero se actualiza el modelo.
- Ejecute los episodios con dos ventanas (como en EX067) y confirme que la escritura de las 07:12 sigue siendo visible, ya sea como elemento propio o como un intervalo prolongado.
- Haga fallar la ejecución cuando los registros no sean iguales a los analizados más los descartados, y revise la lista de descartados en cada turno.
- Mantenga una lista escrita de las rutas que eluden el sensor (routers celulares de las estaciones, tráfico de la HMI local de las estaciones) junto a la declaración de cobertura de la detección.
Ejercicios — capítulo 17
EX065 — Lea los episodios. Objetivo: interpretar la salida de la cadena. Entradas: la salida del Lab 17. Pasos: redacte un resumen de cinco líneas para el jefe de turno del KVROC usando las cuatro etiquetas. Evidencia esperada: resumen. Rúbrica: 3 = observado: equipo desconocido 10.20.0.31, consultas de identificación a tres estaciones, escrituras de 65.535 en el registro 7 de KW-04 a las 07:04:40, 07:05:30 y 07:12:00, una trama mal formada; inferido: reconocimiento seguido de intentos de cambiar el punto de consigna; hecho: no se cambió nada; abierto: identidad del equipo, efecto sobre KW-04, ruta; pide a operaciones que compruebe la bomba 2 de KW-04; 2 = sin etiquetas; 1 = vago; 0 = nada.
EX066 — Corrija el descarte ciego. Objetivo: mejorar la autosupervisión. Entradas: la actividad 17.6 (primera mitad). Pasos: impleméntela y ejecútela. Evidencia esperada: código y salida. Rúbrica: 3 = el episodio NEW-TALKER de 10.20.0.31 se amplía ahora a seis registros dentro del mismo episodio (07:03:00–07:05:30 x6), total de alertas 13; se sigue dando cuenta de los registros; 2 = funciona, recuentos sin explicar; 1 = intentado; 0 = nada.
EX067 — Compromiso de la ventana. Objetivo: razonar sobre la agrupación. Entradas: el Lab 17. Pasos: prediga el número de episodios con ventanas de 1 minuto y de 10 minutos, y después ejecute. Evidencia esperada: predicciones y salidas. Rúbrica: 3 = 1 minuto: NEW-TALKER se divide (07:03:00–07:03:02, 07:04:40–07:05:30, 07:12:00), UNAUTH-WRITE se divide en 07:04:40–07:05:30 y 07:12:00, DEVICE-ID uno → 6 episodios; 10 minutos: NEW-TALKER y UNAUTH-WRITE un episodio cada uno → 3 episodios; recomienda un valor con su motivo; 2 = casi todo correcto; 1 = conjeturas; 0 = nada.
EX068 — Regla de rango de valores. Objetivo: combinar la política del dispositivo y la detección en la red. Entradas: la actividad 17.6 (segunda mitad). Pasos: impleméntela para las funciones 6 y 16 y ejecútela. Evidencia esperada: código y salida. Rúbrica: 3 = alerta de las tres escrituras de 0xFFFF y no de la escritura de 300 de eng-ws-15; explica que la regla detecta pero no impide, así que el cortafuegos sigue siendo necesario; 2 = solo la función 6; 1 = intentado; 0 = nada.
Síntesis del capítulo
Una cadena de detección es un pequeño sistema con sus propios requisitos: reglas que remiten al modelo del sistema, episodios que resumen sin ocultar y una autocomprobación que da cuenta de cada registro. Construida de forma contenida y evaluada con honestidad, incluidos sus puntos ciegos, convierte las técnicas separadas del curso en algo sobre lo que un jefe de turno puede actuar.
Siga estudiando — capítulo 17 de 18
← Capítulo 16 · Índice de capítulos del curso · Capítulo 18 →
Continúe con la siguiente lección escrita.