◇ OpenCyber Academy
Técnico · Capítulo 17 · Español

Capítulo 17 — Laboratorio contenido II

Aprendizaje gratuito y abierto para quienes desean proteger a las personas y la tecnología. No se necesita una cuenta para estudiar ni consultar las respuestas.

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:

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:

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:

  1. 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.
  2. 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

Comprobaciones que detectan estos errores:

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.

Respuestas desarrolladas

EX065 — Lea los episodios. «Observado: 07:03–07:12Z, el equipo desconocido 10.20.0.31 envió consultas de identificación de dispositivo a KW-04, KW-05 y KW-06, una trama mal formada a KW-07, y escribió 65.535 en el registro 7 de KW-04 (punto de consigna de la bomba 2) a las 07:04:40, 07:05:30 y 07:12:00. Inferido: reconocimiento seguido de intentos de cambiar el punto de consigna. Hecho: nada. Abierto: identidad y responsable de 10.20.0.31; si KW-04 aceptó las escrituras; cómo llegó el equipo a Z2. Petición: que Kestrel Water compruebe ahora la bomba 2 y el punto de consigna de KW-04; conservar los datos del sensor».

EX066 — Corrija el descarte ciego. En la rama de las tramas mal formadas, añada NEW-TALKER cuando el origen sea desconocido, antes de continue. Salida verificada: NEW-TALKER 07:03:00–07:05:30Z ×6, alertas 13, episodios 5, y se sigue dando cuenta de los registros (10 = 9 analizados + 1 descartado).

EX067 — Compromiso de la ventana. 1 minuto: 6 episodios; NEW-TALKER se divide en 07:03:00–07:03:02 (×3), 07:04:40–07:05:30 (×2) y 07:12:00 (×1); UNAUTH-WRITE en 07:04:40–07:05:30 (×2) y 07:12:00; DEVICE-ID uno. 10 minutos: 3 episodios; NEW-TALKER 07:03:00–07:12:00 ×6, DEVICE-ID ×3, UNAUTH-WRITE 07:04:40–07:12:00 ×3. Todo verificado ejecutándolo. Cinco minutos es un valor por defecto razonable para una actividad a ritmo humano; la configuración de 10 minutos mantiene visible la escritura de las 07:12 como parte de la misma actividad, lo que aquí es posiblemente mejor: anote la elección y revísela con datos reales.

EX068 — Regla de rango de valores. Añada POLICY = {7: (100, 400)}; para la función 6, decodifique addr, val de los bytes 8–12; para la 16, decodifique cada valor; genere la alerta VALUE-RANGE cuando una dirección de POLICY reciba un valor fuera de su rango. Se dispara con las tres escrituras de 0xFFFF y no con la escritura de 300 de eng-ws-15. Detecta pero no impide: el dispositivo puede seguir aceptando la escritura, así que el cortafuegos y los controles de acceso siguen siendo la capa preventiva.