All incidents

BSC PoolWithdraw Signature-Bypass Drains USDT Pool

Share
Feb 27, 2026 01:09 UTCAttackLoss: 179,747.63 USDTManually checked3 exploit txWindow: 6h 58m
Estimated Impact
179,747.63 USDT
Label
Attack
Exploit Tx
3
Addresses
2
Attack Window
6h 58m
Feb 27, 2026 01:09 UTC → Feb 27, 2026 08:08 UTC

Exploit Transactions

TX 1BSC
0x3d936c59c9d446ee222361acc820be47054aea45f9f5fc92482fe973a596e475
Feb 27, 2026 01:09 UTCExplorer
TX 2BSC
0x0d9e4478567aa33dad3bd7c9a79de5f2afc9c7037c026795aa838f5ab834ca80
Feb 27, 2026 04:47 UTCExplorer
TX 3BSC
0x91f4526052060d7137919a8e2bb3ce6c2169e5a376ab002c4745f69841cfd784
Feb 27, 2026 08:08 UTCExplorer

Victim Addresses

0x20dbf42970eb3fabe62615534c4ef15fd4d59596BSC
0x79f9b0b81e08efc3690bb78611620cb1b708fccbBSC

Loss Breakdown

179,747.63USDT

Similar Incidents

Root Cause Analysis

BSC PoolWithdraw Signature-Bypass Drains USDT Pool

1. Incident Overview TL;DR

An unprivileged adversary repeatedly drained USDT from pool contract 0x20dbf42970eb3fabe62615534c4ef15fd4d59596 on BSC by calling poolWithdraw(address,uint256,address,uint256,address[],uint256,bytes[]) (0xc7e36d91) with allSigners=[] and signatures=[].

The root cause is a function-level authorization gap: poolWithdraw allows value transfer and internal pool-balance decrement without enforcing the configured signer quorum (requiredSignatures=3) when signer arrays are empty.

The attack is ACT (is_act=true) because it is permissionless and reproducible from unrelated EOAs using public on-chain data only.

2. Key Background

  • Victim proxy: 0x20dbf42970eb3fabe62615534c4ef15fd4d59596.
  • Delegate implementation observed in traces: 0x79f9b0b81e08efc3690bb78611620cb1b708fccb.
  • Target token: USDT 0x55d398326f99059ff775485246999027b3197955.
  • Runtime selector mapping identifies:
    • 0xc7e36d91 -> poolWithdraw(address,uint256,address,uint256,address[],uint256,bytes[])
    • 0x43251e03 -> poolFeeWithdraw(address,uint256,address,uint256,uint256,address[],bytes[])
  • Sibling function behavior differs under the same context: poolFeeWithdraw rejects empty signer arrays (invalid allSigners length), while poolWithdraw succeeds.

Evidence snapshot (BSC pre-state simulation at block 0x4fbb1cd):

{
  "requiredSignatures": "3",
  "attacker_whitelist": "false",
  "attacker_isAllowedSigner": "false",
  "poolWithdraw_empty_signers": { "status": "success" },
  "poolFeeWithdraw_empty_signers": {
    "status": "revert",
    "error": "execution reverted: invalid allSigners length"
  }
}

3. Vulnerability Analysis & Root Cause Summary

This incident is an ATTACK-class authorization failure. The intended invariant is: any pool asset withdrawal must require valid authorization (authorized caller and/or signer quorum). In the observed implementation path, poolWithdraw can be executed with empty allSigners and signatures arrays, even when requiredSignatures=3. The call then proceeds to transfer USDT out of the victim and decrement pool accounting. Trace evidence confirms execution enters the proxy and delegatecalls into implementation 0x79f9... with selector 0xc7e36d91, then performs ERC20 transfer to attacker-controlled receivers. The same pre-state confirms attacker addresses are not whitelisted and not allowed signers. Because this path is reachable by arbitrary EOAs/contracts without privileged keys, the exploit is permissionless and repeatable.

