Table of Contents
Survival of the Fittest Writeup is one of the standout smart contract challenges from Hack The Box (HTB), offering devs a sharp look into blockchain exploitation tactics. In the realm of blockchain development, understanding smart contract vulnerabilities isnโt optional โ itโs survival. This write-up breaks down the challenge, walks through the exploit, and serves as a practical guide for developers looking to sharpen their Web3 security skills.
โ ๏ธ Heads up, fam โ the smart contract code youโll see below is a reworked take on the original Survival of the Fittest Writeup from HTB. I tweaked variable names and function titles to keep things fresh for teaching purposes, but the exploit logic and vulnerability patterns stay legit.
Challenge Overview
The challenge involves two Solidity contracts: Setup.sol and Creature.sol.
Setup.sol
This contract initializes the challenge environment:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Beast} from "./Beast.sol";
contract Setup {
Beast public immutable TARGET;
constructor() payable {
require(msg.value == 1 ether, "1 ether required to deploy");
TARGET = new Beast{value: 10}();
}
function challengeCompleted() public view returns (bool) {
return address(TARGET).balance == 0;
}
}
Key points:(CoinsBench)
- Deploys the
Creature
contract with 10 wei. - Ensures the setup is only complete when 1 ether is sent.
- The
isSolved
function checks if theCreature
contract’s balance is depleted.
Creature.sol
This contract represents the target for exploitation:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Beast {
uint256 public health;
address public lastAttacker;
constructor() payable {
health = 20;
}
function heavyStrike(uint256 damage) external {
_takeDamage(damage);
}
function lightHit() external {
_takeDamage(1);
}
function claimReward() external {
require(health == 0, "Beast still stands!");
payable(msg.sender).transfer(address(this).balance);
}
function _takeDamage(uint256 damage) internal {
lastAttacker = msg.sender;
health -= damage;
}
}
Key functionalities:
lifePoints
initialized to 20.strongAttack
andpunch
functions reducelifePoints
.loot
function allows withdrawal of funds whenlifePoints
reach zero.
Exploitation Strategy
The goal is to decrease lifePoints to zero and activate the loot function to withdraw the contractโs balance.
Step 1: Inflict Damage
Utilize the strongAttack
function to reduce lifePoints
:
cast send $target -r $rpc 'function strongAttack(uint256)' 20 --private-key $pk
This command sends a transaction to the strongAttack
function with a damage value of 20, effectively reducing lifePoints
to zero.
Step 2: Loot the Contract
With lifePoints
at zero, invoke the loot
function:
cast send $target -r $rpc 'function loot()' --private-key $pk
Executing this command calls the loot function, which moves the contract’s balance to your address.
Absolutely โ letโs expand that section with more in-depth, developer-relevant insights while weaving in “Survival of the Fittest Writeup” naturally for SEO.
Technical Insights
๐ State Variable Manipulation
One of the core takeaways from the Survival of the Fittest Writeup is how crucial it is to understand and manipulate state variables in smart contracts. In this challenge, lifePoints
acts as the contractโs primary gatekeeper for fund withdrawals. By targeting and reducing this variable directly โ through the strongAttack
function โ attackers can bypass complex logic and trigger unintended outcomes.
This highlights a common issue in Solidity contracts where improperly guarded state variables can be the Achillesโ heel of an otherwise solid system. Always question: What happens if this value changes? Who can change it? And what would break if it did?
๐ Access Control Considerations
Another major flaw exposed in this Survival of the Fittest Writeup is the complete lack of access restrictions on critical functions like strongAttack
and punch
. Any external address can invoke these functions, allowing anyone to manipulate the contractโs internal state without restriction.
In production-grade smart contracts, this is a textbook mistake. Functions that affect key logic or balances should be protected by access modifiers such as onlyOwner
, onlyRole
, or by implementing custom logic to validate caller permissions. Even in simple contracts, never assume functions are safe just because you “expect” only certain users to call them.
๐ Tool Utilization and Foundry Alpha
This Survival of the Fittest Writeup also demonstrates how powerful developer tools like Foundry’s cast
command-line utility can be for both exploitation and auditing. cast send
allows devs to quickly interact with deployed contracts, call functions, and send transactions without spinning up a frontend or writing extra scripts.
For Web3 security researchers, cast
is essential for rapidly testing hypotheses, fuzzing contract interactions, and simulating attacks in local or testnet environments. Pairing Foundry with tools like slither
or mythril
for static analysis can give developers a serious edge in identifying and fixing vulnerabilities before they go live.
๐ง Lessons for Developers
- Implement Robust Access Controls: The Survival of the Fittest Writeup makes it clear โ always protect functions that modify critical state variables. Use access modifiers or custom logic to gatekeep sensitive operations.
- Validate State Changes: Never assume state transitions are safe. Incorporate sanity checks, require conditions, and test for unexpected edge cases. Defensive programming isn’t optional in Web3.
- Leverage the Right Tools: Familiarity with tools like Foundry,
cast
, and automated analysis tools gives you an edge in both development and auditing. The ability to quickly reproduce challenges like the Survival of the Fittest Writeup in your own test environment is a valuable skill for every smart contract engineer. - Think Like an Attacker: This challenge is a perfect example of how an attacker would view your contract โ looking for unprotected functions, unchecked state changes, and weak assumptions. Regularly reviewing your own code through this lens will harden your dApps and protocols.