◇ OpenCyber Academy
Técnico · Capítulo 17 · Português

Capítulo 17 — Laboratório contido II

Ensino gratuito e aberto para quem deseja proteger pessoas e tecnologias. Não é preciso criar uma conta para estudar ou consultar as respostas.

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:

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:

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:

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

Verificações que pegam esses erros:

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.