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

Capítulo 9 — Infraestrutura crítica e segurança setorial: ferramentas e métodos

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 9 — Infraestrutura crítica e segurança setorial: ferramentas e métodos

Mapa da aula

Aula 09 publicada. Mapa, exemplo, EX033.

flowchart TD
  N0["9.1 Escolher um método"]
  N1["9.2 O DNP3 na camada de enlace"]
  N2["9.3 Valide a ferramenta primeiro"]
  N3["9.6 Atividade prática"]
  N4["Síntese do capítulo"]
  N0 --> N1 --> N2 --> N3 --> N4
  N4 --> EX["Exemplo resolvido"]
  EX --> Q["Exercício EX033"]

Desenho lógico

flowchart LR
  N0["9.1 Escolher um método"]
  N1["9.2 O DNP3 na camada de enlace"]
  N2["9.3 Valide a ferramenta primeiro"]
  N3["9.6 Atividade prática"]
  N4["Síntese do capítulo"]
  N0 --> N1 --> N2 --> N3 --> N4 --> Q["EX033"]
[9.1 Escolher um método]
    |
    v
[9.2 O DNP3 na camada de enlace]
    |
    v
[9.3 Valide a ferramenta primeiro]
    |
    v
[9.6 Atividade prática]
    |
    v
[Síntese do capítulo]
    |
    v
[Exemplo resolvido] --> [Exercício EX033]

Objetivos de aprendizagem e conexão

Ao final deste capítulo, você deverá ser capaz de:

Os Capítulos 7 e 8 usaram a telemetria. Este capítulo trata dos próprios instrumentos — como escolhê-los, construir corretamente os pequenos e saber quando eles mentem.

9.1 Escolher um método

Pergunta Preferir Evitar, salvo com escopo
O que está configurado? Exportação de configuração, visão somente de leitura da ferramenta do fornecedor Sondar portas para inferir a configuração
Quem conversa com quem? Logs de fluxo, captura passiva numa porta SPAN ou num tap Varredura ativa
O que um dispositivo executa? Registros de ativos, inventário da ferramenta de engenharia durante uma sessão aprovada Identificação (fingerprinting) por sondagem
Uma regra de bloqueio funciona? Logs do firewall mais um teste de conexão com escopo Varreduras amplas
Um quadro está bem formado? Decodificador com verificações de protocolo (Capítulo 2, este capítulo) Olhar o hexadecimal a olho

O guia de segurança de TO da NIST SP 800-82 Rev. 3 discute o monitoramento passivo e os riscos das técnicas ativas (SP 800-82 Rev. 3). Analisadores de pacotes como o Wireshark incluem dissectores para Modbus e DNP3; os seus próprios scripts servem para as verificações que essas ferramentas não fazem por você, e para uma automação repetível e revisável.

9.2 O DNP3 na camada de enlace

O DNP3 é amplamente usado no telecontrole de eletricidade e de água. Cada quadro da camada de enlace começa com um cabeçalho de 10 bytes: bytes de início 05 64, um byte de comprimento, um byte de controle, um endereço de destino de 2 bytes e um de origem de 2 bytes (little-endian) e um CRC de 2 bytes. Os dados do usuário seguem em blocos de até 16 bytes, cada um com o seu próprio CRC. No byte de controle, o bit 7 (DIR) é ligado nos quadros vindos do mestre, o bit 6 (PRM) nos quadros primários (iniciadores), e os quatro bits baixos são o código de função do enlace — por exemplo, primária 0 "reset link states", primária 4 "unconfirmed user data", secundária 11 "link status". O DNP Users Group publica uma visão geral; a norma completa é a IEEE 1815 (DNP3 overview).

O CRC é o CRC-16/DNP: polinômio 0x3D65, valor inicial 0, entrada e saída refletidas, XOR final 0xFFFF, com o valor de verificação de catálogo 0xEA82 para a string ASCII 123456789 (CRC catalogue). Refletido, o polinômio é 0xA6BC, que é a constante usada numa implementação bit a bit.

9.3 Valide a ferramenta primeiro

Uma rotina de CRC sutilmente errada continua produzindo números. Antes de usá-la, execute um teste de resposta conhecida: calcule o CRC de 123456789 e compare com 0xEA82. Depois, um teste negativo: mude um bit de um quadro válido e confirme que a verificação falha. O Laboratório 9 faz os dois — a linha assert é o teste de resposta conhecida —, e registre a versão do Python, porque o resultado de qualquer ferramenta só é evidência se a ferramenta estiver identificada.

