Back to Blog

Análise Aprofundada do Ataque à Rede Poly

Code Auditing
August 12, 2021
6 min read

O ataque consiste em duas etapas principais. A primeira etapa é alterar o keeper e a segunda etapa é sacar os tokens (executando a função unlock). A segunda etapa foi completamente analisada. Para a primeira etapa, Kevin apontou que a colisão de hash é um truque inteligente usado pelo hacker para invocar a função putCurEpochConPubKeyBytes. No entanto, o motivo pelo qual o atacante consegue ter uma transação válida para fazer essa chamada em primeiro lugar ainda é desconhecido.

Neste blog, usamos a transação maliciosa da Ontology (0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c) para ilustrar todo o processo.

Em resumo, descobrimos que:

  • O relayer da Ontology não possui mecanismos de validação suficientes para a transação proveniente da chain Ontology.
  • O atacante pode invocar diretamente a função putCurEpochConPubKeyBytes no EthCrossChainData sem passar pelo relayer do Ethereum, desde que haja um bloco válido na Poly chain.
  • A colisão de hash conforme apontado por Kevin

Aviso Legal: Este blog contém os resultados de análise da nossa equipe, que são baseados no código-fonte publicamente disponível e nas transações on-chain. Sem informações adicionais da Poly Network, não somos capazes de verificar nosso resultado.

0x.1 Transações e Contratos

Fluxo do Ataque

Transação Ontology -> Relayer Ontology -> Poly chain -> Relayer Ethereum -> Ethereum

Ethereum

0x838bf9e95cb12dd76a54c9f9d2e3082eaf928270: EthCrossChainManager
0xcf2afe102057ba5c16f899271045a0a37fcb10f2: EthCrossChainData
0x250e76987d838a75310c34bf422ea9f1ac4cc906: LockProxy

Transação: 0xb1f70464bd95b774c6ce60fc706eb5f9e35cb5f06e6cfe7c17dcda46ffd59581

Ontology

Transação: 0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c

Poly

Transação: 0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80

0x2. Fluxo do Ataque

Tomando o ataque ocorrido no Ethereum como exemplo. Este é um ataque cross-chain envolvendo três chains (e seus respectivos relayers), ou seja, Ontology Chain, Poly Chain e Ethereum.

O fluxo completo do ataque consiste em três etapas:

  1. o atacante primeiro iniciou uma transação maliciosa (0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c) na Ontology Chain;
  2. o atacante então modificou a chave pública do keeper armazenada no contrato EthCrossChainData no Ethereum;
  3. o atacante finalmente criou uma transação maliciosa para capturar os ativos criptográficos.

0x2.1 A Primeira Etapa

O atacante primeiro iniciou uma transação cross-chain (0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c) a partir da Ontology, que inclui um payload malicioso:

Você pode notar que este payload contém um nome de função criado (começando com 6631, ou seja, f1121318093 após a conversão). Este nome é definitivamente meticuloso, pois o atacante o usaria para invocar a função putCurEpochConPubKeyBytes (veja o contrato EthCrossChainData no Ethereum) explorando a colisão de hash das assinaturas de função. Aqui não ilustraremos os detalhes sobre colisão de hash, pois isso já foi bastante discutido.

Depois disso, essa transação foi aceita com sucesso pelo Relayer da Ontology Chain. Note que NÃO existe qualquer verificação estrita. Como resultado, tornou-se uma nova transação válida (0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80) na Poly Chain.

A nova transação foi então percebida e REJEITADA pelo Relayer do Ethereum. Como o Relayer do Ethereum verificou o endereço do contrato de destino (ou seja, EthCrossChainData neste caso), porém apenas o LockProxy seria permitido.

Assim, o processamento foi encerrado. No entanto, a transação com o payload malicioso foi armazenada na Poly Chain, que pode ser explorada para lançar um ataque.

0x2.2 A Segunda Etapa

O atacante enviou manualmente uma transação ao Ethereum invocando a função verifyHeaderAndExecuteTx do contrato EthCrossChainManager. Os dados da transação maliciosa armazenados na Poly Chain foram usados como entrada. Como uma transação válida da Poly Chain, foi capaz de contornar a verificação (incluindo as assinaturas e a prova merkle) na função verifyHeaderAndExecuteTx. Depois disso, a função putCurEpochConPubKeyBytes do contrato EthCrossChainData foi invocada para modificar os quatro keepers originais para um novo (ou seja, 0xA87fB85A93Ca072Cd4e5F0D4f178Bc831Df8a00B) controlado pelo atacante.

