Posted on :: Tags: ,

Hello readers, I played TCP1P CTF alone (but not anymore). I am focused on blockchain challenges only this time and I managed to solved All blockchain challenges within few hours of CTF. I got many new friends and like minded people in during this CTF. I am excited to share those challenge writeups here.

Blockchain

Venue

Description :

Look at the Amazing Party Venue So do you wish to enter?
contract: 0x1AC90AFd478F30f2D617b3Cb76ee00Dd73A9E4d3
provider: https://eth-sepolia.g.alchemy.com/v2/SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8
Priv-Key: Please use your own private-key, if you need ETH for transact, You can either DM the Author, or get it by yourself at https://sepoliafaucet.com/

Attached Files : [Venue.sol, 101.txt]

It is just a warmup challenge, can be simply done by loading contract on remix, but I wrote a simple ethersJS script to interact with the contract.

101.txt

Feeling Confuse?

Here's how you can attempt this challenge
First thing first you need to know what the code does,
then you need to write a code using web3.js or web3.py

Okay lets see how the contract looks like,

Venue.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract Venue{
    string private flag;
    string private message;

    constructor(string memory initialFlag, string memory initialMessage){
        flag = initialFlag;
        message = initialMessage;
    }

    function enterVenue() public view returns(string memory){
        return flag;
    }

    function goBack() public view returns(string memory){
        return message;
    }
}

Okay, what we have to do here is to call the enterVenue() function which will return the flag for us.

To interact with a contract on blockchain we need few things.

- Contract ABI
- Contract Address
- RPC URI
- Few Test ETH

✅ We can get Test ETH from faucets

✅ RPC URI is provided

✅ Contract Address is available

Now lets get the ABI using solc compiler. We can get ABI using Remix too, but do it in command line.

