Pausable Contracts Discussion

Welcome to the discussion thread about this lecture section. Here you can feel free to discuss the topic at hand and ask questions.

1 Like

Love the course!

Here is my code to the pauseable contract:

PauseAContract.sol
pragma solidity ^0.5.2;

contract PauseAContract{
    
    mapping (address => uint256) balances;
    address owner;
    
    bool private _paused;
    
    modifier onlyOwner(){
        require(msg.sender == owner);
        _;
    }
    
    modifier isNotPaused(){
        require(!_paused);
        _;
    }
    
    modifier isPaused(){
        require(_paused);
        _;
    }
    
    constructor() public {
        owner = msg.sender;
        _paused = false;
    }
    
    function pause() public onlyOwner payable {
        if(!_paused){
            _paused = true;
        }
        else{
            _paused = false;
        }
    }
    
    function deposit() public isNotPaused payable{
        balances[msg.sender] += msg.value;
    }
    
    function withdrawAll() public isNotPaused {
        uint toWithdraw = balances[msg.sender];
        balances[msg.sender] = 0;
        msg.sender.transfer(toWithdraw);
    }
}
2 Likes

Hi guys,

After finishing the smart contract security course, I am trying replicate the Parity Multisig Hack happened in 2017 (resource1, resource2 ), but I couldn’t get the expected effect so far.

The following are the vulnerable contracts and the Attacker contract.

MultisigParity.sol

pragma solidity ^0.8.0;

contract WalletLibrary {
     address owner;

     function initWallet(address _owner) public {
         owner = _owner;
         // ... more setup ...
     }

     function getOwnership() public view returns(address){
         return owner;
     }
     function changeOwner(address _new_owner) external {
         if (msg.sender == owner) {
             owner = _new_owner;
         }
     }

     fallback () external payable {
         
     }
     receive () external payable {
         
     }
     

     function withdraw(uint amount) external returns (bool success) {
         if (msg.sender == owner) {
             return payable(owner).send(amount);
         } else {
             return false;
         }
     }
}

contract Wallet {
    address _walletLibrary;

    constructor(address libAddress) {
        _walletLibrary = libAddress;
 
        address(_walletLibrary).delegatecall(abi.encodePacked(bytes4(keccak256("initWallet(address)")),msg.sender));
    }
    
    function getOwnership() public returns (bytes memory owner){
        (,owner ) = address(_walletLibrary).delegatecall(abi.encodePacked(bytes4(keccak256("getOwnership()"))));
        return owner;
    }

    
    function withdraw(uint amount) public returns (bool success, bytes memory data) {
        return address(_walletLibrary).delegatecall(abi.encodePacked(bytes4(keccak256("withdraw(uint)")), amount));
       
    }

    fallback () external payable {
        address(_walletLibrary).delegatecall(msg.data);
    }
    receive () external payable {
         
     }
}

Attacker.sol

pragma solidity ^0.8.0;

contract MultisigAttacker{
    
    // address owner;
    address victim;
    
    function setVictim(address target) public{ 
        victim = target; 
        
    }
   
    
    function attack() public returns (bool status, bytes memory data){ 
        (status, data) = victim.delegatecall(abi.encodePacked(bytes4(keccak256("initWallet(uint256)")), msg.sender)); 
        return (status, data);
        
    }
   
}

The vulnerable contracts split into two contracts WalletLibrary and Wallet for gas saving purpose. WalletLibrary has a public function initWallet(address _owner) to initiate the owner, and this function will be called in Wallet’s constructor through a delegatecall. The fallback function inside the Wallet contract will delegate all function calls that not within itself to WalletLibrary contract. Therefore, an Attacker contract can change the ownership of the WalletLibrary contract by using a delegatecall to Wallet contract.

function attack() public returns (bool status, bytes memory data){ 
        (status, data) = victim.delegatecall(abi.encodePacked(bytes4(keccak256("initWallet(uint256)")), msg.sender)); 
        return (status, data);
    }

The code above is used in the Attacker contract. There is no initWallet() function inside the Wallet contract, so it will fall into the fallback() function, and then be delegated to WalletLibrary to call the initWallet() function, setting the address of attacker as the owner of the WalletLibrary contract.

But I can’t get the intended effect in remix. I first deployed WalletLibrary and then deployed Wallet contract, passing WalletLibrary instance address into the constructor of the Wallet instance. Then I deployed Attacker contract, pasting Wallet’s address into the setVictim() function and the execute the attack() function. But the owner address of both Wallet and WalletLibrary didn’t change.

I am a bit lost, could anyone who have interest in the Parity Multisig Hack take a look at the code add offer me some hints? Thank you in advance!!!

Best,
Yanjun

For those who code in pragma solidity 0.8.6, here is my code:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;
import "./Ownable.sol";

contract Bank is Ownable {
    
    mapping(address => uint) balances;
    bool private _paused;
    
    constructor () {
        _paused = false;
    }
    
    //allow to execute when contract is not paused
    modifier whenNotPaused() {
        require(!_paused);
        _;
    }
    
    modifier whenPaused() {
        require(_paused);
        _;
    }
    
    function pause() public onlyOwner whenNotPaused {
        _paused = true;
    }
    
    function unPause() public onlyOwner whenPaused {
        _paused = false;
    }
    
    function withdrawAll() public whenNotPaused {
        uint amountToWithdraw = balances[msg.sender];
        balances[msg.sender] = 0;
        payable(msg.sender).transfer(amountToWithdraw);
    }
    
    function emergencyWithdrawal(address emergencyAddress) public onlyOwner whenPaused {
        //withdrawal to owner
    }
    
}
1 Like