All incidents

TecraCoin TcrToken burnFrom Allowance Bug Exploits

Share
Feb 04, 2022 10:54 UTCAttackLoss: 580,170.69 TCR, 639,222.25 USDTManually checked1 exploit txWindow: Atomic
Estimated Impact
580,170.69 TCR, 639,222.25 USDT
Label
Attack
Exploit Tx
1
Addresses
2
Attack Window
Atomic
Feb 04, 2022 10:54 UTC → Feb 04, 2022 10:54 UTC

Exploit Transactions

TX 1Ethereum
0x81e9918e248d14d78ff7b697355fd9f456c6d7881486ed14fdfb69db16631154
Feb 04, 2022 10:54 UTCExplorer

Victim Addresses

0x420725a69e79eeffb000f98ccd78a52369b6c5d4Ethereum
0xe38b72d6595fd3885d1d2f770aa23e94757f91a1Ethereum

Loss Breakdown

580,170.69TCR
639,222.25USDT

Similar Incidents

Root Cause Analysis

TecraCoin TcrToken burnFrom Allowance Bug Exploits

1. Incident Overview TL;DR

On Ethereum mainnet block 14139082, an unprivileged attacker used a single contract-creation transaction to exploit a bug in TecraCoin's TcrToken contract. The exploit destroyed the TCR balance of a TCR/USDT liquidity pool and swapped that exposure into USDT via Uniswap, delivering 639,222.253258 USDT to the attacker's externally owned account (EOA).

The root cause is a mis-indexed allowance check in TcrToken.burnFrom. Instead of checking the victim's allowance to the spender, the implementation checks and updates _allowances[msg.sender][from]. This allows any caller that self-approves a victim address to burn tokens from that victim without the victim's consent. The attacker packaged this bug into a constructor that approves and then calls the vulnerable burn path against the TCR/USDT pool, followed by swaps to drain USDT.

2. Key Background

TecraCoin's TcrToken contract at 0xe38b72d6595fd3885d1d2f770aa23e94757f91a1 is an ERC20-style token with standard _balances and _allowances mappings, owner and pauser roles, blacklist functionality, and burn/burnFrom helpers implemented in Contract.sol. The token uses 8 decimals, a fixed maxSupply, and exposes the typical transfer, transferFrom, and approve interfaces, plus additional bulk transfer and upgrade hooks.

The principal victim of this incident is a TCR/USDT liquidity pool at 0x420725a69e79eeffb000f98ccd78a52369b6c5d4. This pool holds reserves of TCR and USDT and is serviced by the canonical UniswapV2 router at 0x7a250d5630b4cf539739df2c5dacb4c659f2488d. The exploit contract uses the router to route swaps through this pool, converting the destroyed TCR exposure into USDT and sending the proceeds to the attacker EOA.

In a correct ERC20 design, a burnFrom(from, amount) helper uses the allowance mapping as _allowances[from][msg.sender]. The spender (caller) can only burn tokens from from that from has explicitly allowed. This enforces the invariant that destructive operations on a holder's balance are gated by that holder's approvals.

3. Vulnerability Analysis & Root Cause Summary

The vulnerability is an authorization bug in TcrToken.burnFrom(address from, uint256 amount). The function checks allowance by reading _allowances[msg.sender][from] and then calls _approve(msg.sender, from, ...) before burning from's balance. This reverses the indices in the allowance mapping.

As a result, any caller can:

  1. Call approve(victim, amount) from their own address, which writes to _allowances[msg.sender][victim].
  2. Immediately call burnFrom(victim, amount) (directly or via a contract).

Because the function consults _allowances[msg.sender][victim], the call passes the allowance check and burns the victim's tokens even though the victim never consented. The exploit contract deploys with constructor logic that performs large self-approvals and then calls the vulnerable burn path against the TCR/USDT pool address, followed by swaps through UniswapV2Router02 to pull USDT out of the same pool.