mj0ln1r@Linux:~/venue$ solc Venue.sol --combined-json abi
{"contracts":{"Venue.sol:Venue":{"abi":[{"inputs":[{"internalType":"string","name":"initialFlag","type":"string"},{"internalType":"string","name":"initialMessage","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"enterVenue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"goBack","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]}},"version":"0.8.21+commit.d9974bed.Linux.g++"}

✅ We got ABI too

Lets write a simple Ethers JS script to interact with our contract.

require('dotenv').config();
const ethers = require('ethers');

ADDRESS = '0x1AC90AFd478F30f2D617b3Cb76ee00Dd73A9E4d3'
ABI = [{"inputs":[{"internalType":"string","name":"initialFlag","type":"string"},{"internalType":"string","name":"initialMessage","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"enterVenue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"goBack","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
// provider
const provider = new ethers.AlchemyProvider('sepolia', process.env.API_KEY);

const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
async function main(){
    const venueContract = new ethers.Contract(ADDRESS, ABI, wallet)
    const message = await venueContract.goBack();
    console.log(message);
    const flag = await venueContract.enterVenue(); 
    console.log(flag);
}
main()

To make this script works you need to do few things.

  • Install Dependencies

npm install --save ethers && npm install dotenv

  • Make sure to use .env

sample .env

PRIVATE_KEY=853024a95f52d73fe68a50e4ee11a1a2a6818ef71b63eccea9c07edde5e595c
API_KEY=SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8

Now if we run the javascript code above then the flag will be printed to the screen.

mj0ln1r@Linux:~/venue$ node solve.js
But.... You've come this far... come on! just go inside!
TCP1P{d0_3nj0y_th3_p4rty_bu7_4r3_y0u_4_VIP?}

Flag : TCP1P{d0_3nj0y_th3_p4rty_bu7_4r3_y0u_4_VIP?}

Location

Description :

Will you accept the invitation? If so, find the party location now!

Challenge : nc ctf.tcp1p.com 20005

Lets connect to challenge

mj0ln1r@Linux:~/venue$ nc ctf.tcp1p.com 20005
====Going to The Party====

To Find the party location
You need to solve a simple riddle regarding a SLOT
Answer everything correctly, and find the exact location!

Question: In which Slot is Password Stored?

You'll answer with and ONLY WITH [numbers]
ex: 
0,1,2,3,4.....99

Note: 
    -   Slot start from 0
    -   If it doesn't stored on SLOT, answer 0

Identification Required for Guest

Question:

contract StorageChallenge2 {
    address[2] private investor;
    uint64 private password;
    bytes32 private user;
    address private owner;
}

Answer: 

Lets understood challenge clearly

  • It is EVM storage puzzle
  • We have to find the storage slot of the password
  • Slots starts from 0
  • If password not stored on storage then answer will be 0

So, Lets learn EVM storage layout

State variables of contracts are stored in storage in a compact way such that multiple values sometimes use the same storage slot. Except for dynamically-sized arrays and mappings (see below), data is stored contiguously item after item starting with the first state variable, which is stored in slot 0.

The below image will illustrate the storage layout of the EVM.

State variable stored in the continous manner in storage slots of EVM. Each slot of size 32 bytes or 256 bits only. See an example how the state variables are stores in the storage slots.

Contract MyContract {
  uint256 zero;                          // in slot 0
  mapping(address => uint256) one;    // in slot 1
  address two;                          // in slot 2 
  bool a;                              // in slot 3 - 1 byte
  uint8 b;                             // in slot 3 - 1 byte 
  bytes16 c;                           // in slot 3 - 16 bytes
  address immutable noWhere;           // Do not stored on storage
}

The Solidity dont store constant or immutable variables in storage instead the vaues will replaced in every occurrence of these variables with their assigned value in the contract’s bytecode.

Enough knowedge for the challenge, Lets solve challenge.

Here in Challenge2 the password is stored in slot 2 as address[2] will reserve slot 0 and slot 1 for two addresses. Slot 2 will be allocated to password.

contract StorageChallenge2 {
    address[2] private investor; // slot 0 - 1
    uint64 private password;  // slot 2
    bytes32 private user;  // slot 3
    address private owner;  // slot 4
}

Lets see Challenge7, Here the password declared as immutable so it wont stored in any slot so the answer would be 0.

contract StorageChallenge7 {
    bytes32 private key;
    bytes4 private key_1;
    bytes16 private key_2;
    address private owner;
    uint256 private Token;
    address private immutable Investor;
    address private Courier;
    bytes32 private immuatble password;  // no slot for password 
}

Similiarly calculated storage slots for all the 10 challenges manually.

mj0ln1r@Linux:~/location$ nc ctf.tcp1p.com 20005
====Going to The Party====

To Find the party location
You need to solve a simple riddle regarding a SLOT
Answer everything correctly, and find the exact location!

Question: In which Slot is Password Stored?

You'll answer with and ONLY WITH [numbers]
ex: 
0,1,2,3,4.....99

Note: 
    -   Slot start from 0
    -   If it doesn't stored on SLOT, answer 0

Identification Required for Guest

Question:

contract StorageChallenge7 {
    bytes32 private key;
    bytes4 private key_1;
    bytes16 private key_2;
    address private owner;
    uint256 private Token;
    address private immutable Investor;
    address private Courier;
    bytes32 private immuatble password;
}

Answer: 0
===================================
Question:

contract StorageChallenge1 {
    bytes32 private user;
    address[2] private investor;
    uint64 private password;
    address private owner;
}

Answer: 3
===================================
Question:

contract StorageChallenge8 {
    bytes32 private key;
    bytes4 private key_1;
    bytes16 private key_2;
    address private owner;
    uint256 private Token;
    address private immutable Investor;
    address private Courier;
    uint256 private lump_sum;
    bytes32 private password;
}

Answer: 6
===================================
Question:

contract StorageChallenge4 {
    uint64 private password;
    bytes32 private user;
    address[2] private investor;
    bytes32 immutable  passphrase
    address private owner;
}

Answer: 0
===================================
Question:

contract StorageChallenge10 {
    bool private string_true;
    bool private number_false;
    bool private user_true;
    bytes32 private username;
    bytes32 private password;
    bool public status_creds;
}

Answer: 2
===================================
Question:

contract StorageChallenge3 {
    address private owner;
    uint64 private password;
    bytes32 private user;
    address[2] private investor;
}

Answer: 0
===================================
Question:

contract StorageChallenge2 {
    address[2] private investor;
    uint64 private password;
    bytes32 private user;
    address private owner;
}

Answer: 2
===================================
Question:

contract StorageChallenge9 {
    bytes32 private unique_code;
    bytes32 private 12_key;
    address private owner;
    address[20] public player;
    bool private valid;
    bytes32 private password;
    address private enemy;
    bool private answered;
}

Answer: 24
===================================
Question:

contract StorageChallenge5 {
    address[2] private investor;
    bytes32 private user;
    address private owner;
    uint64 private password;
}

Answer: 3
===================================
Question:

contract StorageChallenge6 {
    bytes32 immutable passphrase;
    uint64 private password;
    bytes32 private user;
    address[2] private investor;
    address private owner;
}

Answer: 0
===================================
Final Question:

contract Hell_0 {
    uint256 private avail_money;
    uint256 private saved_money;
    bool private not_minus;
    address private owner;
    uint256[2] private geo_loc;
    bool private is_there;
    bool private there;
    address private wallet;
    address private receiver;
    address[20] private transaction_list;
    bytes32 private user_creds;
    uint256 private immutable user_in_uint;
    bytes32 private password;
    uint256 private password_uint;
    bool private correct_password;
    bool private is_user;
}

Answer: 28
Go to Camelleion Street 78
TCP1P{W00t_w00t_t0_th3_p4rty_47JHbddc}

We can calculate the storage slots from remix too. Compile each contract on Remix and go to compilation details and view STORAGELAYOUT.

Flag : TCP1P{W00t_w00t_t0_th3_p4rty_47JHbddc}

Invitation

Description :

An Invitation to an amazing party, only if you find the right location.

Note: Please read the 101.txt.
Attached Files : [Invitation.txt, 101.txt]

101.txt

Description:
    You are provided a bytecode there, yeah?
    Find out a way to get a certain function name from it,
    the correct function name begin with "TCP1P" string.

Flag Format
    if you manage to find the correct function name
    do the exact same thing as the example below
    
    Found Function name: TCP1P_th1s_1s_4_fl4g_()
        -> remove the "()"
        -> replace the first "_" with "{"
        -> replace the last "_" with "}"
    
    Final and Right flag format: TCP1P{th1s_1s_4_fl4g}

Invitation.txt

60806040526040518060400160405280601e81526020017f546865207269676874206f6e652068617320544350315020737472696e670000815250600090816200004a919062000483565b506040518060400160405280600681526020017f6e6f74206d6500000000000000000000000000000000000000000000000000008152506001908162000091919062000483565b506040518060400160405280601b81526020017f46756e6374696f6e2064623f206e6576657220686561726420656d000000000081525060029081620000d8919062000483565b506040518060400160405280601e81526020017f49742773206e6f7420746865207269676874207468696e67206e6f2e2e2e0000815250600390816200011f919062000483565b506040518060400160405280601081526020017f4841484148414841204e4f54204d4521000000000000000000000000000000008152506004908162000166919062000483565b506040518060400160405280601b81526020017f505553482050555348205055534821212120574841543f20343f20000000000081525060059081620001ad919062000483565b506040518060400160405280601881526020017f54686520342062797465732c206974277320666973687921000000000000000081525060069081620001f4919062000483565b503480156200020257600080fd5b506200056a565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200028b57607f821691505b602082108103620002a157620002a062000243565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026200030b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002cc565b620003178683620002cc565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620003646200035e62000358846200032f565b62000339565b6200032f565b9050919050565b6000819050919050565b620003808362000343565b620003986200038f826200036b565b848454620002d9565b825550505050565b600090565b620003af620003a0565b620003bc81848462000375565b505050565b5b81811015620003e457620003d8600082620003a5565b600181019050620003c2565b5050565b601f8211156200043357620003fd81620002a7565b6200040884620002bc565b8101602085101562000418578190505b620004306200042785620002bc565b830182620003c1565b50505b505050565b600082821c905092915050565b6000620004586000198460080262000438565b1980831691505092915050565b600062000473838362000445565b9150826002028217905092915050565b6200048e8262000209565b67ffffffffffffffff811115620004aa57620004a962000214565b5b620004b6825462000272565b620004c3828285620003e8565b600060209050601f831160018114620004fb5760008415620004e6578287015190505b620004f2858262000465565b86555062000562565b601f1984166200050b86620002a7565b60005b8281101562000535578489015182556001820191506020850194506020810190506200050e565b8683101562000555578489015162000551601f89168262000445565b8355505b6001600288020188555050505b505050505050565b6106f1806200057a6000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c8063937cc96711610071578063937cc96714610145578063b00d78a51461014f578063b8d6426714610159578063c3ea113614610177578063dcbc088514610195578063dd7f7b4b1461019f576100b4565b8063236ff8ab146100b9578063346dbadd146100d75780633d8a63e4146100f55780634adfcb1314610113578063795abd871461011d57806392dca47614610127575b600080fd5b6100c16101bd565b6040516100ce9190610639565b60405180910390f35b6100df61024b565b6040516100ec9190610639565b60405180910390f35b6100fd6102d9565b60405161010a9190610639565b60405180910390f35b61011b610367565b005b610125610369565b005b61012f61036b565b60405161013c9190610639565b60405180910390f35b61014d6103f9565b005b6101576103fb565b005b6101616103fd565b60405161016e9190610639565b60405180910390f35b61017f61048b565b60405161018c9190610639565b60405180910390f35b61019d610519565b005b6101a761051b565b6040516101b49190610639565b60405180910390f35b600480546101ca9061068a565b80601f01602080910402602001604051908101604052809291908181526020018280546101f69061068a565b80156102435780601f1061021857610100808354040283529160200191610243565b820191906000526020600020905b81548152906001019060200180831161022657829003601f168201915b505050505081565b600680546102589061068a565b80601f01602080910402602001604051908101604052809291908181526020018280546102849061068a565b80156102d15780601f106102a6576101008083540402835291602001916102d1565b820191906000526020600020905b8154815290600101906020018083116102b457829003601f168201915b505050505081565b600380546102e69061068a565b80601f01602080910402602001604051908101604052809291908181526020018280546103129061068a565b801561035f5780601f106103345761010080835404028352916020019161035f565b820191906000526020600020905b81548152906001019060200180831161034257829003601f168201915b505050505081565b565b565b600580546103789061068a565b80601f01602080910402602001604051908101604052809291908181526020018280546103a49061068a565b80156103f15780601f106103c6576101008083540402835291602001916103f1565b820191906000526020600020905b8154815290600101906020018083116103d457829003601f168201915b505050505081565b565b565b6000805461040a9061068a565b80601f01602080910402602001604051908101604052809291908181526020018280546104369061068a565b80156104835780601f1061045857610100808354040283529160200191610483565b820191906000526020600020905b81548152906001019060200180831161046657829003601f168201915b505050505081565b600180546104989061068a565b80601f01602080910402602001604051908101604052809291908181526020018280546104c49061068a565b80156105115780601f106104e657610100808354040283529160200191610511565b820191906000526020600020905b8154815290600101906020018083116104f457829003601f168201915b505050505081565b565b600280546105289061068a565b80601f01602080910402602001604051908101604052809291908181526020018280546105549061068a565b80156105a15780601f10610576576101008083540402835291602001916105a1565b820191906000526020600020905b81548152906001019060200180831161058457829003601f168201915b505050505081565b600081519050919050565b600082825260208201905092915050565b60005b838110156105e35780820151818401526020810190506105c8565b60008484015250505050565b6000601f19601f8301169050919050565b600061060b826105a9565b61061581856105b4565b93506106258185602086016105c5565b61062e816105ef565b840191505092915050565b600060208201905081810360008301526106538184610600565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806106a257607f821691505b6020821081036106b5576106b461065b565b5b5091905056fea2646970667358221220746841f27e5037f89c15c7d04d5c1102b534a6fc0a7d4297aaa71c7392bedfd164736f6c63430008120033

Okay, We have enough Information from 101.txt. And we are given a EVM bytecode of the contract in Invitation.txt

Our Goal :

  • Get the function name which starts with TCP1P

Observations :

  • We have only the bytecode of the challenge

What can we do? Can we decompile bytecode into readable solidity contract?

Yes, We can get OP codes and somewhat readable contract by using some decompiling tools. Here is the list of few EVM bytecode Decompilers

Dedaub is many folks favourite and free to use. So, we can decompile our bytecode using Dedaub online decompiler.

Before decompiling lets see how function names stores in bytecode

Function names won't be directly stores as it is in the bytecode. Only function selector of that particular function will be stored in bytecode.

What is function selector?

A function selector is a four-byte identifier determined through the Keccak256 hash of the function's signature. The function signature is derived from the function name and the parenthesized list of parameter types. For example a function is defined as below :

function sum(uint a, uint b) public returns(uint){
    //....
}

Function signature of sum() is sum(uint,uint).

Function selector of sum() is bytes4(keccak256(bytes("sum(uint,uint)")))

We can get the function selector using cast cli tool.

mj0ln1r@Linux:~/invitation$ cast sig "sum(uint,uint)"
0xcad0899b

As you can see only 4 bytes hex value is the function signature. This 4 bytes value only stored in the EVM bytecode.

Which OP code operates this function signature?

It is PUSH4 as PUSH4 OP code used to push 4 bytes at a time into the stack. For the above example, the assembly will be PUSH 0xcad0899b.

We have got enough knowledge know lets solve the challenge

Decompile the EVM bytecode and find the function selector

I am using Dedaub to decompile. Enter the Bytecode in online decompiler and continue to decompile the EVM bytecode and view the disassembled section for the OP Codes. Our aim is to find the function selector which is operated by PUSH4 OP Code, so lets find PUSH4 in the OP Codes.As you can see in below image we found few function selectors.

Can we get back the original function name from function selector?

Absolutely not, it is impossible to get the original function signature information back theatrically. But there is a way to find it. There is a database available which collects tons of function signatures and their hashes.

Search one by one function selectors that we found from the decompilation. 0xb00d78a5 is the one that we are looking.

Finally change the function name to flag format as specified in the 101.txt

Flag : TCP1P{4_Bytes_SigNAtuRe_aS_4n_Invitation_congratz}

VIP

Description :

A very simple system at a party. If you are a VIP, you can get everything.

Challenge : nc ctf.tcp1p.com 23345

Lets connect to challenge,

mj0ln1r@AHLinux:~/vip$ nc ctf.tcp1p.com 23345
Welcome to TCP1P Blockchain Challenge

1. How to 101?
2. get Contract
>> 1
Same as the last challenge, but this time, call the help() function first

mj0ln1r@AHLinux:~/vip$ nc ctf.tcp1p.com 23345
Welcome to TCP1P Blockchain Challenge

1. How to 101?
2. get Contract
>> 2
Contract Addess: 0xCf2C346b07CE9931962fC8B6d93fF094FbF447B9
RPC URL        : https://eth-sepolia.g.alchemy.com/v2/SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8
To start       : Simply call the help() function, everything is written there

Note: Due it's deployed on Sepolia network, please use your own Private key to do the transaction
      If you need funds, you can either DM the probset or get it on https://sepoliafaucet.com/

Firstly get some Sepolia ETH if you dont have like me. Thank you @kiinzu(author) for the Sepolia ETH :-).

Initially I did this challenge by writing a contract on remix and called challenge contract from there. Now I will explain a much more easy way using Foundry.

Setup Foundry

  1. forge init (to setup foundry project)
  2. Delete boiler plate codes under (/src, /test, /script)
  3. Create a new file Solve.s.sol in /script
  4. Import Script and console to write scripts and logging.

First of all as challenge 101 said, call the help() function. I used call method to call the help() function and decoded using abi.decode.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

// import "forge-std/Script.sol";
import {Script} from "forge-std/Script.sol";
import "forge-std/console.sol";

contract SolveVIP is Script {
    function getHelp() public{
        address target = 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637;
        (bool success, bytes memory _help) = target.call(abi.encodeWithSignature("help()"));
        require(success);
        console.logBytes(abi.decode(_help, (bytes)));
    }
    function run() public {
        getHelp();
    }
}

If we run the script and decoded the output we will get some message to move forward in challenge.

mj0ln1r@Linux:~/vip$ forge script script/Solve.s.sol --rpc-url https://eth-sepolia.g.alchemy.com/v2/SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8
[⠢] Compiling...
[⠒] Compiling 15 files with 0.8.20
[⠢] Solc 0.8.20 finished in 5.83s
Compiler run successful!
Script ran successfully.

== Logs ==
  0x57656c636f6d6520746f205443503150205072697661746520436c7562210a0a456e6a6f792074686520435446205061727479206f6620796f7572206c6966652068657265210a4275742066697273742e2e2e20506c656173652067697665206d6520796f75722069642c206e6f726d616c2070656f706c652068617665206174206c65617374206d656d62657220726f6c650a4f6620436f757273652c2074686572652061726520616c736f206d616e792056495073206f76657220686572652e20422d290a0a46756e6374696f6e733a0a0a456e7472616e636528726f6c6529202d3e2076657269667920796f757220726f6c6520686572652c2061726520796f752061206d656d626572206f722056495020436c6173730a2020203e20726f6c6520202d2d3e20696e70757420796f757220726f6c6520617320737472696e670a737465616c564950436f64652829202d3e20736f6d656f6e65206d69676874277665206a75737420737465616c20612076697020636f646520616e642077616e7420746f206769766520697420746f20796f750a676574466c616728292020202020202d3e204f6e636520796f752073686f7720796f757220726f6c652c20796f752063616e2074727920796f7572206c75636b21204f4e4c59205649502043616e206765742074686520466c6167210a0a0a

mj0ln1r@Linux:~/vip$ python3
>>> from Crypto.Util.number import long_to_bytes
>>> long_to_bytes(0x57656c636f6d6520746f205443503150205072697661746520436c7562210a0a456e6a6f792074686520435446205061727479206f6620796f7572206c6966652068657265210a4275742066697273742e2e2e20506c656173652067697665206d6520796f75722069642c206e6f726d616c2070656f706c652068617665206174206c65617374206d656d62657220726f6c650a4f6620436f757273652c2074686572652061726520616c736f206d616e792056495073206f76657220686572652e20422d290a0a46756e6374696f6e733a0a0a456e7472616e636528726f6c6529202d3e2076657269667920796f757220726f6c6520686572652c2061726520796f752061206d656d626572206f722056495020436c6173730a2020203e20726f6c6520202d2d3e20696e70757420796f757220726f6c6520617320737472696e670a737465616c564950436f64652829202d3e20736f6d656f6e65206d69676874277665206a75737420737465616c20612076697020636f646520616e642077616e7420746f206769766520697420746f20796f750a676574466c616728292020202020202d3e204f6e636520796f752073686f7720796f757220726f6c652c20796f752063616e2074727920796f7572206c75636b21204f4e4c59205649502043616e206765742074686520466c6167210a0a0a)

b"Welcome to TCP1P Private Club!

Enjoy the CTF Party of your life here!
But first... Please give me your id, normal people have at least member role
Of Course, there are also many VIPs over here. B-)
Functions:
Entrance(role) -> verify your role here, are you a member or VIP Class
   > role  --> input your role as string
   stealVIPCode() -> someone might've just steal a vip code and want to give it to you
   getFlag()      -> Once you show your role, you can try your luck! ONLY VIP Can get the Flag!"

Now we its clear that we need to call stealVIPCode() first then get the VIP Code and use it to call the Entrance(role) to solve the challenge. Finally calling the getFlag() will give us the flag.

Lets call stealVIPCode()

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

// import "forge-std/Script.sol";
import {Script} from "forge-std/Script.sol";
import "forge-std/console.sol";

contract SolveVIP is Script {
    function callStealVIPCode() public {
            address target = 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637;
            (bool success, bytes memory _vipCode) = target.call(abi.encodeWithSignature("stealVIPCode()"));
            require(success);
            console.logBytes(abi.decode(_vipCode,(bytes)));
    }
    function run() public {
        callStealVIPCode();
    }
}

Run Script and Decode the hex to get the VIPCode.

mj0ln1r@Linux:~/vip$ forge script script/Solve.s.sol --rpc-url https://eth-sepolia.g.alchemy.com/v2/SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8
[⠢] Compiling...
[⠒] Compiling 15 files with 0.10.20
[⠢] Solc 0.8.20 finished in 5.35s
Compiler run successful!
Script ran successfully.

== Logs ==
  0x2049206d6179206f72206d6179206e6f742067657420796f752061207469636b65742c20627574204920646f6e277420756e6465727374616e64206d7563682061626f757420686f7720746f206465636f646520746869732e0a4974277320736f6d6520736f7274206f6620746865697220616269436f64657220706f6c6963792e200a5649502d5469636b65743a203078303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303032303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030326635343433353033313530333137333734343336633631373337333533363536313734323032643230363937333230373436383635323035363439353032303534363936333662363537343230373436383635373932303733363136393634303030303030303030303030303030303030303030303030303030303030303030300a

mj0ln1r@Linux:~/vip$ python3
>>> from Crypto.Util.number import long_to_bytes
>>> long_to_bytes(0x2049206d6179206f72206d6179206e6f742067657420796f752061207469636b65742c20627574204920646f6e277420756e6465727374616e64206d7563682061626f757420686f7720746f206465636f646520746869732e0a4974277320736f6d6520736f7274206f6620746865697220616269436f64657220706f6c6963792e200a5649502d5469636b65743a203078303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303032303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030326635343433353033313530333137333734343336633631373337333533363536313734323032643230363937333230373436383635323035363439353032303534363936333662363537343230373436383635373932303733363136393634303030303030303030303030303030303030303030303030303030303030303030300a)

b" I may or may not get you a ticket, but I don't understand much about how to decode this.\nIt's some sort of their abiCoder policy. \nVIP-Ticket: 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002f5443503150317374436c61737353656174202d2069732074686520564950205469636b6574207468657920736169640000000000000000000000000000000000\n"

>>> long_to_bytes(0x5443503150317374436c61737353656174202d2069732074686520564950205469636b657420746865792073616964)
b'TCP1P1stClassSeat - is the VIP Ticket they said'

Now we have got VIPCode lets call Entrance(role) with role as TCP1P1stClassSeat and call getCall() to get flag hex and decode it for the flag.

The final script :

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script} from "forge-std/Script.sol";
import "forge-std/console.sol";

contract SolveVIP is Script {
    function getHelp() public{
        address target = 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637;
        (bool success, bytes memory _help) = target.call(abi.encodeWithSignature("help()"));
        require(success);
        // console.logBytes(abi.decode(_help, (bytes)));
    }
    function callStealVIPCode() public {
        address target = 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637;
        (bool success, bytes memory _vipCode) = target.call(abi.encodeWithSignature("stealVIPCode()"));
        require(success);
        // console.logBytes(abi.decode(_vipCode,(bytes)));
    }

    function callEntrance(string memory _role) public {
        address target = 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637;
        (bool success, ) = target.call(abi.encodeWithSignature("Entrance(string)", _role));
        require(success);
    }

    function callGetFlag() public {
        address target = 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637;
        (bool success, bytes memory _flag) = target.call(abi.encodeWithSignature("getFlag()"));
        require(success);
        console.logBytes(_flag);
    }
    function run() public {
        // getHelp();
        // callStealVIPCode();
        callEntrance("TCP1P1stClassSeat");
        callGetFlag();
    }
}

mj0ln1r@Linux:~/vip$ forge script script/Solve.s.sol --rpc-url https://eth-sepolia.g.alchemy.com/v2/SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8 --private-key <REDACTED> 
[⠢] Compiling...
[⠒] Compiling 15 files with 0.10.20
[⠢] Solc 0.8.20 finished in 10.35s
Compiler run successful!
Script ran successfully.

== Logs ==
  0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003854435031507b345f6231745f6f665f6630756e6472795f73336e645f346e645f616269436f6465725f77306e375f687572375f793334687d0000000000000000

mj0ln1r@Linux:~/vip$ python3
>>> from Crypto.Util.number import long_to_bytes
>>> long_to_bytes(0x54435031507b345f6231745f6f665f6630756e6472795f73336e645f346e645f616269436f6465725f77306e375f687572375f793334687d)
b'TCP1P{4_b1t_of_f0undry_s3nd_4nd_abiCoder_w0n7_hur7_y34h}'

Quick way to Solve

We can use cast cli tool to call the function from command line quickly.

mj0ln1r@Linux:~/vip$ cast 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637  "help()" --rpc-url https://eth-sepolia.g.alchemy.com/v2/SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8
mj0ln1r@Linux:~/vip$ cast 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637 "stealVIPCode()" --rpc-url https://eth-sepolia.g.alchemy.com/v2/SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8
mj0ln1r@Linux:~/vip$ cast send 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637 "getEntrance(string)" --rpc-url https://eth-sepolia.g.alchemy.com/v2/SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8 --private-key <REDACTED> -- "TCP1P1stClassSeat"
mj0ln1r@Linux:~/vip$ cast 0x52DF9c7cc8f8f5C8204F2401505A6248cE49d637 "getFlag()" --rpc-url https://eth-sepolia.g.alchemy.com/v2/SMfUKiFXRNaIsjRSccFuYCq8Q3QJgks8

And dont forget to decoding each output in the same way we did before to get the flag.


References

  1. Solidity Docs
  2. noxx.substack.com
  3. Storage Layout
  4. blog.trustlook.com
  5. Foundry Book

Thank you for reading!