Análise de vulnerabilidades de ataque DoS em contratos inteligentes Rust
O ataque DoS (Denial of Service) pode fazer com que contratos inteligentes não possam ser utilizados normalmente por um período de tempo ou até permanentemente. As principais razões incluem:
Existem falhas na lógica do contrato. Por exemplo, algumas implementações de funções públicas não consideram a complexidade computacional, resultando em consumo de Gas que ultrapassa o limite.
Dependência do estado de execução de contratos externos ao chamar contratos cruzados. A execução de contratos externos pode ser não confiável e levar ao bloqueio deste contrato.
Fatores humanos, como a perda da chave privada pelo proprietário do contrato, levam à incapacidade de atualizar atempadamente o estado importante do sistema.
A seguir, analisaremos a vulnerabilidade de ataque DoS através de exemplos concretos.
1. Percorrendo estruturas de dados grandes que podem ser alteradas externamente
A seguir está um contrato simples para "dividendos" para usuários registrados:
para cur_account em self.registered.iter() {
let balance = self.accounts.get(&cur_account).expect("ERR_GET");
self.accounts.insert(&cur_account, &balance.checked_add(amount).expect("ERR_ADD"));
log!("Tentar distribuir para a conta {}", &cur_account);
ext_ft_token::ft_transfer(
cur_account.clone(),
montante,
&FTTOKEN,
0,
GAS_FOR_SINGLE_CALL
);
}
}
Os dados de estado do contrato self.registered têm tamanho ilimitado, podendo ser manipulados por usuários mal-intencionados para se tornarem excessivamente grandes. Isso leva a que a execução do distribute_token exceda o limite de consumo de Gas e falhe.
Recomenda-se mudar para o modo de retirada: a parte contratante primeiro faz a contabilidade, o usuário recupera a recompensa por meio da função withdraw. O contrato apenas precisa manter o montante de recompensa de um único usuário.
2. A dependência de estado entre contratos causa o bloqueio de contratos
A lógica deste contrato depende do retorno do token do licitante anterior para atualizar a nova oferta mais alta. Se a conta do anterior estiver encerrada, a nova oferta será bloqueada.
Solução: considerar a possibilidade de falhas em chamadas externas e aumentar o tratamento de erros. Os tokens que não podem ser devolvidos podem ser armazenados temporariamente, para que o usuário os retire posteriormente.
3. Perda da chave privada do proprietário
Algumas funções de contrato são definidas para serem executadas apenas pelo owner, para alterar variáveis essenciais do sistema. Se o owner não puder desempenhar suas funções (, como a perda da chave privada ), essas funcionalidades não estarão disponíveis, o que pode levar à paralisação do contrato.
Solução:
Definir vários proprietários para governança conjunta
Adotar um esquema de múltiplas assinaturas em vez de controle de permissões de um único proprietário
Implementar um mecanismo de governança descentralizado
Um design de permissões razoável e um mecanismo de governança podem reduzir efetivamente o risco de falhas de ponto único e aumentar a robustez dos contratos.
</accountid,balance></accountid,>
Ver original
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.
15 Curtidas
Recompensa
15
7
Compartilhar
Comentário
0/400
MEVHunterLucky
· 07-17 08:49
Percorrer em loop pode ser super perigoso. Já perdi antes.
Ver originalResponder0
BridgeNomad
· 07-15 16:21
vi este vetor de ataque exato na bsc no mês passado... quando é que os desenvolvedores vão aprender smh
Ver originalResponder0
BearMarketSurvivor
· 07-14 20:00
Código de veterano de campo de batalha retorna, as perdas de batalha devem ser compensadas com a gestão de posição.
Ver originalResponder0
BearMarketSurvivor
· 07-14 19:57
O que fazer se a chave privada se perder? Estou em pânico.
Ver originalResponder0
FlatlineTrader
· 07-14 19:56
O design do contrato é tão ruim que foi lançado.
Ver originalResponder0
MetaverseVagrant
· 07-14 19:54
Este BUG tem um nível de perigo um pouco elevado.
Ver originalResponder0
HodlVeteran
· 07-14 19:34
Uma lição amarga e dolorosa após uma derrota evidente dos Veteranos da Criptografia...
Análise de vulnerabilidades DoS em contratos inteligentes Rust: iteração em loop, dependências entre contratos e design de permissões
Análise de vulnerabilidades de ataque DoS em contratos inteligentes Rust
O ataque DoS (Denial of Service) pode fazer com que contratos inteligentes não possam ser utilizados normalmente por um período de tempo ou até permanentemente. As principais razões incluem:
Existem falhas na lógica do contrato. Por exemplo, algumas implementações de funções públicas não consideram a complexidade computacional, resultando em consumo de Gas que ultrapassa o limite.
Dependência do estado de execução de contratos externos ao chamar contratos cruzados. A execução de contratos externos pode ser não confiável e levar ao bloqueio deste contrato.
Fatores humanos, como a perda da chave privada pelo proprietário do contrato, levam à incapacidade de atualizar atempadamente o estado importante do sistema.
A seguir, analisaremos a vulnerabilidade de ataque DoS através de exemplos concretos.
1. Percorrendo estruturas de dados grandes que podem ser alteradas externamente
A seguir está um contrato simples para "dividendos" para usuários registrados:
ferrugem #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub registered: Vec\u003caccountid\u003e, pub accounts: UnorderedMap\u003caccountid, balance=""\u003e, }
pub fn register_account(&mut self) { se self.accounts.insert(&env::predecessor_account_id(), &0).is_some() { env::panic("A conta já está registrada".to_string().as_bytes()); } else { self.registered.push(env::predecessor_account_id()); } log!("Conta registrada {}", env::predecessor_account_id()); }
pub fn distribute_token(&mut self, amount: u128) { assert_eq!(env::predecessor_account_id(), DISTRIBUIDOR, "ERR_NOT_ALLOWED");
}
Os dados de estado do contrato self.registered têm tamanho ilimitado, podendo ser manipulados por usuários mal-intencionados para se tornarem excessivamente grandes. Isso leva a que a execução do distribute_token exceda o limite de consumo de Gas e falhe.
Recomenda-se mudar para o modo de retirada: a parte contratante primeiro faz a contabilidade, o usuário recupera a recompensa por meio da função withdraw. O contrato apenas precisa manter o montante de recompensa de um único usuário.
2. A dependência de estado entre contratos causa o bloqueio de contratos
Considere um contrato de "licitação":
ferrugem #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub registered: Vec, pub bid_price: UnorderedMap\u003caccountid,balance\u003e, pub current_leader: AccountId, pub highest_bid: u128, pub refund: bool }
PromiseOrValue { assert!(amount > self.highest_bid);
}
A lógica deste contrato depende do retorno do token do licitante anterior para atualizar a nova oferta mais alta. Se a conta do anterior estiver encerrada, a nova oferta será bloqueada.
Solução: considerar a possibilidade de falhas em chamadas externas e aumentar o tratamento de erros. Os tokens que não podem ser devolvidos podem ser armazenados temporariamente, para que o usuário os retire posteriormente.
3. Perda da chave privada do proprietário
Algumas funções de contrato são definidas para serem executadas apenas pelo owner, para alterar variáveis essenciais do sistema. Se o owner não puder desempenhar suas funções (, como a perda da chave privada ), essas funcionalidades não estarão disponíveis, o que pode levar à paralisação do contrato.
Solução:
Um design de permissões razoável e um mecanismo de governança podem reduzir efetivamente o risco de falhas de ponto único e aumentar a robustez dos contratos.