
Aviso técnico público e evidência de reprodução para CVE-2026-52134 que afeta o tratamento de replay GOOSE no libiec61850 v1.6.
O CVE-2026-52134 foi atribuído. A publicação do registro CVE correspondente está pendente.
src/goose/goose_receiver.cparseGoosePayload()O caminho de recepção do assinante GOOSE no libiec61850 v1.6 não rejeita adequadamente determinadas mensagens GOOSE repetidas (replay) ou obsoletas antes de atualizar o estado visível ao assinante e invocar o callback do listener registrado.
Dois experimentos controlados demonstram comportamento relacionado no mesmo caminho de recepção:
stNum menor: após o assinante processar stNum=2, repetir um quadro stNum=1 capturado anteriormente fez o listener reportar novamente o estado e os dados mais antigos.sqNum não crescente: quando um quadro capturado anteriormente com o mesmo stNum, porém com sqNum mais antigo, foi repetido, o quadro foi marcado como inválido, mas os callbacks do listener ainda ocorreram e os dados repetidos permaneceram visíveis.Estes são apresentados como duas observações relacionadas de um único problema de tratamento de replay/atualidade de mensagens, e não como dois CVEs separados.
Um atacante deve ser capaz de:
0x88B8Este não é um ataque remoto arbitrário baseado na Internet.
A lógica de recepção relevante verifica sqNum quando o stNum recebido é igual ao stNum armazenado. O caminho de código testado não rejeita um stNum menor antes de o estado do assinante ser atualizado e o listener ser invocado.
O arquivo de biblioteca testado não foi modificado. As cópias original e de trabalho de src/goose/goose_receiver.c tinham o mesmo valor SHA-256:
c42f2aec80f605c458d0bca524ccb09f2e35f9b83a02f854f95d9444616c075b

veth0 / veth1 do LinuxtcpdumpA bancada de teste produziu transições de estado controladas e imprimiu valores de stNum, sqNum, validade e dados visíveis no callback. O próprio arquivo vulnerável da biblioteca não foi modificado.
O publicador controlado enviou dois estados:
State A: stNum=1, sqNum=0, data=1111
State B: stNum=2, sqNum=0, data=2222
A captura de pacotes da linha de base continha dois quadros GOOSE nesta ordem:
Frame 1: stNum=1, sqNum=0
Frame 2: stNum=2, sqNum=0

A saída do assinante também continha um callback auxiliar para stNum=1, sqNum=0 marcado com valid=false antes do Estado B. Esse callback auxiliar é mantido nas evidências, mas não é usado como base para a conclusão do retrocesso. O estado decisivo anterior ao replay era o callback posterior contendo stNum=2 e o valor de dados 2222.
O script de replay carregou a captura de linha de base com dois quadros, selecionou o quadro 1 e o enviou uma vez através de veth0.
Imediatamente antes do replay, o listener havia reportado:
stNum=2, sqNum=0, valid=true, allData={2222}
Depois que o quadro antigo foi repetido uma vez, o listener reportou:
stNum=1, sqNum=0, valid=true, allData={1111}

Isso demonstra um retrocesso observado do estado do assinante de stNum=2 para stNum=1 após repetir um quadro de stNum menor capturado anteriormente.
Mais quatro cópias do mesmo quadro antigo foram enviadas. Todas as quatro continham stNum=1, sqNum=0.
As cópias posteriores foram reportadas como valid=false, mas os números de callback continuaram aumentando e os dados repetidos permaneceram visíveis:

Essa observação secundária mostra que marcar um quadro repetido como inválido não impediu a entrega ao listener no caminho de recepção testado.
Uma reprodução anterior usou o publicador e o assinante de exemplo oficiais. O publicador produziu uma sequência normal com:
stNum=1
sqNum=0, 1, 2, 3
O primeiro quadro capturado (stNum=1, sqNum=0) foi então repetido depois que o assinante já havia processado o valor posterior da sequência.

As cópias repetidas foram reportadas como inválidas porque o sqNum=0 recebido não era mais recente que o valor de sequência armazenado. No entanto, o assinante continuou a imprimir eventos do listener contendo os dados repetidos:

Este experimento suporta uma observação mais restrita, porém relacionada:
As capturas de tela brutas do replay original são mantidas como material de apoio:
Essas imagens brutas contêm avisos não relacionados de importação de módulos opcionais do Scapy. Os avisos não impediram o script de reportar os quadros GOOSE capturados e concluir a transmissão, mas as capturas de tela mais limpas acima são preferidas para revisar o resultado técnico.
Os experimentos demonstram diferentes ramificações do mesmo problema de replay/atualidade de mensagens GOOSE:
A primeira observação é o principal achado do CVE porque demonstra retrocesso de um estado mais novo para um estado mais antigo. A segunda observação é uma evidência de apoio sobre como replays inválidos do mesmo estado continuam pelo caminho do listener.
O impacto demonstrado no nível de software é:
stNum mais novo para um stNum mais antigo.Aplicações que consomem dados de callback sem aplicação independente de atualidade podem processar valores obsoletos ou repetidos.
O efeito a jusante depende da aplicação do assinante, da configuração, da lógica de intertravamento e da lógica de proteção.
Nenhum relé de proteção físico, circuito de disparo, disjuntor ou rede de subestação de produção foi testado ou operado nestes experimentos.
Antes de atualizar o estado do assinante ou invocar o listener:
stNum recebido que seja mais antigo que o último stNum aceito.stNum não mudar, rejeite um sqNum não crescente.stNum e valores repetidos de sqNum.As imagens a seguir fornecem informações de proveniência e ambiente. Elas apoiam a reprodução, mas não são individualmente necessárias para compreender o resultado principal.





O resultado demonstrado é a aceitação de replay e a entrega de estado obsoleto. Isso não depende de comprovar que a autenticação criptográfica estava habilitada na implantação testada.
Reportado por:
| Observação | Mensagem recebida | Resultado observado |
|---|
Replay de stNum menor | stNum=2 armazenado; stNum=1 antigo recebido | O estado e os dados antigos foram entregues ao listener e reportados como válidos na execução testada |
Replay de sqNum não crescente | Mesmo stNum; sqNum mais antigo ou duplicado recebido | A mensagem foi reportada como inválida, mas os callbacks do listener e a entrega dos dados repetidos continuaram |