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:
- escolher a ferramenta técnica certa para uma pergunta num ambiente de controle: exportação de configuração, captura passiva, decodificador de protocolo, consulta a logs, ferramenta de engenharia do fornecedor ou teste ativo;
- implementar e verificar uma checagem de integridade a partir dos seus parâmetros publicados, usando como exemplo o CRC da camada de enlace do DNP3;
- explicar a diferença entre um código de detecção de erros e a autenticação, e por que um CRC válido não prova nada sobre quem enviou um quadro;
- validar uma ferramenta antes de confiar nela: testes de resposta conhecida, testes negativos e versões documentadas.
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
- Confiar num verificador caseiro sem um teste de resposta conhecida.
- Errar a ordem dos bytes: os endereços e o CRC do DNP3 são little-endian; os campos do Modbus são big-endian.
- Tratar "CRC válido" como "autêntico".
- Sondagem ativa quando uma fonte passiva responde à pergunta.
- O Laboratório 9 verifica apenas os cabeçalhos; as camadas de transporte e de aplicação carregam os comandos e precisam da sua própria decodificação.
-
Decodificar uma captura sem registrar a versão da ferramenta e a configuração de ordem dos bytes usadas, de modo que ninguém consegue reproduzir o resultado.
-
Ler o ALERT do Laboratório 9 para o endereço de origem 77 como prova de ataque; ele só mostra um endereço ausente da lista do mestre.
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.