Puzzle 8

GAS, CALL

Puzzle

############
# Puzzle 8 #
############

00      36        CALLDATASIZE
01      6000      PUSH1 00
03      80        DUP1
04      37        CALLDATACOPY
05      36        CALLDATASIZE
06      6000      PUSH1 00
08      6000      PUSH1 00
0A      F0        CREATE
0B      6000      PUSH1 00
0D      80        DUP1
0E      80        DUP1
0F      80        DUP1
10      80        DUP1
11      94        SWAP5
12      5A        GAS
13      F1        CALL
14      6000      PUSH1 00
16      14        EQ
17      601B      PUSH1 1B
19      57        JUMPI
1A      FD        REVERT
1B      5B        JUMPDEST
1C      00        STOP

? Enter the calldata: 

Solution

00      36        CALLDATASIZE
01      6000      PUSH1 00
03      80        DUP1
04      37        CALLDATACOPY

Equivalent to CALLDATACOPY(0, 0, calldata_size).

05      36        CALLDATASIZE
06      6000      PUSH1 00
08      6000      PUSH1 00
0A      F0        CREATE

Equivalent to CREATE(0, 0, calldata_size). Again, this means create a new contract and deposits 0 wei into it. The initialization code is at memory offset 0 and the length is the size of calldata.

0B      6000      PUSH1 00
0D      80        DUP1
0E      80        DUP1
0F      80        DUP1
10      80        DUP1
11      94        SWAP5
12      5A        GAS
13      F1        CALL

This chunk pushes 5 0x00 onto the stack and swap the topmost element with the bottommost element. Recall that the bottommost element is the result of CREATE(0, 0, calldata_size), which is the new contract's address. Now new contract's address is at the top of the stack, following 5 0x00 underneath it.

After that we encounter a new opcode GAS:

It just pushes remaining gas onto the stack. Next, we have another new opcode CALL:

Now this is equivalent to CALL(remaining_gas, new_contract_address, 0, 0, 0, 0, 0). Basically it will just call the new contract with the remaining gas and the argument is empty. This call will push 1 onto the stack if it was successful, otherwise it will push 0 onto the stack. We shall call it result.

14      6000      PUSH1 00
16      14        EQ
17      601B      PUSH1 1B
19      57        JUMPI
1A      FD        REVERT
1B      5B        JUMPDEST
1C      00        STOP

If result == 0, the control flow will goto address 0x1B, which is our destination. In other words, we want CALL(remaining_gas, new_contract_address, 0, 0, 0, 0, 0) fail.

Building calldata

The easiest way to fail the CALL is creating a new contract containing only the REVERT instruction. The same idea appeared in Ethernaut "King".

Just modify the calldata from previous level:

PUSH1 0xFD // opcode of REVERT
PUSH1 0x00 // memory offset 0
MSTORE8
PUSH1 0x01 // return just 1 byte
PUSH1 0x00 // memory offset 0
RETURN

Bytecode:

60fd60005360016000f3

Last updated