Breaking Ethereum 😈

Lessons learnt from broken contracts


10 September 2016

Thomson Reuters Hackethon



PΓ©ter SzilΓ‘gyi

Ethereum Core Developer

If you don't know the chain dynamics...

Ether faucet – 0x793ae8c1b1a160bfc07bfb0d04f85eab1a71f4f2

contract Faucet {
    uint amount = 0.01 ether;
    uint freq   = 5760;

    mapping (address => uint) prev;

    function request() {
        if (address(this).balance < amount) {
            return;
        }
        if(block.number < prev[msg.sender] + freq) {
            return;
        }
        msg.sender.send(amount);
        prev[msg.sender] = block.number;
    }
}

Give away 0.01 Ether to anyone, once per 24 hours... what could go wrong? πŸ˜‡

Ether faucet – pwned πŸ™ƒ

Payout (0.01 Ether) is a nice amount

Faucet security ⇔ Account uniqueness

Lesson: Accounts are free, instantaneous and infinite!

Roulette – 0x5fe5b7546d1628f7348b023a0393de1fc825a4fd

Sizeable implementation of a roulette game

contract Roulette {
    uint seed = 1;

    function rand() private returns (uint) {
        seed = ((seed*3 + 1)/2 % 10**9);
        
        return (seed + block.number + block.difficulty + block.timestamp + block.gaslimit) % 37;
    }
    [...]
}

Uses an onchain random number generator... what could go wrong? πŸ˜‡

Roulette – pwned πŸ™ƒ

Miners make the chain

Transactions are aware of the chain

Lesson: Blockchain state is free for all to use and abuse!

Etherdice – 0x2faa316fc4624ec39adc2ef7b5301124cfb68777

Fairly involved dice game

contract Dice {
    function evaluate(bytes32 seed) {
        // [...] verify the seed
        for (uint i = 0; i < bets.length; i++) {
            // [...] evaluate bets and pay winners
        }
    }
    [...]
}

Iterate over all accumulated bets in one go... what could go wrong? πŸ˜‡

Etherdice – self pwned πŸ™ƒ

Blocks have limited gas allowances

Etherdice iterated all bets when closing a round

Lesson: Operations above O(1) will eventually exceed the gas limit!*

If you don't know the language dynamics...

GovernMental – 0xf45717552f12ef7cb65e95476f217ea008167ae3

Twisted Ponzi scheme with smart contracts

contract GovernMental {
    address[] creditors; uint[] credited;

    function invest() {
        if (block.timestamp - lastInvested < TWELVE_HOURS) { ... } else {
            creditors = new address[](0);
            credited  = new uint[](0);
        }
    }
    [...]
}

Casually reset the contract at round end... what could go wrong? πŸ˜‡

GovernMental – self pwned πŸ™ƒ

Contract storage in EVM is a single hash map

Freeing up a field ⇔ zeroing out a storage entry

Lesson: Understand and avoid magical constructs!

King of the Ether – 0xb336a86e2feb1e87a328fcb7dd4d04de3df254d0

Game of Thrones pyramid contract

contract KingOfTheEtherThrone {
    address monarch;

    function claim() {
        // [...] calculate the ruler's compensation

        monarch.send(compensation);
        monarch = msg.sender;
    }
    [...]
}

Send blindly to compensate the previous ruler... what could go wrong? πŸ˜‡

King of the Ether – broken πŸ™ƒ

Sending funds is an external CALL operation

But what if the ether transfer failed?

Lesson: Anything that can go wrong, will go wrong!

If you don't know the EVM dyamics...

MakerDAO – 0xe02640be68df835aa3327ea6473c02c8f6c3815a

Contracts and frameworks for an on-chain crypto exchange

contract MakerEthToken {
    function withdraw(uint amount) {
        if (balances[msg.sender] >= amount) {
            if (msg.sender.call.value(amount)()) {
                balances[msg.sender] -= amount;
            }
        }
    }
    [...]
}

Send funds with full gas allowance... what could go wrong? πŸ˜‡

MakerDAO – preventive pwned πŸ™ƒ

Calling another contract relinquishes execution

Recipient may have enough gas to call further

Lesson: External calls will eventually loop back in!

Pre-homestead multisig wallet

Wallet contract requiring multiple authorizations

contract MultiOwned {
    mapping(uint => uint) owners;

    modifier onlyowner {
        if (owners[tx.origin] > 0) {
            _
        }
    }
    [...]
}

Simply use tx.origin for authentication... what could go wrong? πŸ˜‡

Pre-homestead multisig wallet – swapped before pwn πŸ™ƒ

Pre-homestead, libraries used CALLCODE

Internal transactions retain the same tx.origin

Lesson: Authorization forwarding is exceptionally risky!

TheDAO – 0xbb9bc244d798123fde783fcc1c72d3bb8c189413

Lesson: Don't! Just don't!

Beer keg challenge 🍺

Beer keg – 0x629469c8db3a4d7bcc3a823effcf8900119ba7e7

Untappable beer contract

contract BeerKeg {
    bytes20 prev; // Nickname of the previous tap attempt

    function tap(bytes20 nickname) {
        prev = nickname;
        if (prev != nickname) {
            msg.sender.send(this.balance);
        }
    }
}
Lesson: You tell me! 😈

Legacy of the fallen ones... ଘ(ΰ©­*ΛŠα΅•Λ‹)ΰ©­

Thank you

PΓ©ter SzilΓ‘gyi

Ethereum Core Developer

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)