All incidents

ABCCApp Reward Clock Drain

Share
Aug 23, 2025 14:32 UTCAttackLoss: 2,656,101.16 DDDDPending manual check1 exploit txWindow: Atomic
Estimated Impact
2,656,101.16 DDDD
Label
Attack
Exploit Tx
1
Addresses
1
Attack Window
Atomic
Aug 23, 2025 14:32 UTC → Aug 23, 2025 14:32 UTC

Exploit Transactions

TX 1BSC
0xee4eae6f70a6894c09fda645fb24ab841e9847a788b1b2e8cb9cc50c1866fb12
Aug 23, 2025 14:32 UTCExplorer

Victim Addresses

0x1bc016c00f8d603c41a582d5da745905b9d034e5BSC

Loss Breakdown

2,656,101.16DDDD

Similar Incidents

Root Cause Analysis

ABCCApp Reward Clock Drain

1. Incident Overview TL;DR

ABCCApp on BSC was exploited in transaction 0xee4eae6f70a6894c09fda645fb24ab841e9847a788b1b2e8cb9cc50c1866fb12 at block 58615055. An unprivileged attacker EOA 0x53feee33527819bb793b72bd67dbf0f8466f7d2c invoked helper contract 0x90e076ef0fed49a0b63938987f2cad6b4cd97a24, borrowed 12,500 USDT from Moolah, deposited into ABCCApp, advanced ABCCApp’s global reward clock by 1,000,000,000 days via a public function, claimed DDDD immediately, sold the DDDD back into USDT, repaid the flash loan, and kept 10062258375072914282796 USDT before gas.

The root cause is an access-control failure in ABCCApp’s reward-time accounting. addFixedDay(uint) is publicly callable and mutates the global fixedDay offset used by getCanClaimUSDT() and therefore by claimDDDD(). Because reward maturity depends on this mutable global offset, an attacker can force a fresh deposit to appear fully matured in the same transaction and drain ABCCApp’s DDDD inventory.

2. Key Background

ABCCApp accepts USDT deposits and internally converts the deposited USDT into DDDD through Pancake V3. For each user, it records reward-accounting fields including remainingUSDT, dailyUSDT, claimedUSDT, and lastClaimTime. The intended economic design is that rewards mature gradually over time and can later be claimed as DDDD.

The critical time-accounting path uses a contract-global variable fixedDay. The verified ABCCApp source shows:

function getCanClaimUSDT(address target) public view returns(uint totalUSDT, uint staticUSDT, uint dynamicUSDT) {
    User memory user = users[target];
    if(user.remainingUSDT == 0) {
        return (user.dynamicUSDT, 0, user.dynamicUSDT);
    }
    uint diffDay = (block.timestamp + getFixedDay() - user.lastClaimTime) / DAY;
    staticUSDT = diffDay * user.dailyUSDT;
    if(staticUSDT > user.remainingUSDT) {
        staticUSDT = user.remainingUSDT;
    }
    dynamicUSDT = user.dynamicUSDT;
    totalUSDT = staticUSDT + dynamicUSDT;
}

The same verified source also exposes the write primitive:

function getFixedDay() public view returns(uint) {
    return fixedDay * DAY;
}

function addFixedDay(uint target) public {
    if(target == 0) {
        fixedDay = 0;
    } else {
        fixedDay += target;
    }
}

Because addFixedDay(uint) is public and does not enforce owner or operator authorization, any caller can distort the elapsed-time input that reward claiming trusts.

3. Vulnerability Analysis & Root Cause Summary

This is an ATTACK-category ACT exploit against ABCCApp, not a benign MEV opportunity. The violated invariant is that a user’s claimable reward should increase only with real elapsed time after deposit, not with arbitrary attacker-controlled writes to shared state. ABCCApp breaks that invariant because getCanClaimUSDT() uses block.timestamp + getFixedDay() as elapsed time while addFixedDay(uint) lets any caller change fixedDay.

The code-level breakpoint is the pair of functions addFixedDay(uint) and getCanClaimUSDT(address). A fresh user position is initialized during deposit() with lastClaimTime = block.timestamp + getFixedDay(), remainingUSDT = 2 * payUSDT, and a nonzero dailyUSDT. If the same attacker immediately calls addFixedDay(1000000000), then getCanClaimUSDT() computes an enormous diffDay, saturates staticUSDT to the user’s full remainingUSDT, and hands claimDDDD() a fully matured claim in the same transaction. claimDDDD() then converts that claimable USDT value into DDDD and transfers DDDD out of ABCCApp.

The exploit is ACT because every required step is public and permissionless: Moolah flash liquidity, ABCCApp deposit, ABCCApp addFixedDay, ABCCApp claimDDDD, and the Pancake V3 exit route. No privileged key, whitelist, attacker-owned legacy contract, or private artifact is required.

4. Detailed Root Cause Analysis

The exploit starts from BSC pre-state immediately before block 58615055, where ABCCApp (0x1bc016c00f8d603c41a582d5da745905b9d034e5) already held a large DDDD treasury balance and Moolah (0x8f73b65b4caaf64fba2af91cc5d4a2a1318e5d8c) had enough USDT liquidity to fund a flash loan. The seed artifacts show ABCCApp held 2983610630433574236403014 DDDD before the transaction.

