Análise de vulnerabilidades do compilador Solidity e estratégias de resposta
O compilador é um dos componentes centrais dos sistemas modernos de computadores. Ele transforma linguagens de programação de alto nível, compreensíveis para os humanos, em instruções de baixo nível executáveis pelo computador. Embora desenvolvedores e especialistas em segurança geralmente se concentrem na segurança do código de aplicação, as questões de segurança do próprio compilador também não devem ser ignoradas. Vulnerabilidades no compilador podem, em certas circunstâncias, desencadear riscos de segurança graves.
Por exemplo, em um navegador, ao interpretar e executar o código JavaScript, as vulnerabilidades do motor JavaScript podem permitir que um atacante execute código remotamente quando o usuário visita uma página maliciosa, permitindo assim que o atacante controle o navegador da vítima ou até mesmo o sistema operacional. Além disso, bugs no compilador C++ também podem resultar em consequências graves, como a execução remota de código.
O compilador Solidity não é exceção, existem vulnerabilidades de segurança em várias versões. A função do compilador Solidity é converter o código de contratos inteligentes em código de instrução da Ethereum Virtual Machine (EVM), que é finalmente executado na EVM. É importante notar que as vulnerabilidades do compilador Solidity são diferentes das vulnerabilidades da própria EVM. As vulnerabilidades da EVM referem-se a problemas de segurança durante a execução das instruções pela máquina virtual, que podem afetar toda a rede Ethereum. Já as vulnerabilidades do compilador Solidity são problemas que ocorrem ao converter o Solidity em código EVM.
Um dos perigos de uma vulnerabilidade no compilador Solidity é que o código EVM gerado pode não corresponder às expectativas do desenvolvedor. Como os contratos inteligentes geralmente envolvem os ativos de criptomoeda dos usuários, qualquer bug causado pelo compilador pode resultar em perdas de ativos para os usuários, com consequências muito graves. Desenvolvedores e auditores tendem a focar na implementação lógica dos contratos e nas vulnerabilidades comuns, enquanto as vulnerabilidades do compilador precisam ser analisadas em conjunto com versões específicas e padrões de código.
Abaixo, apresentamos alguns casos reais para demonstrar as formas, causas e perigos das vulnerabilidades do compilador Solidity.
SOL-2016-9 HighOrderByteCleanStorage
A vulnerabilidade existe em versões anteriores do compilador Solidity (>=0.1.6 <0.4.4).
Considere o seguinte código:
solidez
contract C {
uint32 a = 0x12345678;
uint32 b = 0;
função f() pública {
a = a + 1;
}
function run() public view returns (uint32) {
return b;
}
}
A variável b, sem modificações, deveria retornar o valor padrão 0 na função run(). No entanto, no código gerado pela versão vulnerável do compilador, run() retorna 1.
Esta inconsistência é difícil de detectar através de uma simples revisão de código. Embora o impacto do código de exemplo seja limitado, se a variável b for utilizada para validação de permissões ou contabilidade de ativos, as consequências poderão ser muito graves.
A causa deste problema está no fato de que o EVM usa elementos de pilha de 32 bytes, enquanto cada slot de armazenamento subjacente também é de 32 bytes. O Solidity suporta tipos de dados menores que 32 bytes, como uint32, e o compilador precisa limpar os bits mais altos ao lidar com esses tipos para garantir a correção dos dados. Neste caso, após um estouro de adição, o compilador não limpou corretamente os bits mais altos do resultado, fazendo com que o bit de estouro de 1 fosse escrito no armazenamento, sobrescrevendo a variável b.
SOL-2022-4 Efeitos Colaterais de Memória em InlineAssembly
Esta vulnerabilidade existe nas versões 0.8.13 a 0.8.15 do compilador. Considere o seguinte código:
solidity
contrato C {
function f() public pure returns (uint) {
assembly {
mstore(0, 0x42)
}
uint x;
assembly {
x := mload(0)
}
return x;
}
}
O compilador Solidity realiza uma análise profunda do fluxo de controle e dos dados durante o processo de otimização, a fim de reduzir o tamanho do código gerado e otimizar o consumo de gás. Embora essa otimização seja comum, devido à complexidade das situações, é fácil que ocorram bugs ou vulnerabilidades de segurança.
O problema do código acima advém desse tipo de otimização. O compilador acredita que se uma função modificar os dados na posição de memória 0, mas não usar mais esses dados posteriormente, pode remover a instrução de modificação para economizar gas. No entanto, essa otimização só se aplica a um único bloco de assembly.
Neste exemplo, a escrita e o acesso à memória 0 ocorrem em dois blocos de assembly diferentes. O compilador analisou apenas os blocos de assembly separadamente, considerando a escrita no primeiro bloco como redundante, e, portanto, a removeu, gerando um bug. Na versão com a vulnerabilidade, a função f() retornará 0, enquanto o valor de retorno correto deveria ser 0x42.
Normalmente, esse código deve retornar o valor da variável a "aaaa". Mas na versão com a vulnerabilidade, ele retornará uma string vazia "".
O problema é que o Solidity, ao realizar a operação abi.encode em um array do tipo calldata, erroneamente limpa alguns dados, resultando na modificação de dados adjacentes, o que causa a inconsistência dos dados após a codificação e decodificação.
É importante notar que o Solidity implicitamente codifica os parâmetros usando abi.encode durante chamadas externas e emissão de eventos, portanto, o impacto desta vulnerabilidade pode ser maior do que o esperado.
Sugestões de Segurança
Com base na análise de vulnerabilidades do compilador Solidity, são apresentadas as seguintes recomendações para desenvolvedores e profissionais de segurança:
Para os desenvolvedores:
Utilize uma versão mais recente do compilador Solidity. Embora a nova versão possa introduzir novos problemas, geralmente há menos problemas de segurança conhecidos.
Melhorar os testes unitários. A maioria dos bugs a nível de compilador pode levar a resultados de execução que não correspondem ao esperado; esses problemas são difíceis de detectar através da revisão de código, mas podem ser facilmente expostos em testes. Aumentar a cobertura do código pode minimizar ao máximo esses problemas.
Evite usar assembly inline, operações complexas de codificação e decodificação ABI, e não use novas características e funcionalidades experimentais de forma cega. A maioria das vulnerabilidades históricas está relacionada a essas operações complexas.
Para a equipe de segurança:
Não ignore os riscos de segurança que podem ser introduzidos pelo compilador durante a auditoria. O item de verificação correspondente na Classificação de Fraquezas de Contratos Inteligentes)SWC[0] é SWC-102: Versão do Compilador Desatualizada.
Durante o processo de desenvolvimento SDL, incentivar a equipe de desenvolvimento a atualizar a versão do compilador e considerar a introdução de uma verificação automática da versão do compilador no CI/CD.
Não é necessário se preocupar excessivamente com vulnerabilidades do compilador. A maioria das vulnerabilidades só é acionada em padrões de código específicos; usar contratos compilados com versões vulneráveis não implica necessariamente em risco, devendo ser avaliado de acordo com a situação específica.
Alguns recursos úteis:
Alarmes de segurança publicados regularmente pela equipe Solidity
Lista de bugs atualizada regularmente no repositório oficial do Solidity
Lista de bugs do compilador de várias versões, que pode ser usada para verificação automática em CI/CD.
O ícone de aviso no canto superior direito da página de código do contrato Etherscan pode indicar vulnerabilidades de segurança na versão atual do compilador.
Resumo
Este artigo apresenta o conceito de vulnerabilidades do compilador Solidity, analisa os riscos de segurança que podem surgir no desenvolvimento em Ethereum e fornece recomendações práticas de segurança para desenvolvedores e profissionais de segurança. Embora as vulnerabilidades do compilador não sejam comuns, seu impacto é profundo, e vale a pena que as equipes de desenvolvimento e segurança prestem atenção.
Esta página pode conter conteúdos de terceiros, que são fornecidos apenas para fins informativos (sem representações/garantias) e não devem ser considerados como uma aprovação dos seus pontos de vista pela Gate, nem como aconselhamento financeiro ou profissional. Consulte a Declaração de exoneração de responsabilidade para obter mais informações.
10 gostos
Recompensa
10
5
Partilhar
Comentar
0/400
0xInsomnia
· 07-24 15:11
A camada inferior tem falhas e a camada superior não vai ajudar.
Ver originalResponder0
0xSunnyDay
· 07-24 06:35
Quem ainda se atreve a dizer que os compiladores são seguros?
Ver originalResponder0
BankruptcyArtist
· 07-24 06:29
Todos os dias à procura de falhas, todos os dias em falência.
Ver originalResponder0
bridge_anxiety
· 07-24 06:23
A dificuldade de exploração de vulnerabilidades é um golpe crítico!
Ver originalResponder0
TokenDustCollector
· 07-24 06:13
Segurança é o começo de perder dinheiro para idiotas.
Análise de vulnerabilidades do compilador Solidity: impacto, casos e estratégias de resposta
Análise de vulnerabilidades do compilador Solidity e estratégias de resposta
O compilador é um dos componentes centrais dos sistemas modernos de computadores. Ele transforma linguagens de programação de alto nível, compreensíveis para os humanos, em instruções de baixo nível executáveis pelo computador. Embora desenvolvedores e especialistas em segurança geralmente se concentrem na segurança do código de aplicação, as questões de segurança do próprio compilador também não devem ser ignoradas. Vulnerabilidades no compilador podem, em certas circunstâncias, desencadear riscos de segurança graves.
Por exemplo, em um navegador, ao interpretar e executar o código JavaScript, as vulnerabilidades do motor JavaScript podem permitir que um atacante execute código remotamente quando o usuário visita uma página maliciosa, permitindo assim que o atacante controle o navegador da vítima ou até mesmo o sistema operacional. Além disso, bugs no compilador C++ também podem resultar em consequências graves, como a execução remota de código.
O compilador Solidity não é exceção, existem vulnerabilidades de segurança em várias versões. A função do compilador Solidity é converter o código de contratos inteligentes em código de instrução da Ethereum Virtual Machine (EVM), que é finalmente executado na EVM. É importante notar que as vulnerabilidades do compilador Solidity são diferentes das vulnerabilidades da própria EVM. As vulnerabilidades da EVM referem-se a problemas de segurança durante a execução das instruções pela máquina virtual, que podem afetar toda a rede Ethereum. Já as vulnerabilidades do compilador Solidity são problemas que ocorrem ao converter o Solidity em código EVM.
Um dos perigos de uma vulnerabilidade no compilador Solidity é que o código EVM gerado pode não corresponder às expectativas do desenvolvedor. Como os contratos inteligentes geralmente envolvem os ativos de criptomoeda dos usuários, qualquer bug causado pelo compilador pode resultar em perdas de ativos para os usuários, com consequências muito graves. Desenvolvedores e auditores tendem a focar na implementação lógica dos contratos e nas vulnerabilidades comuns, enquanto as vulnerabilidades do compilador precisam ser analisadas em conjunto com versões específicas e padrões de código.
Abaixo, apresentamos alguns casos reais para demonstrar as formas, causas e perigos das vulnerabilidades do compilador Solidity.
SOL-2016-9 HighOrderByteCleanStorage
A vulnerabilidade existe em versões anteriores do compilador Solidity (>=0.1.6 <0.4.4).
Considere o seguinte código:
solidez contract C { uint32 a = 0x12345678; uint32 b = 0; função f() pública { a = a + 1; } function run() public view returns (uint32) { return b; } }
A variável b, sem modificações, deveria retornar o valor padrão 0 na função run(). No entanto, no código gerado pela versão vulnerável do compilador, run() retorna 1.
Esta inconsistência é difícil de detectar através de uma simples revisão de código. Embora o impacto do código de exemplo seja limitado, se a variável b for utilizada para validação de permissões ou contabilidade de ativos, as consequências poderão ser muito graves.
A causa deste problema está no fato de que o EVM usa elementos de pilha de 32 bytes, enquanto cada slot de armazenamento subjacente também é de 32 bytes. O Solidity suporta tipos de dados menores que 32 bytes, como uint32, e o compilador precisa limpar os bits mais altos ao lidar com esses tipos para garantir a correção dos dados. Neste caso, após um estouro de adição, o compilador não limpou corretamente os bits mais altos do resultado, fazendo com que o bit de estouro de 1 fosse escrito no armazenamento, sobrescrevendo a variável b.
SOL-2022-4 Efeitos Colaterais de Memória em InlineAssembly
Esta vulnerabilidade existe nas versões 0.8.13 a 0.8.15 do compilador. Considere o seguinte código:
solidity contrato C { function f() public pure returns (uint) { assembly { mstore(0, 0x42) } uint x; assembly { x := mload(0) } return x; } }
O compilador Solidity realiza uma análise profunda do fluxo de controle e dos dados durante o processo de otimização, a fim de reduzir o tamanho do código gerado e otimizar o consumo de gás. Embora essa otimização seja comum, devido à complexidade das situações, é fácil que ocorram bugs ou vulnerabilidades de segurança.
O problema do código acima advém desse tipo de otimização. O compilador acredita que se uma função modificar os dados na posição de memória 0, mas não usar mais esses dados posteriormente, pode remover a instrução de modificação para economizar gas. No entanto, essa otimização só se aplica a um único bloco de assembly.
Neste exemplo, a escrita e o acesso à memória 0 ocorrem em dois blocos de assembly diferentes. O compilador analisou apenas os blocos de assembly separadamente, considerando a escrita no primeiro bloco como redundante, e, portanto, a removeu, gerando um bug. Na versão com a vulnerabilidade, a função f() retornará 0, enquanto o valor de retorno correto deveria ser 0x42.
SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup
Esta vulnerabilidade afeta os compiladores das versões 0.5.8 a 0.8.16. Considere o seguinte código:
solidity contrato C { função f(string) calldata a( externo puro retorna [1]string memória) { return abi.decode(abi.encode)a(, (string)([1]); } }
Normalmente, esse código deve retornar o valor da variável a "aaaa". Mas na versão com a vulnerabilidade, ele retornará uma string vazia "".
O problema é que o Solidity, ao realizar a operação abi.encode em um array do tipo calldata, erroneamente limpa alguns dados, resultando na modificação de dados adjacentes, o que causa a inconsistência dos dados após a codificação e decodificação.
É importante notar que o Solidity implicitamente codifica os parâmetros usando abi.encode durante chamadas externas e emissão de eventos, portanto, o impacto desta vulnerabilidade pode ser maior do que o esperado.
Sugestões de Segurança
Com base na análise de vulnerabilidades do compilador Solidity, são apresentadas as seguintes recomendações para desenvolvedores e profissionais de segurança:
Para os desenvolvedores:
Para a equipe de segurança:
Alguns recursos úteis:
Resumo
Este artigo apresenta o conceito de vulnerabilidades do compilador Solidity, analisa os riscos de segurança que podem surgir no desenvolvimento em Ethereum e fornece recomendações práticas de segurança para desenvolvedores e profissionais de segurança. Embora as vulnerabilidades do compilador não sejam comuns, seu impacto é profundo, e vale a pena que as equipes de desenvolvimento e segurança prestem atenção.