This is an ACT (anyone-can-take) opportunity: any unprivileged EOA that can pay gas and deploy an equivalent exploit contract can reproduce the same sequence against the same vulnerable TcrToken deployment and a TCR-funded pool.

4. Detailed Root Cause Analysis

4.1 Vulnerable token implementation

The TecraCoin TcrToken contract implements burn and burnFrom as follows (excerpt from the verified source):

function burn(uint256 amount) external {
    require(_balances[msg.sender] >= amount, ERROR_BTL);
    _burn(msg.sender, amount);
}

function burnFrom(address from, uint256 amount) external {
    require(_allowances[msg.sender][from] >= amount, ERROR_ATL);
    require(_balances[from] >= amount, ERROR_BTL);
    _approve(msg.sender, from, _allowances[msg.sender][from] - amount);
    _burn(from, amount);
}

The allowance mapping is declared as:

mapping(address => mapping(address => uint256)) private _allowances;

Everywhere else, allowance-related operations treat the mapping as _allowances[owner][spender]. However, in burnFrom, the check and subsequent _approve call both use _allowances[msg.sender][from], effectively treating msg.sender as the owner and from as the spender. This breaks the standard invariant:

For any holder H and spender S, a destructive burn of H's balance via burnFrom(H, amount) must only succeed if _allowances[H][S] >= amount and H has explicitly granted that allowance to S.

With the indices reversed, an attacker can:

  • Call approve(victim, amount) from their EOA, which sets _allowances[attacker][victim] = amount.
  • Use an attacker-controlled contract as msg.sender to call burnFrom(victim, amount), relying on the mis-indexed lookup to see a large allowance and then burn the victim’s balance.

4.2 Pre-state and pool balances

The ACT pre-state σ_B is Ethereum mainnet state just before inclusion of the exploit transaction 0x81e9918e248d14d78ff7b697355fd9f456c6d7881486ed14fdfb69db16631154 in block 14139082. The collected prestate balance diff shows the TCR/USDT pool’s TCR balance:

{
  "token": "0xe38b72d6595fd3885d1d2f770aa23e94757f91a1",
  "holder": "0x420725a69e79eeffb000f98ccd78a52369b6c5d4",
  "before": "58027283904946",
  "after": "10214462474",
  "delta": "-58017069442472",
  "contract_name": "TcrToken"
}

This shows that, over the course of the exploit transaction, the pool loses exactly 58,017,069,442,472 raw TCR units, matching the amount burned in the constructor path described below.

4.3 Exploit constructor and burn sequence

The seed transaction is a type‑2 contract-creation transaction from EOA 0xb19b7f59c08ea447f82b587c058ecbf5fde9c299 with hash:

  • 0x81e9918e248d14d78ff7b697355fd9f456c6d7881486ed14fdfb69db16631154

The receipt confirms deployment of contract 0x6653d9bcbc28fc5a2f5fb5650af8f2b2e1695a15 and significant interactions with WETH9, USDT, TcrToken, the TCR/USDT pool, and UniswapV2Router02. The trace (trace.cast.log) and decoded calldata show the constructor performing the following high-level steps:

  1. Approve UniswapV2Router02 (0x7a250d5630b4cf539739df2c5dacb4c659f2488d) to spend unlimited USDT and TCR on behalf of the exploit contract.
  2. Approve the TCR/USDT pool address 0x420725a69e79eeffb000f98ccd78a52369b6c5d4 for a very large TCR allowance using TcrToken’s approve.
  3. Call the vulnerable burnFrom-style function on TcrToken with from = 0x420725a69e79eeffb000f98ccd78a52369b6c5d4 and amount = 58017069442472 TCR units, relying on the mis-indexed _allowances[msg.sender][from] mapping to satisfy the allowance check based on the earlier self-approval.
  4. Route swaps through UniswapV2Router02 and the TCR/USDT pool so that the destroyed TCR exposure results in a large USDT transfer from the pool to the exploit contract, and then onward to the attacker EOA.

USDT Transfer logs in the exploit receipt show:

{
  "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
  "data": "0x0000000000000000000000000000000000000000000000000000000006ba2816",
  "topics": [
    "Transfer(...)",
    "0x0000000000000000000000000d4a11d5eeaac28ec3f61d100daf4d40471f1852",
    "0x000000000000000000000000420725a69e79eeffb000f98ccd78a52369b6c5d4"
  ]
}

and a subsequent Transfer from the exploit contract to the attacker EOA, confirming that 639222253258 raw USDT units (639,222.253258 USDT with 6 decimals) are ultimately delivered to 0xb19b7f59c08ea447f82b587c058ecbf5fde9c299.

4.4 Profit calculation and ACT success predicate

The native balance diff for the exploit transaction shows:

  • The attacker EOA loses 101095144261776538 wei.
  • WETH9 gains 40000000000000000 wei.
  • The miner’s fee-recipient address gains 1230886000000000 wei.

From the receipt, gasUsed = 0x12c826 and effectiveGasPrice = 0xb8e7b741f. Multiplying these yields an exact gas fee of 61095144261776538 wei (0.061095144261776538 ETH). The remaining ETH delta corresponds to the 0.04 ETH value sent in the contract-creation transaction.

The USDT inflow is completely determined by the USDT Transfer logs: the attacker EOA receives exactly 639222253258 USDT units (639,222.253258 USDT). Valuing ETH at approximately 3,000 USDT around the 2022‑02‑04 block timestamp, the gas fee corresponds to about 183.29 USDT. Treating the attacker’s pre‑tx USDT balance as 0 in the reference portfolio, the exploit yields:

  • value_before ≈ 0 USDT,
  • value_after ≈ 639,222.253258 USDT,
  • value_delta ≈ +639,038.97 USDT net of gas.

This fulfills the ACT profit predicate: the adversary’s portfolio value in the USDT reference asset strictly increases after fees.

4.5 Conditions for exploitation

For this ACT opportunity to exist, the following conditions must hold:

  • The deployed TcrToken contract must contain the mis-indexed burnFrom allowance check (_allowances[msg.sender][from]).
  • A TCR holder such as the TCR/USDT pool must hold a substantial TCR balance and not be blacklisted or otherwise protected from burnFrom.
  • An unprivileged EOA must be able to deploy and fund an exploit contract, send a single constructor transaction that self-approves and then calls burnFrom against the victim, and route swaps through a DEX router to extract USDT.
  • Gas costs for the deployment and swaps must be small relative to the USDT value unlocked by destroying the victim’s TCR reserve, ensuring positive net profit.

All of these conditions are satisfied in the observed attack, and they remain reproducible for any adversary that can deploy an equivalent constructor against the same vulnerable TcrToken deployment.

5. Adversary Flow Analysis

5.1 Adversary-related accounts

The adversary-related cluster consists of:

  • EOA 0xb19b7f59c08ea447f82b587c058ecbf5fde9c299: sender of the exploit transaction and final recipient of 639,222.253258 USDT.
  • Exploit contract 0x6653d9bcbc28fc5a2f5fb5650af8f2b2e1695a15: deployed by the EOA; its constructor performs the TcrToken approvals, the abusive burnFrom call, and the swaps via UniswapV2Router02 that move USDT from the pool to the EOA.

Victim-related contracts are:

  • TecraCoin TcrToken at 0xe38b72d6595fd3885d1d2f770aa23e94757f91a1 (verified source available).
  • TCR/USDT liquidity pool at 0x420725a69e79eeffb000f98ccd78a52369b6c5d4 (pair contract used as the TCR holder and USDT source).