Mantenha no script o assert que verifica o valor de catálogo e faça o job falhar se ele não se sustentar depois de uma mudança na função de CRC. Registre a versão do Python ao lado de todo resultado que será usado como evidência. Execute o teste negativo num quadro que você mesmo corrompeu, para saber que o verificador rejeita CRCs ruins antes de confiar nele numa captura.

9.4 Detecção de erros não é autenticação

Um CRC é calculado a partir de parâmetros públicos; qualquer um que consiga enviar um quadro consegue calcular um CRC válido. Ele detecta corrupção acidental, não falsificação. O Laboratório 9 mostra isso diretamente: um quadro vindo de um endereço de origem desconhecido (77) tem um CRC válido. Distinguir o genuíno do falsificado exige autenticação criptográfica (o DNP3 Secure Authentication, ou um túnel protegido) ou contexto — endereços de origem conhecidos, caminhos conhecidos, temporização esperada. Compare com o Capítulo 15 do livro de tecnólogo, que usa HMAC (RFC 2104) para a autenticidade (RFC 2104).

9.5 Exemplo resolvido: verificar os cabeçalhos da SS-3 (sintético)

O mestre da Kestrel Grid (endereço DNP3 1) faz a varredura da outstation da subestação SS-3 (endereço 10) por meio do fep-01. Um técnico capturou seis cabeçalhos de enlace.

Laboratório 9 (salve como lab09.py e execute com python3 lab09.py):

# Lab 9 - check DNP3 link-layer headers and their CRC (offline, standard library only)
# CRC-16/DNP: poly 0x3D65 (reflected 0xA6BC), init 0x0000, reflected in/out, xorout 0xFFFF.
# Synthetic frames between the Kestrel Grid master (address 1) and substation SS-3 outstation (address 10).
import struct
def crc_dnp(data: bytes) -> int:
    crc = 0
    for byte in data:
        crc ^= byte
        for _ in range(8):
            crc = (crc >> 1) ^ 0xA6BC if crc & 1 else crc >> 1
    return crc ^ 0xFFFF
assert crc_dnp(b"123456789") == 0xEA82          # catalogue check value
def header(length, ctrl, dest, src):
    h = bytes([0x05, 0x64, length, ctrl]) + struct.pack("<HH", dest, src)
    return h + struct.pack("<H", crc_dnp(h))
capture = [
    ("master->SS-3", header(0x05, 0xC0, 10, 1).hex()),     # primary function 0: reset link states
    ("SS-3->master", header(0x05, 0x0B, 1, 10).hex()),     # secondary function 11: link status
    ("master->SS-3", header(0x11, 0xC4, 10, 1).hex()),     # primary function 4: unconfirmed user data
    ("master->SS-3", "056411c40a000100" + "ffff"),          # wrong CRC
    ("unknown->SS-3", header(0x11, 0xC4, 10, 77).hex()),   # source not in station table
    ("master->SS-3", "0565" + header(0x05, 0xC0, 10, 1).hex()[4:]),  # bad start bytes
]
KNOWN_SOURCES = {1}
for label, hx in capture:
    b = bytes.fromhex(hx)
    if b[:2] != b"\x05\x64": print(f"{label:<14} {hx}  REJECT start bytes {b[:2].hex()}"); continue
    got = struct.unpack("<H", b[8:10])[0]; want = crc_dnp(b[:8])
    length, ctrl, dest, src = b[2], b[3], *struct.unpack("<HH", b[4:8])
    if got != want: print(f"{label:<14} {hx}  REJECT crc {got:04x} != {want:04x}"); continue
    direction = "from master" if ctrl & 0x80 else "from outstation"
    note = "" if src in KNOWN_SOURCES or src == 10 else "  ALERT unknown source address"
    print(f"{label:<14} {hx}  ok len={length} ctrl=0x{ctrl:02x} ({direction}, fc={ctrl & 0x0F}) dest={dest} src={src}{note}")

Saída esperada (Python 3.13.5):

master->SS-3   056405c00a000100b1ac  ok len=5 ctrl=0xc0 (from master, fc=0) dest=10 src=1
SS-3->master   0564050b01000a006ded  ok len=5 ctrl=0x0b (from outstation, fc=11) dest=1 src=10
master->SS-3   056411c40a0001000615  ok len=17 ctrl=0xc4 (from master, fc=4) dest=10 src=1
master->SS-3   056411c40a000100ffff  REJECT crc ffff != 1506
unknown->SS-3  056411c40a004d0073e6  ok len=17 ctrl=0xc4 (from master, fc=4) dest=10 src=77  ALERT unknown source address
master->SS-3   056505c00a000100b1ac  REJECT start bytes 0565

