Acessando variáveis privadas

porMatheusem20/06/2022

Nesse artigo iremos aprender a como um contrato malicioso age para acessar variáveis privadas e como previnir um ataque em seu contrato inteligente.

Vulnerabilidade

Todos os dados de um contrato inteligente podem ser lidos.
Vamos ver como podemos ler dados de variáveis definidas como private. No processo, você aprenderá como o Solidity armazena as variáveis de estado.

OBS: não pode usar web3 na JVM, então use o contrato implementado na Testenet Ropsten (ETH).
OBS: o navegador Web3 é antigo, então use o Web3 do console do Truffle.

Contrato implantado na Testnet Ropsten com endereço 0x3505a02BCDFbb225988161a95528bfDb279faD6b

Armazenar

  • 2 ** 256 slots
  • 32 bytes para cada slot
  • os dados são armazenados sequencialmente na ordem de declaração
  • o armazenamento é otimizado para economizar espaço. Se as variáveis vizinhas couberem em um único 32 bytes, elas serão empacotadas no mesmo slot, começando da direita
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Vault {
// slot 0
uint public count = 123;
// slot 1
address public owner = msg.sender;
bool public isTrue = true;
uint16 public u16 = 31;
// slot 2
bytes32 private password;
// constantes não usam armazenamento
uint public constant someConst = 123;
// slot 3, 4, 5 (um para cada elemento da matriz)
bytes32[3] public data;
struct User {
uint id;
bytes32 password;
}
// slot 6 - comprimento do array
// começando do slot hash(6) - elementos do array
// slot onde o elemento do array é armazenado = keccak256(slot)) + (index * elementSize)
// onde slot = 6 e elementSize = 2 (1 (uint) + 1 (bytes32))
User[] private users;
// slot 7 - vazio
// as entradas são armazenadas em hash(chave, slot)
// onde slot = 7, key = map key
mapping(uint => User) private idToUser;
constructor(bytes32 _password) {
password = _password;
}
function addUser(bytes32 _password) public {
User memory user = User({id: users.length, password: _password});
users.push(user);
idToUser[user.id] = user;
}
function getArrayLocation(
uint slot,
uint index,
uint elementSize
) public pure returns (uint) {
return uint(keccak256(abi.encodePacked(slot))) + (index * elementSize);
}
function getMapLocation(uint slot, uint key) public pure returns (uint) {
return uint(keccak256(abi.encodePacked(key, slot)));
}
}
/*
slot 0 - contar
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", 0, console.log)
slot 1 - u16, isTrue, proprietário
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", 1, console.log)
slot 2 - senha
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", 2, console.log)
slot 6 - tamanha da array
getArrayLocation(6, 0, 2)
web3.utils.numberToHex("111414077815863400510004064629973595961579173665589224203503662149373724986687")
Nota: Também podemos usar o web3 para obter a localização dos dados
web3.utils.soliditySha3({ type: "uint", value: 6 })
1st usuário
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f", console.log)
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40", console.log)
Nota: use web3.toAscii para converter bytes32 em alfabeto
2nd usuário
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d41", console.log)
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d42", console.log)
slot 7 - vazio
getMapLocation(7, 1)
web3.utils.numberToHex("81222191986226809103279119994707868322855741819905904417953092666699096963112")
Nota: Também podemos usar o web3 para obter a localização dos dados
web3.utils.soliditySha3({ type: "uint", value: 1 }, {type: "uint", value: 7})
usuário 1
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", "0xb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b828", console.log)
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", "0xb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b829", console.log)
*/

Técnicas Preventivas

  • Não armazene informações confidenciais na blockchain.

Testar no Remix