5.2 Lifecycle stages

  1. Initial funding

    • Internal transfers deliver ETH to the attacker EOA in the following transactions:
      • 0xfaf32d4a7eb2b17857d8d17b6aaa457cbc68b73ed84c8a4a3b23aa959380a2d5 (block 14138961),
      • 0x91946c20c99d3d3837244f23f0406f82ca3482ac95ae4e6c017984d1125994c7 (block 14139280),
      • 0xee1e24d077f96393d1439f6bf20e48a3122ec9c252de69eb69154f7cf4a14f7e (block 14139320).
    • These internal transfers, visible in the address-level trace data, accumulate sufficient ETH for the contract deployment and gas.
  2. Exploit contract deployment

    • The attacker sends a single normal transaction with nonce 0 and hash 0x81e9918e248d14d78ff7b697355fd9f456c6d7881486ed14fdfb69db16631154, deploying the exploit contract 0x6653d9bcbc28fc5a2f5fb5650af8f2b2e1695a15.
    • The transaction includes 0.04 ETH value and consumes 0.061095144261776538 ETH in gas, as shown by the receipt and prestate balance diff.
  3. Constructor exploit and payout

    • During the constructor execution, the exploit contract:
      • Calls TcrToken approve to set large allowances from the contract to UniswapV2Router02 and to the TCR/USDT pool address.
      • Invokes the vulnerable burnFrom path with from = 0x420725a69e79eeffb000f98ccd78a52369b6c5d4 and an amount of 58017069442472 TCR units, relying on the mis-indexed _allowances[msg.sender][from] check.
      • Routes swaps through UniswapV2Router02 that cause USDT to move from the TCR/USDT pool to the exploit contract and then to the attacker EOA.
    • USDT Transfer logs confirm that the attacker EOA receives 639,222.253258 USDT in this single transaction.
    • The exploit completes entirely within the constructor; there are no follow-up attacker-crafted transactions required to realize the profit.

Because all of these steps are driven by a single public transaction from an unprivileged EOA and rely only on publicly deployed contracts and on-chain state, the strategy is an ACT opportunity.

6. Impact & Losses

On-chain balances and logs quantify the impact as follows:

  • The TCR/USDT pool loses 58,017,069,442,472 raw TCR units. With 8 decimals, this corresponds to 580,170,694.42472 TCR in economic exposure.
  • The same pool’s USDT reserve is reduced by 639,222.253258 USDT, as shown by TetherToken Transfer logs routing USDT from the pool to the exploit contract and then to the attacker EOA.
  • The attacker EOA pays exactly 0.061095144261776538 ETH in gas for the exploit transaction, equivalent to about 183.29 USDT when valued at approximately 3,000 USDT/ETH at the block timestamp.
  • The attacker’s net profit in the USDT reference asset is approximately +639,038.97 USDT, computed as the USDT inflow minus the gas cost converted into USDT.

The direct victims are liquidity providers in the TCR/USDT pool, whose TCR and USDT positions are depleted. The TcrToken contract itself remains deployed with the same vulnerable logic, so the same pattern can be applied to any other TCR holder that is not otherwise protected.

7. References

  • Seed exploit transaction and trace
    Ethereum mainnet tx 0x81e9918e248d14d78ff7b697355fd9f456c6d7881486ed14fdfb69db16631154 (block 14139082), including RPC metadata, receipt, trace, and prestate balance diff (local artifacts: tx.rpc.json, receipt.rpc.json, trace.cast.log, balance_diff.prestate.json).

  • TecraCoin TcrToken contract source
    Verified source for TcrToken at 0xe38b72d6595fd3885d1d2f770aa23e94757f91a1, including the vulnerable burnFrom implementation (local artifact: Contract.sol).

  • Attacker EOA funding history
    Address-level txlists for 0xb19b7f59c08ea447f82b587c058ecbf5fde9c299, showing internal ETH funding transactions and the single exploit transaction with nonce 0.

  • TCR/USDT pool behavior
    Prestate ERC20 balance diff and USDT Transfer logs demonstrating the TCR burn and USDT outflow from 0x420725a69e79eeffb000f98ccd78a52369b6c5d4 to the exploit contract and then to the attacker EOA.

  • ACT opportunity characterization
    Root cause and ACT opportunity definitions as encoded in the analysis artifacts, tying the mis-indexed burnFrom allowance check to an anyone-can-take, single-tx profit strategy.