Skip to content

Instantly share code, notes, and snippets.

@sirEven
sirEven / Ethernaut Level 10 - Re-entrancy.md
Last active October 5, 2022 15:07
Ethernaut Level 10 - Re-entrancy

Key Takeaways

  • reentrancy attacks are based on recursively withdrawing an amount before the victim contract can update the balance from which the attacker is withdrawing
    • general insight for myself: This means, that the management of balances on a contract (e.g. balances[msg.sender] += msg.value) are only a superficial division of the total funds of a contract. For them to work and the funds not being falsly changed, the updating of these balances must be exactly on time and correct.
  • ELI5 Example: a bank has 10 money, Lisa has an account with 5, Peter one with 3 and Hans one with 2 money. If Hans withdraws his money, the bank has only 8 money left and needs to update the balance of Hans which is now 0. But if it fails to update that balance of Hans in time, Hans can come back and withdraw as much as his balance states, which would be still 2 money. If he does that one more time, the bank only has 6 money left. Rince and repeat until the bank is at 0 and Hans walks away with 10, instead of 2 m
@sirEven
sirEven / Ethernaut Level 9 - King.md
Last active September 21, 2022 14:39
Ethernaut Level 9 - King

Key Takeaways

  • fallback functions can be implemented in a way, that every attempt to transfer or send money to the contract will fail and thus revert the transaction
  • there are several ways to let a transaction fail, which are described in the walkthroughs in the Resources section
  • if a contract's code execution relies on a transaction to be successful, it can be exploited
  • therefore it is essential to handle transaction failures on your side in order to stay in control of what happens afterwards

Solution

  • So from what I gathered from the walkthroughs in Resources, the goal is to implement and deploy a malicious contract that will claim kingship forever. The core piece of this is the following line in the original contracts receive() function that will be called by the instance in order to reclaim kingship:
    • king.transfer(msg.value) -> if we manage to let our own contract become king, we can end the game here!
  • we can implement our malicious king contract with a receive() fun
@sirEven
sirEven / Ethernaut Level 8 - Vault.md
Last active August 26, 2022 08:53
Ethernaut Level 8 - Vault

Key Takeaways

  • sensitive data like passwords should never be put on the blockchain without encryption
  • smart contract constructor arguments can be retrieved by reading the creation bytecode on etherscan

Solution

  • the overall idea is to compare the bytecode we receive by compiling the contract on Remix to the bytecode from the contract deployment we can read on etherscan. Apart from minor differences (that may stem from different versions - not sure yet) there should be a genereal portion at the end of the creation bytecode that represents arguments passed to the constructor
  • this representation is in hex-code and can easily be translated to ascii
  • bytecode after remix compilation: `608060405234801561001057600080fd5b506040516101653803806101658339818101604052602081101561003357600080fd5b810190808051906020019092919050505060016000806101000a81548160ff021916908315150217905550806001819055505060f1806100746000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063cf309012146037578063ec
@sirEven
sirEven / Ethernaut Level 7 - Force.md
Last active August 23, 2022 19:08
Ethernaut Level 7 - Force

Key Takeaways

  • there are several ways for a contract to receive money, even if it does not have any payable functionality (see link in Resources)
  • one of which is to self destruct another contract and by doing so, sending its remaining funds forcefully to the contract we want them to arrive at

Solution

  • overview:
    • create a contract on Remix that will be destroyed in order to transfer its funds to the target address:
      • it needs a receive() function
      • and a function, that calls selfdestruct()
  • send funds to this contract
@sirEven
sirEven / Ethernaut Level 6 - Delegation.md
Last active August 22, 2022 15:38
Ethernaut Level 6 - Delegation

Key Takeaways

  • a delegate call invokes functionality from another contract, but within the context of the caller
  • so if A calls a function of B, B's function is executed with A's storage, msg.sender and msg.value
    • which also means, that only the context of A is modified by this call - B just "lends" some logic, so to say
  • the syntax to do this is quite verbose (see the example in Resources) and taking a closer look at it will help solve this challenge
  • Important: Because the caller that uses a delegate call to another contract's logic is providing its complete state alongside that call, the contract that "lends" its logic to this call has complete power over that state!

Solution

  • over all goal: Find a way to call the pnw() function of the Delegate contract, where a new owner is assigned
  • one way to get to the solution is to take a close look at the example referenced in Resources, and figure out, how the data parameter of the fallback function in our contract cou

Key Takeaways

  • uint type has a maximum and minimum that it can represent as values.
    • example: uint8 has 8 bytes that can be 0 or 1, so 2^8 (-1 for representing 0) is its maximum value which is 256-1 = 255
    • if you have a uint8 variable uint8 number = 255 and add more to it by number + 1 you generate an overflow
    • this overflow will result in the uint starting over from the other end, which is 0 (uint stands for unsigned integer, which means, that it only represents positive numbers, thus the min to max values are 0 to 255)
  • This concept goes in the other direction too, which then is an underflow - that's what will be leveraged in this solution
    • example: uint8 number = 0 now we subtract 1: number - 1 this will lead to the uint rolling over to the opposite constraint which is its max value of 255
  • the same goes for any uint; a uint is by default a uint256. Underflowing this will lead to an insanely huge number;)
  • underflowing and overflowing should be preventend
@sirEven
sirEven / Ethernaut Level 4 - Telephone.md
Last active August 3, 2022 22:01
Ethernaut Level 4 - Telephone

Key Takeaways

  • the sender of a message is not necessarily the same as the origin of a transaction
  • the message sender always refers to the previous address in the call chain
    • can be both, a contract address or a user wallet address
  • the transaction origin always refers to the original address, where the call chain started
    • can only be a user wallet address
  • best practice is to abstain from using transaction.origin if possible
  • in order to make message sender different from transaction origin, we facilitate once again a proxy contract, that calls the function of the original contract

Solution

@sirEven
sirEven / Ethernaut Level 3 - Coin Flip.md
Last active September 19, 2025 21:05
Ethernaut Level 3 - Coin Flip

Key Takeaways

  • pseudo randomness can be exploited
    • randomness based on hashing block numbers and other known data can be faked
      • if the hashing logic is known by the attacker
      • if the data to be hashed is known by the attacker
      • in this case both of which are easily obtainable, thus the problem to solve is deterministic
  • to achieve real randomness we should utilise an external source like chainlink
  • smart contracts can call functions of other smart contracts
    • this way we can facilitate a proxy contract to crack the coin flip
  • remix can be utilised to write and deploy such contracts on the fly
@sirEven
sirEven / Ethernaut Level 2 - Fallout.md
Last active August 3, 2022 22:02
#Ethernaut Level 2 - Fallout

Key Takeaways

  • again, ownership assignment should be coded in a very secure way

Solution

  • (optional) first of all, we can check that we are not yet the owner of the contract:
    • command: await contract.owner()
    • this returns an address not equal to player (command: player) which verifies, that we are not the owner.
  • we are able to take over the contract by simply calling its fallout function:
    • command: await contract.Fal1out()
  • done :-)