Hack The Box: 'Blockout ' A Blockchain Writeup - Global Cyber Skills Benchmark CTF 2025
Cracking "Blockout": A Tale of Gaslighting Gateways in a Blockchain CTF
Capture The Flag (CTF) competitions are a fantastic way to test and hone cybersecurity skills. Recently, in the HTB's Global Cyber Skills Benchmark 2025, the "Blockout" challenge stood out as one of the trickier blockchain puzzles. Many participants found themselves scratching their heads, with 12 pwns in 3.5 days. With a bit of perseverance (and a few strategically placed cast commands), we managed to be the first ones to plunge Volnaya's VCNKv2 power grid into darkness 12 hours in. This write-up details the journey, the vulnerabilities, and the exploit that led to capturing the flag: HTB{REDACTED}.

Prerequisites: Setting up Your Toolkit
Before diving into the challenge, you'll need a couple of essential tools:
Foundry: A blazing fast, portable, and modular toolkit for Ethereum application development. We'll primarily use
castfrom the Foundry suite to interact with the smart contracts.nc(netcat): A versatile networking utility used here to communicate with the challenge server for obtaining details and submitting the flag.
Installing Foundry (on Linux)
If you don't have Foundry installed, you can get it by running:
curl -L https://foundry.paradigm.xyz | bash
# The pipe (|) bash will take your curl output
# and send it as an input for the next command
Then, in a new terminal session, run foundryup to install the latest version:
foundryup
This will make cast, forge, and other Foundry tools available in your path.
Initial Reconnaissance: Connecting and Gathering Intel
The first step in any CTF is to understand the playground. The "Blockout" challenge provides a netcat interface that dispenses crucial information.
Connect to the server (e.g., 94.237.123.80:32139 in our case) and select option 1:
echo 1 | nc 94.237.123.80 32139
Your designated RPC an Player private key are:
RPC: http://94.237.123.80:42403
PrivateKey: 0xdedd2aa848b2344cc4f6d8c46dbfdb3a92146863574cadc0ea913422e23c5738
Your Player address is: 0xEdC1bBEa176cd432945180425795154b0Bba982F
The target contract VCNKv2 is deployed at: 0x64934dd027bDEA963fA623C28C476848CC55e468
The Setup contract is deployed at: 0x37F67BBEe5F98f3bf60D759D7185b6a87efA2f05
Make a note of these essential details:
Player Private Key:
0xdedd2aa848b2344cc4f6d8c46dbfdb3a92146863574cadc0ea913422e23c5738(This is your identity on the blockchain)RPC URL:
http://94.237.123.80:42403(Your connection point to the Ethereum node)Target Contract (VCNKv2):
0x64934dd027bDEA963fA623C28C476848CC55e468(The main contract we need to exploit)Setup Contract:
0x37F67BBEe5F98f3bf60D759D7185b6a87efA2f05(Used to check if the challenge is solved)
For the rest of this write-up, we'll use these values. Remember to replace them if you get different ones.
First Look: Understanding the Grid and the Goal
The challenge dropped us into a Foundry-based project. Our mission, as laid out in the Setup.sol contract, was clear: we needed to set the controlUnit.status within the VCNKv2 target contract to CU_STATUS_EMERGENCY (which has a value of 3). In simpler terms, trip the main breaker.
// Excerpt from Setup.sol
function isSolved() public view returns (bool) {
uint8 CU_STATUS_EMERGENCY = 3; // Our target status
(uint8 status, , , , ) = TARGET.controlUnit(); // TARGET is the VCNKv2 contract
return status == CU_STATUS_EMERGENCY;
}
The VCNKv2.sol contract represented the power grid's control system. It had a controlUnit struct managing things like current capacity, the number of active power gateways, and their health. Emergency mode, we discovered, could be triggered in two ways, defined by the failSafeMonitor modifier:
If
controlUnit.currentCapacitydropped belowFAILSAFE_THRESHOLD(10 ether).If
controlUnit.healthyGatewaysPercentagefell below 50%.
// Excerpt from VCNKv2.sol - The critical modifier
modifier failSafeMonitor() {
if (controlUnit.currentCapacity <= FAILSAFE_THRESHOLD) {
controlUnit.status = CU_STATUS_EMERGENCY;
emit ControlUnitEmergencyModeActivated();
}
else if (controlUnit.healthyGatewaysPercentage < 50) { // This became our focus
controlUnit.status = CU_STATUS_EMERGENCY;
emit ControlUnitEmergencyModeActivated();
}
else {
_; // Continue normal operation
}
}