4. Detailed Root Cause Analysis

  1. Pre-state at block 83603917 (0x4fbb1cd) is publicly reconstructible and shows signer policy active (requiredSignatures=3) with positive victim USDT pool balance.
  2. The adversary submits a normal L1 transaction calling poolWithdraw on victim proxy 0x20db... with:
    • token = USDT,
    • receiver = attacker-controlled address,
    • non-expired expireTime,
    • allSigners=[], signatures=[].
  3. Trace confirms victim proxy forwards by DELEGATECALL to implementation 0x79f9..., still with selector 0xc7e36d91.
  4. Execution succeeds (no revert), emits PoolWithdraw, transfers USDT from victim, and decreases internal pool balance.
  5. Independent simulation from unrelated address 0x1111111111111111111111111111111111111111 reproduces the same success with empty arrays; under identical conditions poolFeeWithdraw reverts with invalid allSigners length.

Representative execution snippet (exploit tx 0x0d9e... call trace):

{
  "from": "0xd25f43449231218c8be3871073938fcc2202ab56",
  "to": "0x20dbf42970eb3fabe62615534c4ef15fd4d59596",
  "type": "CALL",
  "input_prefix": "0xc7e36d91",
  "nested_call": {
    "to": "0x79f9b0b81e08efc3690bb78611620cb1b708fccb",
    "type": "DELEGATECALL",
    "input_prefix": "0xc7e36d91"
  }
}

Invariant break and breakpoint:

  • Invariant: decreasing poolBalances[token] and transferring pool assets must require valid signer quorum/authorization.
  • Breakpoint: poolWithdraw accepts empty signer/signature arrays and still performs state-changing withdrawal transfer.

5. Adversary Flow Analysis

Stage A - Unauthorized pool withdrawal

  • 0x0d9e4478567aa33dad3bd7c9a79de5f2afc9c7037c026795aa838f5ab834ca80 (block 83603918): withdraws 59,974.609375 USDT to sender 0xd25f....
  • 0x3d936c59c9d446ee222361acc820be47054aea45f9f5fc92482fe973a596e475 (block 83574859): withdraws 59,781.25 USDT to sender 0x8c98....
  • 0x91f4526052060d7137919a8e2bb3ce6c2169e5a376ab002c4745f69841cfd784 (block 83630514): helper 0xebd... withdraws 59,991.77 USDT and forwards to 0xc6e....

Stage B - Profit realization

  • In 0x0d9e... and 0x3d93..., attacker swaps stolen USDT through pair 0x16b9... and unwraps WBNB to native BNB.
  • For 0x0d9e..., measured native delta for 0xd25f... is +94047881930522033994 wei net of gas.

Stage C - Repetition

  • The same bypass pattern is executed independently by different attacker-controlled accounts/contracts, confirming repeatable permissionless exploitability.

6. Impact & Losses

  • Total observed drain: 179747.629375 USDT.
  • Impact type: unauthorized depletion of victim pool accounting and transfer of pool-held USDT to attacker-controlled addresses.
  • Affected component: victim pool proxy/implementation withdrawal path (poolWithdraw).

7. References

  • Root cause source: root_cause.json.
  • Key exploit transactions:
    • 0x0d9e4478567aa33dad3bd7c9a79de5f2afc9c7037c026795aa838f5ab834ca80
    • 0x3d936c59c9d446ee222361acc820be47054aea45f9f5fc92482fe973a596e475
    • 0x91f4526052060d7137919a8e2bb3ce6c2169e5a376ab002c4745f69841cfd784
  • Evidence artifacts (human-labeled):
    • BSC pre-state and simulation checks (requiredSignatures, whitelist/signer status, empty-array simulation outcomes).
    • Full call traces for the three exploit transactions.
    • Transaction receipts with PoolWithdraw and transfer logs.
    • State/balance-diff snapshots showing victim depletion and attacker gain.