# The Rabbit Sends in a Little Contract

• Category: Blockchain
• 300 Points
• Solved by the JCTF Team

## Description

0x046ca747d8472d2f8c070655aed06b841215d4b3

## Solution

This is the contract we got and it looks like we need to pass the flag as bytes and pass the verifier conditions.

``````/**
*Submitted for verification at Etherscan.io on 2022-12-11
*/

pragma solidity ^0.8.9;

interface IVerifier {
function verify(bytes calldata flag) external returns(bool);
}

contract CheckFlag {
IVerifier _verifier;

_verifier = IVerifier(verifier);
}

function check(bytes calldata flag) payable external returns(bool){

require(msg.value > 13333333333333333337 ether, "Please pay rabbit hole entrance fee");
require(flag.length == 18);
require(uint256(keccak256(abi.encodePacked(flag[:7], flag[17]))) == 49459084011290387902369587151867275004690538990200813105748590866129266398873);

return _verifier.verify(flag[7:17]);
}
}``````

I'm using https://emn178.github.io/online-tools/keccak_256.html to calculate keccak256

The flag is 18 bytes long and the first contract trim the first 7 seven bytes and the last one so we can guess it is `INTENT{` and `}`

``````abi.encodePacked(flag[:7], flag[17]) = "INTENT{}"
keccak256(abi.encodePacked(flag[:7], flag[17])) = "6d58d97a386ee471a3357f7561e3659cdd5e5cd80d0b698a0afa88f7a58bd699"
uint256(keccak256(abi.encodePacked(flag[:7], flag[17]))) = 49459084011290387902369587151867275004690538990200813105748590866129266398873``````

The verifier passed to the constructor:

``````000000000000000000000000726afed38eaa7aab9540c0b4b4adc66c1fb14a41

-----Decoded View---------------

-----Encoded View---------------
1 Constructor Arguments found :

The flag [7:17] bytes are passed to the verifier.

``````/**
*Submitted for verification at Etherscan.io on 2022-12-11
*/

pragma solidity ^0.8.9;

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract Verifier1 {
IVerifier _verifier;
uint value = 0x72;

_verifier = IVerifier(verifier);
}

function verify(bytes memory flag) external returns(bool){
require(uint(uint8(flag[0])) == value);
return _verifier.verify(flag);
}
}``````

flag[0] = 0x72 = 'r'

Trimmed Flag = r[X][X][X][X][X][X][X][X][X]

### Verifier 2 0x207197Bd280527E0ef590E46C6BBEBb5AeF66094

``````/**
*Submitted for verification at Etherscan.io on 2022-12-11
*/

pragma solidity ^0.8.9;

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract Verifier2 {
IVerifier _verifier;
uint _value;

owner = msg.sender;
_verifier = IVerifier(verifier);
}

function verify(bytes memory flag) external returns(bool){
require(uint(uint8(flag[1])) == _value);
return _verifier.verify(flag);
}

function setValue (uint value) external{
require(msg.sender == owner);
_value = value;
}
}``````

The flag stored in value of the contract so we can see the transaction 0xe0f9a294f62146eb1170e9a6426d70d4e6e522f36533db98d067a3c269e3cdfd

``````Function: setValue(uint256 value)

MethodID: 0x55241077
[0]:  0000000000000000000000000000000000000000000000000000000000000034``````

flag[1] = 0x34 = '4'

Trimmed Flag = r4[X][X][X][X][X][X][X][X]

``````/**
*Submitted for verification at Etherscan.io on 2022-12-11
*/

pragma solidity ^0.8.9;

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract Verifier3 {
IVerifier _verifier;
uint value = 0x66;

_verifier = IVerifier(verifier);
_target = target;
}

function verify(bytes memory flag) external returns(bool){
uint size = getSize(_target);
require(uint(uint8(flag[2])) == size-265);
return _verifier.verify(flag);
}

uint32 size;
assembly {
}
return size;
}
}``````

The address was set in the constructor to `0x9e69f292F83ED4145ed87EA21f55BefF37342781`

We got the extcode size using python:

``````infura_url='https://mainnet.infura.io/v3/TOKEN'

w3 = Web3(Web3.HTTPProvider(infura_url))
#Check Connection
print(w3.isConnected())

print(len(w3.eth.get_code("0x9e69f292F83ED4145ed87EA21f55BefF37342781")))``````

Reault: 363

flag[2] = size - 265 = 363 - 265 = 0x62 = 'b'

Trimmed Flag = r4b[X][X][X][X][X][X][X]

### Verifier 4 0x10a29fCA0D0661f8bCe563EB8beC5e0BCf529101

We need to decompile the code and understand it so we will brute-force it later :)