The attacker path is fully visible in the collected trace. The helper contract first borrows 12,500 USDT from Moolah, then deposits it into ABCCApp:

FlashLoan(... param2: 12500000000000000000000)
0x1bC016C00F8d603c41A582d5Da745905B9D034e5::deposit(125, 0x0000000000000000000000000000000000000000)
emit OnDeposit(..., 12500000000000000000000)

That deposit creates a user position for the helper contract with remainingUSDT = 25000000000000000000000 and dailyUSDT = 150000000000000000000, matching the reward scheme described in the root cause JSON. At this point the claim should not yet be matured, because no real time has elapsed.

The exploit breakpoint happens immediately after deposit, when the same helper contract calls ABCCApp function selector 0x3b1126c9 with value 1000000000, which corresponds to addFixedDay(1000000000):

0x1bC016C00F8d603c41A582d5Da745905B9D034e5::3b1126c9(000000000000000000000000000000000000000000000000000000003b9aca00)
storage @ slot 5: 0 -> 0x...3b9aca00

Once fixedDay is inflated, the same transaction can call claimDDDD() and bypass the intended waiting period. The trace then shows ABCCApp transferring DDDD directly to the attacker helper:

0x422cBee1289AAE4422eDD8fF56F6578701Bb2878::transfer(
  0x90e076eF0fEd49A0b63938987F2caD6B4Cd97a24,
  5758647707877403562561426
)

This is the direct treasury-drain step. The attacker helper then approves Pancake’s router, sells the claimed DDDD back into USDT, repays the 12,500 USDT flash loan, and transfers the residual USDT to the attacker EOA. The trace records:

USDT::transfer(0x90e076eF0fEd49A0b63938987F2caD6B4Cd97a24, 22562258375072914282796)
USDT::transferFrom(0x90e076eF0fEd49A0b63938987F2caD6B4Cd97a24, Moolah, 12500000000000000000000)
USDT::transfer(0x53FEEe33527819bB793b72bd67dbf0f8466f7d2c, 10062258375072914282796)

The balance diff artifact confirms the economic result. The attacker EOA ends with 10062258375072914282796 USDT, while ABCCApp’s DDDD balance falls from 2983610630433574236403014 to 24422744020404105183388, a net depletion of 2959187886413170131219626 DDDD in contract balance and 2656101164945938364769025 DDDD in realized attacker-cluster gain measured in the report’s loss section.

5. Adversary Flow Analysis

The adversary cluster consists of the profit-receiving EOA 0x53feee33527819bb793b72bd67dbf0f8466f7d2c and helper contract 0x90e076ef0fed49a0b63938987f2cad6b4cd97a24. Their roles are separable and evidenced directly by the transaction metadata and final transfers.

Stage 1 is permissionless funding. In the seed transaction, Moolah transfers 12500000000000000000000 USDT to the helper and invokes its flash-loan callback. No privilege is required beyond calling the public flash-loan interface.

Stage 2 is deposit plus forced reward maturation. The helper deposits 12,500 USDT into ABCCApp and then immediately calls addFixedDay(1000000000). This is the decisive adversary action because it rewrites a global reward-time variable that all later entitlement logic trusts.

Stage 3 is immediate claim and monetization. The helper calls claimDDDD(), receives 5758647707877403562561426 DDDD from ABCCApp, swaps the DDDD through Pancake V3 pools into 22562258375072914282796 USDT, repays the flash loan principal, and forwards 10062258375072914282796 USDT to the originating EOA. Gas cost recorded in the balance diff is 77554260000000 wei.

This sequence is atomic and self-financing. If any step failed, the flash loan would not be repaid and the whole transaction would revert. The successful inclusion of the transaction therefore proves the exploit path is complete end to end.

6. Impact & Losses

ABCCApp lost control of its DDDD reward inventory because unearned rewards could be claimed immediately after deposit. The root cause JSON records the measurable loss as:

{
  "token_symbol": "DDDD",
  "amount": "2656101164945938364769025",
  "decimal": 18
}

In economic terms, the attacker converted the drained DDDD into 10062258375072914282796 USDT before gas. The victim protocol was ABCCApp, and the consequence was not limited to one user position: the shared fixedDay variable is global, so the broken invariant affects reward-maturity accounting across the contract.

7. References

  1. Seed transaction metadata for 0xee4eae6f70a6894c09fda645fb24ab841e9847a788b1b2e8cb9cc50c1866fb12, confirming block, sender, callee, and calldata.
  2. Seed opcode trace for the same transaction, showing flash-loan funding, deposit(125, 0x0), addFixedDay(1000000000), claimDDDD(), DDDD sale, loan repayment, and profit transfer.
  3. Seed balance diff artifact, confirming attacker USDT profit and protocol-side DDDD depletion.
  4. Verified ABCCApp source on BscScan for 0x1bc016c00f8d603c41a582d5da745905b9d034e5, especially getCanClaimUSDT, deposit, claimDDDD, getFixedDay, and addFixedDay.