Skip to content

13. GatekeeperOne

r1oga edited this page Oct 29, 2022 · 2 revisions

Target

Make it past the gatekeeper one.

Weakness

  • Contract relies on tx.origin.
  • Being able to read the public contract logic teaches how to pass gateTwo and gateThree.

Solidity Concepts

Be careful, conversion of integers and bytes behave differently!

conversion to uint bytes
shorter type left-truncate: uint8(273 = 0000 0001 0001 0001) = 00001 0001 = 17 right-truncate: bytes4(0x1111111122222222) = 0x11111111
larger type left-padded with 0: uint16(17 = 0001 0001) = 0000 0000 0001 0001 = 17 right-padded with 0: bytes8(0x11111111) = 0x1111111100000000

Masking means using a particular sequence of bits to turn some bits of another sequence "on" or "off" via a bitwise operation. For example to "mask off" part of a sequence, we perform an AND bitwise operation with:

  • 0 for the bits to mask
  • 1 for the bits to keep
    10101010
AND 00001111
 =  00001010

Hack

  1. Pass gateOne
    Deploy an attacker contract that will call the victim contract's enter function to ensure msg.sender != tx.origin. This is similar to what we've accomplished for the Level 4 - Telephone
  2. Pass gateTwo
    We brute force this gate by trying successively lot of different gas values.
  3. Pass gateThree
    Note that we need to pass a 8 bytes long gateKey. It is then explicitly converted to a 64 bits long integer.
    1. Part one: uint32(uint64(_gateKey)) == uint16(uint64(_gateKey))
      image Of the last 32 bits of gatekey, we want to keep the last 16 bits but remove the first 16 bits.
      0000 0000 0000 0000 1111 1111 1111 1111 = 0000 FFFF

    2. Part two: uint32(uint64(_gateKey)) != uint64(_gateKey)
      image We want to keep the first 32 bits of the 64 bits long gateKey: 1111 1111 1111 1111 1111 1111 1111 1111 = ffff ffff

    3. We then concatenate both masks: 0xFFFF FFFF 0000 FFFF

  4. Part three: uint32(uint64(gateKey)) == uint16(tx.origin)
    • we need to take gateKey = tx.origin
    • we then apply the mask on tx.origin to ensure part one and two are correct

Takeaways

  • Abstain from asserting gas consumption in your smart contracts, as different compiler settings will yield different results.
  • Be careful about data corruption when converting data types into different sizes.
  • Save gas by not storing unnecessary values.
  • Save gas by using appropriate modifiers to get functions calls for free, i.e. external pure or external view function calls are free!
  • Save gas by masking values (less operations), rather than typecasting
Clone this wiki locally