The same address created all the contracts so we can see Verifier 5 in 0x9ddf5b332cd24d62807b3e2e7362792897fc387c

### Verifier 5 0xcfddc063dd95ffaf4e2f32e6fbc9e49a02dec0ae

``````/**
*Submitted for verification at Etherscan.io on 2022-12-11
*/

pragma solidity ^0.8.9;

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract Verifier5 {
uint[79] public values = [126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48]; //FLAG
uint index;

owner = msg.sender;
_verifier = verifier;
_impl = implementation;
}

function verify(bytes memory flag) external returns(bool){
(bool success, bytes memory returnData) = _impl.delegatecall(
abi.encodeWithSignature("verify(bytes)", flag)
);
return abi.decode(returnData, (bool));
}

function setIndex (uint i) external{
require(msg.sender == owner);
index = i;
}
}``````

It is just calling the verifier implementation that passed to the constuctor 0x2B5F0F6a2492Fd99691e6513D96826E263989E21

When contract A executes delegatecall to contract B, B's code is executed with contract A's storage, msg.sender and msg.value.

``````/**
*Submitted for verification at Etherscan.io on 2022-12-11
*/

pragma solidity ^0.8.9;

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract ImplVerifier5 {
IVerifier _verifier;
uint[79] public values = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126];
uint index = 0;

owner = msg.sender;
_verifier = IVerifier(verifier);
}

function verify(bytes memory flag) external returns(bool){
require(uint(uint8(flag[4])) == values[index+10]);
return _verifier.verify(flag);
}

function setIndex (uint i) external{
require(msg.sender == owner);
index = i;
}
}``````

The index of Verifier 5 was set in this transaction to 0 0xc221f790e5947955d7b2c94b142639d362821ff2fffe20cb599a386266096320

flag[4] = values[index+10] = values[10] = 116 = "t"

Trimmed Flag = r4b[X]t[X][X][X][X][X]

### Verifier 6 0x0C24243F50d67f3E96ac30EB74eCe6Be8feA8EE1

``````/**
*Submitted for verification at Etherscan.io on 2022-12-11
*/

pragma solidity ^0.7.0;

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract Verifier6 {
IVerifier _verifier;
uint8 value = 0x89 + 0x66;

_verifier = IVerifier(verifier);
}

function verify(bytes memory flag) external returns(bool){
uint8 mod = 0x74;
value += mod;
require(uint(uint8(flag[5])) == value);
return _verifier.verify(flag);
}
}``````

flag[5] = (0x89 + 0x66 + 0x74) % 256 = 99 = "c"

Trimmed Flag = r4b[X]tc[X][X][X][X]

### Verifier 7 0xc3b47aA7c213279DECd826243F98D25eaf8Ffacb

``````// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract Verifier7 {
IVerifier _verifier;
IERC20 _rhol;
uint value = 0xFF;

_verifier = IVerifier(verifier);
_rhol = IERC20(token);
}

function verify(bytes memory flag) external returns(bool){
value = _rhol.totalSupply() / 10**18;
value += _rhol.balanceOf(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
value = value - 1331;
require(uint(uint8(flag[6])) == value);
return _verifier.verify(flag);
}
}``````

We need to get the totalSupply and balance of `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE` in token 0x0f117cE0915a7e4aFff87f180c87491ce3bf4E49

``````infura_url='https://mainnet.infura.io/v3/TOKEN'

w3 = Web3(Web3.HTTPProvider(infura_url))
#Check Connection
print(w3.isConnected())

totalSupply=contract.functions.totalSupply().call()
print(f"totalSupply={totalSupply}")
balanceOf=contract.functions.balanceOf("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE").call()
print(f"balanceOf={balanceOf}")``````

Result:

``````totalSupply=1337000000000000000042
balanceOf=42``````

flag[6] = (1337000000000000000042 / 10**18) + 42 - 1331 = "0"

Trimmed Flag = r4b[X]tc0[X][X][X]

### Verifier 8 0x639D7b5a3cbc0338660f445e08D961e4bcfBd9c1

``````/**
*Submitted for verification at Etherscan.io on 2022-12-11
*/

pragma solidity ^0.8.9;

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract Verifier8 {
IVerifier _verifier;

_verifier = IVerifier(verifier);
}
uint value = 0x1F;

function verify(bytes memory flag) external returns(bool){
require(uint(uint8(flag[7])) == value ^ uint(uint8(msg.data[1])));
return _verifier.verify(flag);
}
}``````

msg.data[1] is the second byte of the method id https://ethereum.stackexchange.com/a/50616

The method id is 0x8e760afe as we can see in writeContract

msg.data[1] = 0x76

flag[7] = 0x1F ^ msg.data[1] = 0x1F ^ 0x76 = 0x69 = "i"

Trimmed Flag = r4b[X]tc0i[X][X]

