Table of Contents
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
forge init
(to setup foundry project)- Delete boiler plate codes under (/src, /test, /script)
- Create a new file
Solve.s.sol
in/script
- 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
Thank you for reading!