Capítulo 17 — Laboratório contido II
Mapa da aula
Aula 17 publicada. Mapa, exemplo, EX067.
flowchart TD
N0["17.1 Regras a partir do modelo"]
N1["17.2 Episódios"]
N2["17.3 O pipeline monitora a si mesmo"]
N3["17.5 Rubrica de avaliação"]
N4["17.6 Atividade prática"]
N5["Síntese do capítulo"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5
N5 --> EX["Exemplo resolvido"]
EX --> Q["Exercício EX067"]
Desenho lógico
flowchart LR
N0["17.1 Regras a partir do modelo"]
N1["17.2 Episódios"]
N2["17.3 O pipeline monitora a si mesmo"]
N3["17.5 Rubrica de avaliação"]
N4["17.6 Atividade prática"]
N5["Síntese do capítulo"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX067"]
[17.1 Regras a partir do modelo]
|
v
[17.2 Episódios]
|
v
[17.3 O pipeline monitora a si mesmo]
|
v
[17.5 Rubrica de avaliação]
|
v
[17.6 Atividade prática]
|
v
[Síntese do capítulo]
|
v
[Exemplo resolvido] --> [Exercício EX067]
Objetivos de aprendizagem e conexão
Ao final deste capítulo, você deverá ser capaz de:
- integrar as técnicas do curso — decodificação (Capítulo 2), validação (Capítulo 4), regras de detecção (Capítulos 8 e 10), episódios e autoverificações — num único pipeline de detecção;
- escrever regras de detecção a partir do modelo de sistema, e não de um catálogo de produto: novo interlocutor, escrita não autorizada, consulta de identificação;
- agrupar alertas em episódios e explicar a troca envolvida na janela de agrupamento;
- fazer o pipeline monitorar a si mesmo: todo registro de entrada é analisado ou contado como descartado;
- avaliar o pipeline segundo uma rubrica e identificar os seus pontos cegos.
O Capítulo 12 construiu um dispositivo. Este segundo laboratório avaliado constrói o vigia, da mesma forma contida: entradas sintéticas fixas, apenas a biblioteca padrão, sem rede.
17.1 Regras a partir do modelo
Cada regra deriva de uma afirmação do modelo de sistema:
- NEW-TALKER: o modelo lista os hosts que podem falar Modbus com as estações (scada-01, eng-ws-15). Qualquer outra origem é uma constatação.
- UNAUTH-WRITE: apenas a estação de engenharia pode escrever (Capítulo 2).
- DEVICE-ID-QUERY: a função 43 tem a forma de reconhecimento; alertar, com contexto.
Elas são deliberadamente simples e explicáveis (Capítulo 8). O MITRE ATT&CK for ICS cataloga técnicas de adversários com as quais essas regras se relacionam, incluindo descoberta e mensagens de comando não autorizadas (ATT&CK for ICS); use-o para verificar a cobertura, não como fonte das regras.
17.2 Episódios
Um único host não autorizado pode gerar dezenas de alertas num minuto. Agrupar alertas com a mesma regra e a mesma origem num episódio — estendido enquanto novos alertas chegam dentro de uma janela a partir do anterior — dá a um analista um único item, com contagem e intervalo de tempo. A janela é uma troca: curta demais, e uma atividade vira muitos episódios; longa demais, e duas atividades separadas se fundem. Registre a janela escolhida e o porquê.
No Laboratório 17, a janela é medida a partir do último alerta do episódio, e não do primeiro; assim, um gotejar constante de alertas a cada quatro minutos estenderia um único episódio indefinidamente. Limite a duração do episódio (por exemplo, uma hora) ou relate episódios longos como em andamento, com o seu horário mais recente. Uma janela de cinco minutos separa a escrita das 07:12 num episódio próprio, o que é desejável se um analista puder, de outro modo, achar que a atividade tinha parado; uma janela muito mais longa a fundiria com a rajada anterior e esconderia o intervalo.
17.3 O pipeline monitora a si mesmo
Um detector que descarta em silêncio os registros que não consegue analisar fica cego exatamente onde um atacante envia tráfego malformado. O pipeline precisa prestar contas de toda entrada: registros = analisados + descartados, verificado por um assert, com os registros descartados listados e contados. As orientações de monitoramento contínuo do NIST fazem o mesmo argumento no nível do programa: o próprio monitoramento precisa ser monitorado (SP 800-137).
No Laboratório 17, a verificação é a linha assert parsed + len(dropped) == len(recs), e a linha de resumo imprime a própria lista de descartados: um registro, 07:03:03 vindo de 10.20.0.31, cujo campo de comprimento anuncia seis bytes seguintes quando só chegam dois. O descarte é contado, mas não gera alerta, porque as regras só rodam sobre quadros analisados — a lacuna que a atividade prática fecha. No plantão, trate a contagem de descartes como um sinal por si só: um sensor que de repente descarta muitos registros tem uma falha de captura, um bug de análise ou um interlocutor enviando tráfego deliberadamente malformado, e cada um desses casos merece um chamado.
17.4 Exemplo resolvido: a atividade das 07:03 (sintético)
Dez registros do sensor do centro de controle em 12 de outubro.
Laboratório 17 (salve como lab17.py e execute com 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)}")
Saída 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
Interpretação: um host nunca visto antes, 10.20.0.31, consultou a identificação do dispositivo em três estações em três segundos, enviou um quadro malformado a uma quarta, depois escreveu 0xFFFF no registrador 7 (o setpoint da bomba) da KW-04 duas vezes, e de novo seis minutos e meio depois. Cinco episódios resumem doze alertas. Duas observações importam além dos próprios alertas:
- A escrita das 07:12 é um episódio separado porque veio mais de cinco minutos depois do alerta anterior. Um analista que lesse apenas os três primeiros episódios poderia achar que a atividade tinha parado.
- O quadro malformado do novo interlocutor foi descartado e não gerou alerta — a autoverificação o mostra, e é a primeira coisa a consertar: o tráfego impossível de analisar vindo de um host desconhecido deveria, ele mesmo, gerar NEW-TALKER, já que o endereço de origem é conhecido mesmo quando a carga não é.
O valor 0xFFFF (65.535) num registrador de setpoint cuja faixa de política é 100–400 é o tipo de escrita que o respondedor do Laboratório 12 recusaria com a exceção 03 — se o dispositivo tivesse tal política. A maioria não tem; assim, a detecção do lado da rede e o firewall (Capítulo 10) são o que fica entre esse host e o processo. Na história do KVROC, este é o momento de preservar, informar a Kestrel Water e abrir um incidente (Capítulo 13).
17.5 Rubrica de avaliação
| Critério | 3 | 1 |
|---|---|---|
| Contenção | Entradas fixas; sem rede; declarada | Lê tráfego em produção sem escopo |
| Proveniência das regras | Cada regra cita a afirmação do modelo que aplica | Regras de uma lista, sem justificativa |
| Automonitoramento | Registros contabilizados; descartados listados | Descartes silenciosos |
| Desenho dos episódios | Janela declarada, com a troca envolvida | Arbitrária ou inexistente |
| Pontos cegos | Lista o que o sensor não consegue ver (tráfego local das estações, enlaces criptografados) | Afirma cobertura total |
| Reprodutibilidade | Saída registrada com a versão do Python | Não registrada |
Pontue o Laboratório 17 em cada linha e escreva uma melhoria para toda pontuação abaixo de três. Os pontos cegos precisam incluir o tráfego local das estações e qualquer caminho que não passe pelo sensor do centro de controle, como o Capítulo 13 encontrou na KW-04. Guarde a rubrica pontuada com a saída do laboratório e a versão do Python.
17.6 Atividade prática
Altere o pipeline para que um registro descartado (malformado) vindo de uma origem desconhecida gere NEW-TALKER, e acrescente uma regra que alerte sobre qualquer escrita cujo valor esteja fora da faixa de política do registrador, usando a tabela de política do Laboratório 12. Registre a nova saída e como os episódios mudam.
Antes de mudar o código, guarde a saída atual como linha de base e anote a sua previsão: qual episódio deve crescer e quantos alertas a regra de faixa de valores deve acrescentar. Depois da mudança, a linha de registros ainda precisa fechar em dez (nove analisados, um descartado): consertar o descarte cego muda o que o registro descartado produz, não se ele é contado. Teste a regra de faixa de valores nos dois sentidos — ela precisa disparar nas escritas de 0xFFFF e ficar em silêncio na escrita de 300 (0x012c) da eng-ws-15 às 07:02:10 — e depois pontue de novo as linhas de automonitoramento e de proveniência das regras da seção 17.5.
17.7 Erros comuns e limites
- Regras copiadas de um catálogo, sem uma afirmação do modelo por trás delas.
- Episódios tão longos que atividades separadas desaparecem.
- Descartar registros malformados em silêncio.
- Acreditar que o sensor vê tudo: o Capítulo 13 encontrou um caminho até a KW-04 que não passava por este sensor.
- Os timestamps do pipeline são os do sensor; corrija o relógio dele como o de qualquer outro (Capítulo 7).
Verificações que pegam esses erros:
- Para cada regra, cite a linha do modelo de sistema que ela aplica; uma regra sem essa linha é removida, ou o modelo é atualizado primeiro.
- Execute os episódios com duas janelas (como no EX067) e confirme que a escrita das 07:12 continua visível, seja como item próprio, seja como um intervalo estendido.
- Faça a execução falhar quando os registros não forem iguais aos analisados mais os descartados, e revise a lista de descartados a cada turno.
- Mantenha uma lista escrita dos caminhos que contornam o sensor (roteadores celulares das estações, tráfego local da IHM da estação) ao lado da declaração de cobertura da detecção.
Exercícios — capítulo 17
EX065 — Leia os episódios. Objetivo: interpretar a saída do pipeline. Insumos: a saída do Laboratório 17. Passos: escrever um resumo de cinco linhas para o líder de plantão do KVROC usando os quatro rótulos. Evidência esperada: resumo. Rubrica: 3 = observado: host desconhecido 10.20.0.31, consultas de identificação a três estações, escritas de 65.535 no registrador 7 da KW-04 às 07:04:40, 07:05:30 e 07:12:00, um quadro malformado; inferido: reconhecimento seguido de tentativas de mudança de setpoint; feito: nada mudado; em aberto: identidade do host, efeito sobre a KW-04, caminho; pede às operações uma verificação da bomba 2 da KW-04; 2 = sem rótulos; 1 = vago; 0 = nada.
EX066 — Conserte o descarte cego. Objetivo: melhorar o automonitoramento. Insumos: atividade 17.6 (primeira metade). Passos: implementar e executar. Evidência esperada: código e saída. Rubrica: 3 = o episódio NEW-TALKER de 10.20.0.31 agora se estende a seis registros dentro do mesmo episódio (07:03:00–07:05:30 x6), total de 13 alertas; registros ainda contabilizados; 2 = funciona, contagens não explicadas; 1 = tentativa; 0 = nada.
EX067 — A troca da janela. Objetivo: raciocinar sobre o agrupamento. Insumos: Laboratório 17. Passos: prever as contagens de episódios com janelas de 1 minuto e de 10 minutos, depois executar. Evidência esperada: previsões e saídas. Rubrica: 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 em 07:04:40–07:05:30 e 07:12:00, DEVICE-ID um → 6 episódios; 10 minutos: NEW-TALKER e UNAUTH-WRITE, um episódio cada → 3 episódios; recomenda um valor, com justificativa; 2 = quase tudo certo; 1 = palpites; 0 = nada.
EX068 — Regra de faixa de valores. Objetivo: combinar a política do dispositivo e a detecção na rede. Insumos: atividade 17.6 (segunda metade). Passos: implementar para as funções 6 e 16 e executar. Evidência esperada: código e saída. Rubrica: 3 = alerta sobre as três escritas de 0xFFFF e não sobre a escrita de 300 da eng-ws-15; explica que a regra detecta, mas não impede, de modo que o firewall continua necessário; 2 = apenas a função 6; 1 = tentativa; 0 = nada.
Síntese do capítulo
Um pipeline de detecção é um pequeno sistema com requisitos próprios: regras rastreáveis até o modelo de sistema, episódios que resumem sem esconder e uma autoverificação que presta contas de cada registro. Construído de forma contida e avaliado com honestidade — incluindo os seus pontos cegos —, ele transforma as técnicas separadas do curso em algo sobre o qual um líder de plantão pode agir.
Continue estudando — capítulo 17 de 18
← Capítulo 16 · Índice de capítulos do curso · Capítulo 18 →
Siga para a próxima lição escrita.