### Verifier 9 0xCE43F918E590B5A4f47B30574069a94F2473693b

``````// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract Verifier9 {
uint mod = 0x49;
mapping(string => uint256[]) tea;

_verifier = verifier;
tea["green"] = [0x41,0x42,0x43,0x44,0x45];
tea["black"] = [0x46,0x47,0x48,0x49,0x4A];
tea["oolong"] = [0x4B,0x4C,0x4D,0x4E,0x4F];
tea["masala"] = [0x50,0x51,0x52,0x53,0x54];
tea["earlgrey"] = [0x55,0x56,0x57,0x58,0x59];
tea["white"] = [0x5A,0x5B,0x5C,0x5D,0x5E];
tea["ginger"] = [0x5F,0x60,0x61,0x62,0x63];
tea["mint"] = [0x64,0x65,0x66,0x67,0x68];
tea["lemon"] = [0x69,0x6A,0x6B,0x6C,0x6D];
tea["chamomile"] = [0x6E,0x6F,0x70,0x71,0x72];
tea["hibiscus"] = [0x73,0x74,0x75,0x76,0x77];
tea["rooibos"] = [0x78,0x79,0x7A,0x7B,0x7C];
}

function verify(bytes memory flag) external returns(bool){
uint value;
uint slot;
slot = uint256(keccak256(abi.encode(3326828573661424032217781112562256063166426064292145799638376177296211491616)));

assembly {
}
require(uint(uint8(flag[8])) == value);
_verifier.call(
abi.encodeWithSignature("verify(bytes )", flag)
);
return true;
}
}``````

We will brute-force it later :)

### Verifier 10 0x44aC5B57C74FC129c10DCbd1d0F6c30e179Ced9a

``````// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

interface IVerifier {
function verify(bytes memory flag) external returns(bool);
}

contract Verifier10 {
IVerifier _verifier;
uint value = 0x55;

_verifier = IVerifier(verifier);
}

function verify(bytes memory flag) external returns(bool){
require(uint(uint8(flag[9])) == value);
return _verifier.verify(flag);
}
function verify1(bytes memory flag) external returns(bool){
require(uint(uint8(flag[9])) == 0x55);
return _verifier.verify(flag);
}
function verify2(bytes memory flag) external returns(bool){
require(uint(uint8(flag[9])) == 0x44);
return _verifier.verify(flag);
}
function verify3(bytes memory flag) external returns(bool){
require(uint(uint8(flag[9])) == 0x33);
return _verifier.verify(flag);
}
function verify4(bytes memory flag) external returns(bool){
require(uint(uint8(flag[9])) == 0x61);
return _verifier.verify(flag);
}
function verify5(bytes memory flag) external returns(bool){
require(uint(uint8(flag[9])) == 0x38);
return _verifier.verify(flag);
}
function verify6(bytes memory flag) external returns(bool){
require(uint(uint8(flag[9])) == 0x70);
return _verifier.verify(flag);
}
fallback() external {
bytes memory flag = abi.decode(msg.data[4:], (bytes));
require(uint(uint8(flag[9])) == 0x35);
}
}``````

In order to get the last char we need to see how Verifier 9 called this verifier

``````_verifier.call(
abi.encodeWithSignature("verify(bytes )", flag)
);``````

There is no matching function in verifier 10 because of the whitespace after the bytes, so it will get to the fallback()

flag[9] = 0x35 = "5"

Trimmed Flag = r4b[X]tc0i[X]5

Let brute-force the 2 missing chars

we will brute-force it from the last unsolved verifier in order to get valid verifier on success

### Verifier 9 (brute-force)

``````abi=json.loads('[{"inputs":[{"internalType":"address","name":"verifier","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"alice","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"flag","type":"bytes"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]')

found = False

for f9 in [0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x7C]:
try:
res=contract.functions.verify(b"r4b" + b"\xFF" + b"tc0i" + f9.to_bytes(1, byteorder="little") + b"5").call()
print(res)
found = True
except:
pass
if found:
exit()``````

Result: 110 = "n"

Trimmed Flag = r4b[X]tc0in5

### Verifier 4 (brute-force)

We will do it using the Verifier 1 because we know it's abi and we got the rest of the flag

``````abi=json.loads('[{"inputs":[{"internalType":"address","name":"verifier","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes","name":"flag","type":"bytes"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]')

found = False

for f4 in range(0x30, 0x7F):
try:
res=contract.functions.verify(b"r4b" + f4.to_bytes(1, byteorder="little") + b"tc0in5").call()
print(f4)
found = True
except:
pass
if found:
exit()``````

Result: 49 = "1"

Trimmed Flag = r4b1tc0in5

The full flag is `INTENT{r4b1tc0in5}`