Reproduced Exploit

Tapioca DAO — repay allowance checked on part, pulls elastic

1. approveBorrow documents a maximum spendable amount. 2. repay checks allowance against debt part. 3. _repay converts part→elastic and withdraws amount > part after interest.

Jul 2023Otheruntagged2 min read

Chain

Other

Category

untagged

Date

Jul 2023

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 27534-h-44-bigbangrepay-and-singularityrepay-spend-more-than-allow. Standalone Foundry PoC and full write-up: 27534-h-44-bigbangrepay-and-singularityrepay-spend-more-than-allow_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/wrong-condition · vuln/direct-drain · vuln/vote-delegation-loop

Reproduction: self-contained Foundry PoC with only forge-std — no fork, no RPC. Full trace: output.txt. PoC: test/27534-h-44-bigbangrepay-and-singularityrepay-spend-more-than-allow.sol.


Key info#

ImpactHIGH — spender approved for part can pull more asset than allowed once interest makes elastic > base
ProtocolTapioca DAO
Vulnerable codeBigBang._repay / SGLBorrow.repayallowedBorrow(from, part) then withdraw amount
Bug classUnit mismatch (part vs elastic) in allowance
FindingCode4rena — Tapioca, 2023-07 · #27534 · reporter zzzitron
Reportcode4rena.com/reports/2023-07-tapioca
SourceAuditVault
StatusAudit finding with Hardhat PoC in report
Compiler^0.8.24 (PoC)

TL;DR#

  1. approveBorrow documents a maximum spendable amount.
  2. repay checks allowance against debt part.
  3. _repay converts part→elastic and withdraws amount > part after interest.

The vulnerable code#

SOLIDITY
function repay(..., uint256 part) public allowedBorrow(from, part) {
    return _repay(from, to, part);
}
function _repay(...) internal returns (uint256 amount) {
    (totalBorrow, amount) = totalBorrow.sub(part, true);
    // @> VULN: pulls elastic amount, not part
    yieldBox.withdraw(assetId, from, address(this), amount, 0);
}

Fix: check allowance against the elastic amount actually pulled.

Root cause#

Allowance units are treated as asset amounts, but the check uses debt-share parts while the pull uses elastic.

Attack walkthrough#

  1. Victim approves spender for 1e18 part; victim funds YieldBox.
  2. Market elastic > base after accrual.
  3. Spender repays 1e18 part → pulls >1e18 asset from victim.

Diagrams#

flowchart LR A["allowance = 1e18 part"] --> B["repay part=1e18"] B --> C["amount = part * elastic / base"] C --> D["amount > 1e18 when elastic > base"] D --> E["yieldBox.withdraw amount from granter"]

Impact#

Approved repayers can overspend the granter's YieldBox asset balance beyond the stated allowance; gap grows with interest.

Taxonomy#

  • genome: wrong-condition, direct-drain, vote-delegation-loop
  • sector: governance, lending, staking, token
  • severity: high
  • platform: code4rena

Sources#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.