0x2.3 A Terceira Etapa

Após a modificação do keeper, o atacante foi capaz de chamar diretamente a função verifyHeaderAndExecuteTx sem usar a Poly Chain. Por fim, a função unlock do contrato LockProxy foi invocada para roubar uma enorme quantidade de ativos digitais do Ethereum. A análise detalhada pode ser encontrada em nosso relatório anterior.

0x3. Relayer

Tanto os relayers da Ontology quanto do Ethereum são implementados em Go. No entanto, eles carecem de validação suficiente, de modo que

  • O atacante pode construir uma transação maliciosa, que será incluída na Poly chain
  • O atacante pode invocar diretamente as funções no contrato inteligente EthCrossChainData no Ethereum

0x3.1 O Relayer da Ontology Confia Cegamente nas Transações Cross-chain da Ontology

O ont_relayer é responsável por escutar as transações cross-chain da chain Ontology e enviá-las para a Poly chain.

  • Side significa a chain Ontology; Alliance significa a Poly chain
  • CrossChainContractAddress é o contrato inteligente nativo (número 09) na chain Ontology

A figura acima mostra que o relayer da Ontology inicia duas rotinas para escutar as transações cross-chain de e para a chain Ontology, e a rotina para verificar o status da transação cross-chain (linha 71).

Na figura acima, o relayer da Ontology invoca a interface RPC exposta pela chain Ontology (linha 215 GetSmartContractEventByBlock) para obter eventos na chain. A partir das linhas 228 e 232, podemos ver que esta rotina escuta apenas o evento makeFromOntProof acionado pelo CrossChainContractAddress.

Na figura acima, ao processar a transação cross-chain, há cinco verificações. As duas primeiras verificam as requisições RPC (verificações 1 e 4) à chain Ontology e três verificações para saber se os parâmetros são nulos (verificações 2, 3 e 5). No entanto, não existe qualquer verificação para a semântica na transação cross-chain, ou seja, se o contrato e o nome do método são razoáveis. Por fim, envia a transação para a Poly chain (linha 183)

O relayer da Ontology constrói e envia a transação para a Poly chain usando a interface RPC (linha 164 - SendTransaction).

A função ProcessToAliianceCheckAndRetry verifica apenas se a transação falhou. Em caso afirmativo, ela reenviará a transação.

Em resumo, o ont-relayer escuta todos os eventos makeFromOntProof acionados pelo CrossChainContractAddress da chain Ontology. Em seguida, a transação será enviada para a Poly chain. Note que a transação cross-chain de qualquer pessoa na chain Ontology acionará o evento makeFromOntProof, o que resulta no envio para a Poly chain.

0x3.2 Contornando o Relayer do Ethereum

O Relayer do Ethereum é responsável por escutar as transações da Poly chain e enviá-las para o Ethereum.

O Relayer do Ethereum inicia uma Goroutine para monitorar a Poly Chain;

Ele monitora a transação cross-chain cujo alvo é o Ethereum (linhas 275 - 278). Em seguida, verifica se o contrato de destino (ToContractAddress) é um dos contratos configurados em config.TargetContracts. Caso contrário, a transação cross-chain não será enviada para a chain de destino (Ethereum).

No entanto, o atacante pode interagir diretamente com a chain de destino e invocar a função no EthCrossChainManager. Em outras palavras, a verificação no relayer do Ethereum pode ser contornada. Desde que a transação maliciosa tenha sido incluída na poly chain (o que foi alcançado na etapa anterior por meio do relayer da Ontology), o atacante pode interagir diretamente com o EthCrossChainManager. Durante este processo, a verificação de assinatura (ECCUtils.verifySig) e a prova merkle (ECCUtils.merkleProve) podem ser aprovadas, uma vez que existe uma transação válida na Poly chain.

Usando os dois métodos anteriores, o atacante pode invocar com sucesso o ToContractAddress.method no Ethereum. Combinando com a colisão de hash, a função putCurEpochConPubKeyBytes é eventualmente invocada para alterar o keeper.

Créditos

Yufeng Hu, Siwei Wu, Lei Wu, Yajin Zhou @ BlockSec

Site oficial: https://blocksec.com/

Conta oficial no Twitter: https://twitter.com/BlockSecTeam

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit