Análise de vulnerabilidades do compilador Solidity e estratégias de mitigação
O compilador é uma das partes fundamentais dos sistemas de computação modernos. É um programa de computador cuja função é converter o código-fonte de linguagens de programação de alto nível, que são fáceis de entender e escrever para os humanos, em códigos de instrução executáveis que podem ser processados pela CPU de um computador ou por uma máquina virtual de bytecode.
A maioria dos desenvolvedores e profissionais de segurança geralmente se preocupa mais com a segurança do código de aplicativos, mas pode ignorar a segurança do próprio compilador. Na verdade, como um programa de computador, o compilador também pode apresentar vulnerabilidades de segurança, e as vulnerabilidades de segurança geradas pelo compilador podem, em certas circunstâncias, representar sérios riscos de segurança. Por exemplo, durante o processo de compilação e interpretação do código frontend em Javascript pelo navegador, pode ocorrer uma exploração de vulnerabilidades do mecanismo de interpretação do Javascript, levando os usuários a serem atacados ao acessar páginas maliciosas, o que pode resultar na execução remota de código por parte do atacante, controlando assim o navegador da vítima ou até mesmo o sistema operacional. Estudos demonstraram que bugs no compilador Clang C++ também podem levar a consequências graves, como a execução remota de código.
O compilador Solidity não é exceção, de acordo com o alerta de segurança da equipe de desenvolvimento do Solidity, existem vulnerabilidades de segurança em várias versões diferentes do compilador Solidity.
Vulnerabilidade do compilador Solidity
O compilador Solidity tem a função de converter o código do contrato inteligente escrito pelos desenvolvedores em código de instrução da Máquina Virtual Ethereum (EVM). Esses códigos de instrução EVM são empacotados e enviados para o Ethereum através de transações, sendo finalmente analisados e executados pela EVM.
É necessário distinguir entre as vulnerabilidades do compilador Solidity e as vulnerabilidades da própria EVM. As vulnerabilidades da EVM referem-se a falhas de segurança que ocorrem quando a máquina virtual executa instruções. Como os atacantes podem carregar qualquer código na Ethereum, esse código acabará por ser executado em cada programa cliente P2P da Ethereum. Se houver vulnerabilidades de segurança na EVM, isso afetará toda a rede Ethereum, podendo causar uma negação de serviço (DoS) e até permitir que toda a cadeia seja totalmente controlada pelos atacantes. No entanto, devido ao design relativamente simples da EVM e ao fato de que o código central não é atualizado com frequência, a probabilidade de surgirem esses problemas é relativamente baixa.
Vulnerabilidades do compilador Solidity referem-se a falhas que ocorrem quando o compilador converte Solidity em código EVM. Ao contrário de cenários em que navegadores compilam e executam Javascript no computador do usuário, o processo de compilação do Solidity ocorre apenas no computador do desenvolvedor de contratos inteligentes, não sendo executado na rede Ethereum. Portanto, vulnerabilidades do compilador Solidity não afetam diretamente a própria rede Ethereum.
Uma das principais ameaças de uma vulnerabilidade no compilador Solidity é que pode levar a um código EVM gerado que não corresponde às expectativas do desenvolvedor do contrato inteligente. Como os contratos inteligentes na Ethereum geralmente envolvem os ativos de criptomoeda dos usuários, qualquer bug causado pelo compilador que resulte na geração de contratos inteligentes pode causar perdas de ativos para os usuários, trazendo consequências graves.
Os desenvolvedores e auditores de contratos podem se concentrar em problemas de implementação da lógica do código do contrato, bem como em questões de segurança no nível do Solidity, como reentrância e estouro de inteiros. No entanto, é difícil detectar vulnerabilidades do compilador Solidity apenas por meio da auditoria da lógica do código-fonte do contrato. É necessário analisar em conjunto a versão específica do compilador e padrões de código específicos para determinar se o contrato inteligente é afetado por vulnerabilidades do compilador.
Exemplo de vulnerabilidade do compilador Solidity
A seguir, através de vários casos reais de vulnerabilidades do compilador Solidity, são mostradas as formas específicas, 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
contrato C {
uint32 a = 0x12345678;
uint16 b = 0x1234;
função f() pública {
a = a + 1;
}
function run() public view returns (uint16) {
return b;
}
}
A variável de armazenamento b não foi modificada, portanto a função run() deve retornar o valor padrão 0. No entanto, o código gerado pela versão do compilador com vulnerabilidade retornará 1.
Sem conhecer a vulnerabilidade do compilador, é difícil para um desenvolvedor comum descobrir, através de uma simples revisão de código, os bugs presentes no código acima. O código acima é apenas um exemplo simples, portanto, não causará danos especialmente graves. Mas se a variável b for utilizada para verificação de permissões, contabilidade de ativos, etc., essa inconsistência com o esperado pode levar a consequências muito graves.
A razão para a anomalia mencionada acima é que o EVM utiliza uma máquina virtual baseada em pilha, onde cada elemento da pilha tem um tamanho de 32 bytes (, ou seja, o tamanho da variável uint256 ). Por outro lado, cada slot no armazenamento subjacente também tem 32 bytes de tamanho. No nível da linguagem Solidity, são suportados tipos de dados como uint32 e outros menores que 32 bytes, e o compilador, ao lidar com esse tipo de variável, precisa realizar operações apropriadas de limpeza nos bits mais altos (clean up) para garantir a correção dos dados. Na situação mencionada, quando a adição resulta em um estouro de inteiro, o compilador não limpa corretamente os bits mais altos do resultado, levando ao fato de que o bit de 1 alto do estouro é escrito no armazenamento, sobrescrevendo a variável a e, consequentemente, alterando o valor da variável b para 1.
( SOL-2022-4 InlineAssemblyMemorySideEffects
A vulnerabilidade existe nas versões do compilador >=0.8.13 <0.8.15. O compilador Solidity, ao converter a linguagem Solidity em código EVM, não se limita a uma simples tradução. Ele também realiza uma análise profunda do fluxo de controle e dos dados, implementando vários processos de otimização de compilação para reduzir o tamanho do código gerado e otimizar o consumo de gás durante o processo de execução. Esse tipo de operação de otimização é muito comum em compiladores de várias linguagens de alto nível, mas devido à complexidade das situações a serem consideradas, também é fácil que ocorram bugs ou vulnerabilidades de segurança.
Considere o seguinte código:
solidez
contrato C {
function f)### public pure returns (uint256) {
assembly {
mstore(0, 0x42)
}
uint256 x;
assembleia {
x := mload(0)
}
return x;
}
}
A vulnerabilidade do código acima decorre da otimização de compilação. Em certas situações, se houver código na função que modifica dados na posição de memória 0, mas não houver nenhum outro lugar subsequente que utilize esses dados, então é possível remover diretamente o código que modifica a memória 0, economizando gas e sem afetar a lógica do programa subsequente.
Esta estratégia de otimização em si não tem problemas, mas na implementação específica do código do compilador Solidity, tal otimização é aplicada apenas dentro de um único bloco de assembly. No código de exemplo acima, a escrita e o acesso à memória 0 existem em dois blocos de assembly diferentes, enquanto o compilador analisa e otimiza apenas o bloco de assembly separado. Como não há operações de leitura após a escrita na memória 0 no primeiro bloco de assembly, a instrução de escrita é considerada redundante e será removida, resultando em um bug. Na versão com vulnerabilidade, a função f() retornará o valor 0, enquanto na realidade, o código acima deveria retornar o valor correto 0x42.
A vulnerabilidade afeta compiladores >= 0.5.8 < 0.8.16. Considere o seguinte código:
solidez
contract C {
function f)bytes calldata data### external pure returns (bytes memory) {
bytes4( memória a = [bytes4)dados[1]];
return abi.encode(a);
}
}
Normalmente, o código acima deve retornar que a variável a é "aaaa". Mas na versão com vulnerabilidades, retorna uma string vazia "".
A causa da vulnerabilidade é que o Solidity, ao realizar operações abi.encode em arrays do tipo calldata, limpou erroneamente certos dados, resultando na modificação de outros dados adjacentes e causando inconsistências nos dados após a codificação e decodificação.
É importante notar que o Solidity, ao realizar chamadas externas e emitir eventos, codifica implicitamente os parâmetros usando abi.encode, portanto, a probabilidade de ocorrer o código de vulnerabilidade mencionado é maior do que a intuição sugere.
A vulnerabilidade foi adaptada para um problema de blockchain na famosa competição de segurança 0ctf 2022, demonstrando o impacto das vulnerabilidades do compilador nos contratos inteligentes em cenários de desenvolvimento reais.
Sugestões de Segurança
Para as ameaças e respostas às vulnerabilidades do compilador Solidity, podem ser dadas as seguintes recomendações:
Para desenvolvedores:
Utilize uma versão mais recente do compilador Solidity. Embora novas versões possam introduzir novos problemas de segurança, os problemas de segurança conhecidos geralmente são menos que nas versões antigas.
Melhorar os casos de teste unitário. A maioria dos bugs a nível de compilador pode causar resultados de execução de código que não correspondem às expectativas. Este tipo de problema é difícil de detectar através de revisões de código, mas é fácil de expor na fase de testes. Portanto, aumentar a cobertura do código pode evitar ao máximo esse tipo de problema.
Evite usar assembly em linha, operações complexas como codificação e decodificação ABI para arrays multidimensionais e estruturas complexas sempre que possível. Evite o uso cego de novos recursos da linguagem e funcionalidades experimentais na ausência de necessidades claras. A maioria das vulnerabilidades está relacionada a operações como assembly em linha e codificadores ABI. O compilador é mais propenso a bugs ao lidar com características complexas da linguagem. Por outro lado, os desenvolvedores também podem cometer erros ao usar novos recursos, levando a problemas de segurança.
Para o pessoal de segurança:
Ao realizar uma auditoria de segurança no código Solidity, não negligencie os riscos de segurança que o compilador Solidity pode introduzir. O item de verificação correspondente na Classificação de Fraquezas de Smart Contracts(SWC) é SWC-102: Versão do Compilador Obsoleta.
No processo de desenvolvimento de segurança interna, exortar a equipe de desenvolvimento a atualizar a versão do compilador Solidity e considerar a introdução de verificações automáticas da versão do compilador no processo CI/CD.
Mas não é necessário se preocupar excessivamente com as vulnerabilidades do compilador, a maioria das vulnerabilidades do compilador só é ativada em padrões de código específicos. Os contratos compilados com versões vulneráveis do compilador não apresentam necessariamente riscos de segurança, e é necessário avaliar o impacto real da segurança com base nas circunstâncias específicas do projeto.
Alguns recursos úteis:
Alarmes de segurança publicados regularmente pela equipe Solidity:
Lista de bugs atualizada regularmente do repositório oficial do Solidity:
Lista de bugs do compilador de várias versões:
No Etherscan, o triângulo com ponto de exclamação no canto superior direito da página do Código do Contrato 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 ambiente de desenvolvimento 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 pode ser grave, o que merece a atenção de desenvolvedores e profissionais de segurança.
Esta página pode conter conteúdo de terceiros, que é fornecido apenas para fins informativos (não para representações/garantias) e não deve ser considerada como um endosso de suas opiniões pela Gate nem como aconselhamento financeiro ou profissional. Consulte a Isenção de responsabilidade para obter detalhes.
22 Curtidas
Recompensa
22
7
Compartilhar
Comentário
0/400
fork_in_the_road
· 07-19 10:48
Compiladores também podem ter vulnerabilidades? Para que tanta preocupação!
Ver originalResponder0
gas_fee_therapist
· 07-19 08:57
Este bug está bem explicado ah~
Ver originalResponder0
GateUser-75ee51e7
· 07-18 18:19
É melhor encontrar um bug do que fazer Mineração.
Ver originalResponder0
MetaverseHobo
· 07-16 19:40
O compilador também falhou? Esta onda deixou-me nervoso.
Ver originalResponder0
DeepRabbitHole
· 07-16 19:39
Que desastre, tantos buracos e ainda jogando com a Posição de bloqueio?
Ver originalResponder0
GasWaster
· 07-16 19:37
O compilador também tem vulnerabilidades? yyds
Ver originalResponder0
ForkTongue
· 07-16 19:23
A melhor maneira de ganhar dinheiro é explorando falhas em cartões.
Análise de vulnerabilidades do compilador Solidity: impactos, casos e estratégias de resposta
Análise de vulnerabilidades do compilador Solidity e estratégias de mitigação
O compilador é uma das partes fundamentais dos sistemas de computação modernos. É um programa de computador cuja função é converter o código-fonte de linguagens de programação de alto nível, que são fáceis de entender e escrever para os humanos, em códigos de instrução executáveis que podem ser processados pela CPU de um computador ou por uma máquina virtual de bytecode.
A maioria dos desenvolvedores e profissionais de segurança geralmente se preocupa mais com a segurança do código de aplicativos, mas pode ignorar a segurança do próprio compilador. Na verdade, como um programa de computador, o compilador também pode apresentar vulnerabilidades de segurança, e as vulnerabilidades de segurança geradas pelo compilador podem, em certas circunstâncias, representar sérios riscos de segurança. Por exemplo, durante o processo de compilação e interpretação do código frontend em Javascript pelo navegador, pode ocorrer uma exploração de vulnerabilidades do mecanismo de interpretação do Javascript, levando os usuários a serem atacados ao acessar páginas maliciosas, o que pode resultar na execução remota de código por parte do atacante, controlando assim o navegador da vítima ou até mesmo o sistema operacional. Estudos demonstraram que bugs no compilador Clang C++ também podem levar a consequências graves, como a execução remota de código.
O compilador Solidity não é exceção, de acordo com o alerta de segurança da equipe de desenvolvimento do Solidity, existem vulnerabilidades de segurança em várias versões diferentes do compilador Solidity.
Vulnerabilidade do compilador Solidity
O compilador Solidity tem a função de converter o código do contrato inteligente escrito pelos desenvolvedores em código de instrução da Máquina Virtual Ethereum (EVM). Esses códigos de instrução EVM são empacotados e enviados para o Ethereum através de transações, sendo finalmente analisados e executados pela EVM.
É necessário distinguir entre as vulnerabilidades do compilador Solidity e as vulnerabilidades da própria EVM. As vulnerabilidades da EVM referem-se a falhas de segurança que ocorrem quando a máquina virtual executa instruções. Como os atacantes podem carregar qualquer código na Ethereum, esse código acabará por ser executado em cada programa cliente P2P da Ethereum. Se houver vulnerabilidades de segurança na EVM, isso afetará toda a rede Ethereum, podendo causar uma negação de serviço (DoS) e até permitir que toda a cadeia seja totalmente controlada pelos atacantes. No entanto, devido ao design relativamente simples da EVM e ao fato de que o código central não é atualizado com frequência, a probabilidade de surgirem esses problemas é relativamente baixa.
Vulnerabilidades do compilador Solidity referem-se a falhas que ocorrem quando o compilador converte Solidity em código EVM. Ao contrário de cenários em que navegadores compilam e executam Javascript no computador do usuário, o processo de compilação do Solidity ocorre apenas no computador do desenvolvedor de contratos inteligentes, não sendo executado na rede Ethereum. Portanto, vulnerabilidades do compilador Solidity não afetam diretamente a própria rede Ethereum.
Uma das principais ameaças de uma vulnerabilidade no compilador Solidity é que pode levar a um código EVM gerado que não corresponde às expectativas do desenvolvedor do contrato inteligente. Como os contratos inteligentes na Ethereum geralmente envolvem os ativos de criptomoeda dos usuários, qualquer bug causado pelo compilador que resulte na geração de contratos inteligentes pode causar perdas de ativos para os usuários, trazendo consequências graves.
Os desenvolvedores e auditores de contratos podem se concentrar em problemas de implementação da lógica do código do contrato, bem como em questões de segurança no nível do Solidity, como reentrância e estouro de inteiros. No entanto, é difícil detectar vulnerabilidades do compilador Solidity apenas por meio da auditoria da lógica do código-fonte do contrato. É necessário analisar em conjunto a versão específica do compilador e padrões de código específicos para determinar se o contrato inteligente é afetado por vulnerabilidades do compilador.
Exemplo de vulnerabilidade do compilador Solidity
A seguir, através de vários casos reais de vulnerabilidades do compilador Solidity, são mostradas as formas específicas, 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 contrato C { uint32 a = 0x12345678; uint16 b = 0x1234;
função f() pública { a = a + 1; }
function run() public view returns (uint16) { return b; } }
A variável de armazenamento b não foi modificada, portanto a função run() deve retornar o valor padrão 0. No entanto, o código gerado pela versão do compilador com vulnerabilidade retornará 1.
Sem conhecer a vulnerabilidade do compilador, é difícil para um desenvolvedor comum descobrir, através de uma simples revisão de código, os bugs presentes no código acima. O código acima é apenas um exemplo simples, portanto, não causará danos especialmente graves. Mas se a variável b for utilizada para verificação de permissões, contabilidade de ativos, etc., essa inconsistência com o esperado pode levar a consequências muito graves.
A razão para a anomalia mencionada acima é que o EVM utiliza uma máquina virtual baseada em pilha, onde cada elemento da pilha tem um tamanho de 32 bytes (, ou seja, o tamanho da variável uint256 ). Por outro lado, cada slot no armazenamento subjacente também tem 32 bytes de tamanho. No nível da linguagem Solidity, são suportados tipos de dados como uint32 e outros menores que 32 bytes, e o compilador, ao lidar com esse tipo de variável, precisa realizar operações apropriadas de limpeza nos bits mais altos (clean up) para garantir a correção dos dados. Na situação mencionada, quando a adição resulta em um estouro de inteiro, o compilador não limpa corretamente os bits mais altos do resultado, levando ao fato de que o bit de 1 alto do estouro é escrito no armazenamento, sobrescrevendo a variável a e, consequentemente, alterando o valor da variável b para 1.
( SOL-2022-4 InlineAssemblyMemorySideEffects
A vulnerabilidade existe nas versões do compilador >=0.8.13 <0.8.15. O compilador Solidity, ao converter a linguagem Solidity em código EVM, não se limita a uma simples tradução. Ele também realiza uma análise profunda do fluxo de controle e dos dados, implementando vários processos de otimização de compilação para reduzir o tamanho do código gerado e otimizar o consumo de gás durante o processo de execução. Esse tipo de operação de otimização é muito comum em compiladores de várias linguagens de alto nível, mas devido à complexidade das situações a serem consideradas, também é fácil que ocorram bugs ou vulnerabilidades de segurança.
Considere o seguinte código:
solidez contrato C { function f)### public pure returns (uint256) { assembly { mstore(0, 0x42) } uint256 x; assembleia { x := mload(0) } return x; } }
A vulnerabilidade do código acima decorre da otimização de compilação. Em certas situações, se houver código na função que modifica dados na posição de memória 0, mas não houver nenhum outro lugar subsequente que utilize esses dados, então é possível remover diretamente o código que modifica a memória 0, economizando gas e sem afetar a lógica do programa subsequente.
Esta estratégia de otimização em si não tem problemas, mas na implementação específica do código do compilador Solidity, tal otimização é aplicada apenas dentro de um único bloco de assembly. No código de exemplo acima, a escrita e o acesso à memória 0 existem em dois blocos de assembly diferentes, enquanto o compilador analisa e otimiza apenas o bloco de assembly separado. Como não há operações de leitura após a escrita na memória 0 no primeiro bloco de assembly, a instrução de escrita é considerada redundante e será removida, resultando em um bug. Na versão com vulnerabilidade, a função f() retornará o valor 0, enquanto na realidade, o código acima deveria retornar o valor correto 0x42.
( SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup
A vulnerabilidade afeta compiladores >= 0.5.8 < 0.8.16. Considere o seguinte código:
solidez contract C { function f)bytes calldata data### external pure returns (bytes memory) { bytes4( memória a = [bytes4)dados[1]]; return abi.encode(a); } }
Normalmente, o código acima deve retornar que a variável a é "aaaa". Mas na versão com vulnerabilidades, retorna uma string vazia "".
A causa da vulnerabilidade é que o Solidity, ao realizar operações abi.encode em arrays do tipo calldata, limpou erroneamente certos dados, resultando na modificação de outros dados adjacentes e causando inconsistências nos dados após a codificação e decodificação.
É importante notar que o Solidity, ao realizar chamadas externas e emitir eventos, codifica implicitamente os parâmetros usando abi.encode, portanto, a probabilidade de ocorrer o código de vulnerabilidade mencionado é maior do que a intuição sugere.
A vulnerabilidade foi adaptada para um problema de blockchain na famosa competição de segurança 0ctf 2022, demonstrando o impacto das vulnerabilidades do compilador nos contratos inteligentes em cenários de desenvolvimento reais.
Sugestões de Segurança
Para as ameaças e respostas às vulnerabilidades do compilador Solidity, podem ser dadas as seguintes recomendações:
Para desenvolvedores:
Utilize uma versão mais recente do compilador Solidity. Embora novas versões possam introduzir novos problemas de segurança, os problemas de segurança conhecidos geralmente são menos que nas versões antigas.
Melhorar os casos de teste unitário. A maioria dos bugs a nível de compilador pode causar resultados de execução de código que não correspondem às expectativas. Este tipo de problema é difícil de detectar através de revisões de código, mas é fácil de expor na fase de testes. Portanto, aumentar a cobertura do código pode evitar ao máximo esse tipo de problema.
Evite usar assembly em linha, operações complexas como codificação e decodificação ABI para arrays multidimensionais e estruturas complexas sempre que possível. Evite o uso cego de novos recursos da linguagem e funcionalidades experimentais na ausência de necessidades claras. A maioria das vulnerabilidades está relacionada a operações como assembly em linha e codificadores ABI. O compilador é mais propenso a bugs ao lidar com características complexas da linguagem. Por outro lado, os desenvolvedores também podem cometer erros ao usar novos recursos, levando a problemas de segurança.
Para o pessoal de segurança:
Ao realizar uma auditoria de segurança no código Solidity, não negligencie os riscos de segurança que o compilador Solidity pode introduzir. O item de verificação correspondente na Classificação de Fraquezas de Smart Contracts(SWC) é SWC-102: Versão do Compilador Obsoleta.
No processo de desenvolvimento de segurança interna, exortar a equipe de desenvolvimento a atualizar a versão do compilador Solidity e considerar a introdução de verificações automáticas da versão do compilador no processo CI/CD.
Mas não é necessário se preocupar excessivamente com as vulnerabilidades do compilador, a maioria das vulnerabilidades do compilador só é ativada em padrões de código específicos. Os contratos compilados com versões vulneráveis do compilador não apresentam necessariamente riscos de segurança, e é necessário avaliar o impacto real da segurança com base nas circunstâncias específicas do projeto.
Alguns recursos úteis:
Alarmes de segurança publicados regularmente pela equipe Solidity:
Lista de bugs atualizada regularmente do repositório oficial do Solidity:
Lista de bugs do compilador de várias versões:
No Etherscan, o triângulo com ponto de exclamação no canto superior direito da página do Código do Contrato 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 ambiente de desenvolvimento 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 pode ser grave, o que merece a atenção de desenvolvedores e profissionais de segurança.