Três quadros são válidos e esperados. Um tem o CRC ffff onde se esperava 1506 — corrupção ou um quadro fabricado; num enlace ruidoso, uma sequência de falhas de CRC é uma constatação de qualidade do enlace para o provedor de telecomunicações. Um tem bytes de início errados e é rejeitado antes de qualquer outra coisa ser lida. A linha mais importante é a que passa no CRC: o endereço de origem 77 não está na tabela de estações. O CRC diz que os bytes chegaram íntegros; só a verificação do endereço diz que vieram de um lugar inesperado.

9.6 Atividade prática

Estenda o Laboratório 9 para verificar o CRC de cada bloco de dados do usuário de 16 bytes que segue o cabeçalho. Construa um quadro com 17 bytes de dados do usuário (um bloco completo e um bloco de 1 byte), calcule os CRCs dos dois blocos e corrompa um byte no segundo bloco para mostrar que a verificação o pega.

Corrompa um byte no segundo bloco e confirme que só a verificação desse bloco falha. Guarde as etapas de construção nas notas do laboratório, para que um revisor possa reconstruir o mesmo quadro, e anote qual byte você corrompeu e o CRC esperado e o calculado desse bloco.

9.7 Erros comuns e limites

Exercícios — capítulo 9

EX033 — Teste de resposta conhecida. Objetivo: validar uma ferramenta. Insumos: a crc_dnp do Laboratório 9. Passos: (1) substituir 0xA6BC por 0x3D65 e executar; (2) explicar o resultado. Evidência esperada: saída e explicação. Rubrica: 3 = o assert falha, porque um algoritmo que desloca para a direita (refletido) precisa do polinômio refletido; o teste de resposta conhecida pegou um bug plausível antes que qualquer quadro fosse julgado; 2 = observa a falha, sem explicação; 1 = tentativa; 0 = nada.

EX034 — Decodifique o byte de controle. Objetivo: ler os campos de enlace do DNP3. Insumos: os bytes de controle 0xC9, 0x44 e 0x0B. Passos: dar DIR, PRM e o código de função de cada um e nomear a função. Evidência esperada: três linhas. Rubrica: 3 = 0xC9: DIR 1, PRM 1, fc 9 request link status; 0x44: DIR 0, PRM 1, fc 4 unconfirmed user data vindo de uma outstation (por exemplo, uma resposta não solicitada); 0x0B: DIR 0, PRM 0, fc 11 link status; 2 = um errado; 1 = apenas os bits; 0 = nada.

EX035 — Experimento mental de falsificação. Objetivo: separar integridade e autenticidade. Insumos: um quadro com CRC válido, endereço de origem 1 e destino 10, chegando à outstation a partir de um endereço da rede MPLS que não é o fep-01. Passos: explicar o que o CRC mostra, o que o endereço mostra e o que realmente estabeleceria a autenticidade. Evidência esperada: explicação curta. Rubrica: 3 = o CRC mostra que não houve corrupção acidental; o endereço DNP3 1 é autodeclarado; a autenticidade exige o Secure Authentication ou um túnel criptográfico que termine em pontas conhecidas, mais a aplicação do caminho pela rede; 2 = parcial; 1 = "CRC válido, logo genuíno"; 0 = nada.

EX036 — Seleção de método. Objetivo: escolher o método menos intrusivo. Insumos: perguntas: (a) que endereços de mestre DNP3 a SS-3 viu neste mês; (b) se o firewall do fep-01 permite apenas a outstation; (c) a versão de firmware da outstation. Passos: escolher um método para cada uma e justificar. Evidência esperada: três escolhas. Rubrica: 3 = (a) captura passiva ou logs do fep-01 analisados com um decodificador; (b) exportação da configuração mais os logs, depois um teste com escopo, se necessário; (c) registro de ativos ou ferramenta do fornecedor durante uma sessão aprovada, com a concordância do engenheiro de proteção; 2 = uma escolha intrusiva; 1 = varredura; 0 = nada.

Síntese do capítulo

Escolha o método menos intrusivo que responda à pergunta e valide cada ferramenta antes de confiar nela: testes de resposta conhecida, testes negativos, versões registradas. O CRC da camada de enlace do DNP3 mostra essa disciplina em miniatura — implemente-o a partir dos parâmetros publicados, confira-o frente ao valor de catálogo — e também o seu limite: a detecção de erros prova que os bytes chegaram íntegros, nunca quem os enviou.

Continue estudando — capítulo 09 de 18

← Capítulo 08 · Índice de capítulos do curso · Capítulo 10 →

Siga para a próxima lição escrita.