All incidents

Themis Oracle Manipulation

Share
Jun 27, 2023 18:33 UTCAttackLoss: 94.32 ETH, 130,471.92 USDC +1 morePending manual check1 exploit txWindow: Atomic
Estimated Impact
94.32 ETH, 130,471.92 USDC +1 more
Label
Attack
Exploit Tx
1
Addresses
3
Attack Window
Atomic
Jun 27, 2023 18:33 UTC → Jun 27, 2023 18:33 UTC

Exploit Transactions

TX 1Arbitrum
0xff368294ccb3cd6e7e263526b5c820b22dea2b2fd8617119ba5c3ab8417403d8
Jun 27, 2023 18:33 UTCExplorer

Victim Addresses

0x75f805e2fb248462e7817f0230b36e9fae0280fcArbitrum
0xc8f42db9eb6ab58bbfa6e2642107a6086cb4473bArbitrum
0xe611e633c1e88d4f026fec5bc1e40e8a477f41adArbitrum

Loss Breakdown

94.32ETH
130,471.92USDC
58,824.33USDT

Similar Incidents

Root Cause Analysis

Themis Oracle Manipulation

1. Incident Overview TL;DR

On Arbitrum transaction 0xff368294ccb3cd6e7e263526b5c820b22dea2b2fd8617119ba5c3ab8417403d8 in block 105524524, an unprivileged attacker used flash-loaned WETH to skew Balancer's wstETH-WETH stable pool, refreshed Themis's Balancer-gauge oracle while that skew was live, and then borrowed WETH against overstated gauge collateral. The same transaction repaid the flash liquidity and left the attacker with realized profits in ETH, USDC, and USDT.

The root cause is a lending-oracle design failure in Themis. Themis trusted a same-transaction LP price derived from live Balancer pool balances, so temporary attacker-controlled balance changes could immediately increase collateral value inside the borrow path.

2. Key Background

Themis is an Aave-style lending market on Arbitrum with reserves such as tArbWETH, tArbDAI, tArbUSDC, tArbUSDT, tArbARB, and tArbWBTC. The affected collateral was Balancer gauge 0x8f0b53f3ba19ee31c0a73a6f6d84106340fadf5f, which represents LP exposure to Balancer pool 0x36bf227d6bac96e2ab1ebb5492ecec69c691943f (wstETH-WETH stable pool). Themis relied on Balancer LP oracle 0x17df2b52f5d756420846c78c69f4fe4ff10e57a4, and its price-oracle layer at 0xc8f42db9eb6ab58bbfa6e2642107a6086cb4473b consumed that LP price during borrow-limit checks in controller 0x75f805e2fb248462e7817f0230b36e9fae0280fc.

The relevant public protocol components identified in the analysis are:

  • Themis controller 0x75f805e2fb248462e7817f0230b36e9fae0280fc
  • Themis price oracle 0xc8f42db9eb6ab58bbfa6e2642107a6086cb4473b
  • Themis WETH reserve 0xe611e633c1e88d4f026fec5bc1e40e8a477f41ad
  • Balancer LP oracle 0x17df2b52f5d756420846c78c69f4fe4ff10e57a4
  • Themis subaccount proxy 0xde85d18adda9d2b9eafa7dbf0cec5a89119d90f0

The ACT pre-state used in the analysis is Arbitrum immediately before block 105524524 tx index 3, including Themis reserve balances, Balancer pool balances and total supply, Balancer gauge balances, and the public Chainlink feeds for WETH and wstETH. The auditor marked each of the key protocol contracts above as is_verified=false; the exploit analysis therefore relies on the collected trace and runtime behavior rather than verified explorer source for those addresses. The attack still qualifies as ACT because every critical call path used public on-chain entrypoints: Aave V3 flash loans, Uniswap V3 flash liquidity, Balancer joins and swaps, and Themis supply and borrow flows.

3. Vulnerability Analysis & Root Cause Summary

This incident is an ATTACK-category oracle-manipulation exploit. Themis priced Balancer gauge collateral from spot Balancer balances that were observable at the exact moment of the borrow, rather than from a manipulation-resistant source such as a TWAP or delayed oracle update. That allowed the attacker to use flash liquidity to push WETH into the wstETH-WETH stable pool, drastically change the pool composition, and immediately force 0x17df...::latestAnswer() to return an inflated LP price. Themis then consumed that inflated LP price through 0xc8f42...::getAssetPrice() during 0x75f805...::borrow(), so the borrow-limit calculation treated the attacker subaccount as if it had much more collateral than it actually did. Once the WETH borrow succeeded, the attacker unwound the Balancer distortion and repaid the flash liquidity, proving that the price increase was transient and fully attacker-controlled. The broken invariant is straightforward: collateral valuation used in health-factor and borrow-limit checks must not be increaseable within the same transaction by temporary pool balance changes under attacker control.

The vulnerable components were:

  • Themis controller / borrow path 0x75f805e2fb248462e7817f0230b36e9fae0280fc
  • Themis price oracle 0xc8f42db9eb6ab58bbfa6e2642107a6086cb4473b
  • Balancer LP oracle 0x17df2b52f5d756420846c78c69f4fe4ff10e57a4
  • Balancer gauge collateral reserve 0x8f0b53f3ba19ee31c0a73a6f6d84106340fadf5f

The exploit required only three public conditions:

  • enough temporary WETH liquidity to skew the Balancer pool,
  • the ability to make Themis consume the skewed oracle before the skew was reverted,
  • the ability to post gauge shares as collateral through the standard Themis proxy/subaccount flow.

The violated security principles are the same ones called out in the root cause artifact:

  • do not use flash-loan manipulable spot AMM balances as lending collateral oracles,
  • borrow-limit checks must use manipulation-resistant pricing,
  • onboarding exotic LP or gauge collateral requires oracle logic that remains valid under large temporary balance swings.

4. Detailed Root Cause Analysis

The collector trace shows the attacker first assembling temporary WETH liquidity from Aave V3 and Uniswap V3. It then set up a fresh Themis subaccount, joined the Balancer pool with 55 WETH, deposited the received BPT into the Balancer gauge, and supplied 54.665092912647660455 gauge shares into Themis as collateral.

The key oracle breakpoint is visible before and after the Balancer skew. Before manipulation, the LP oracle returned a normal baseline value:

0x17df2B52f5D756420846c78c69F4fE4fF10e57A4::latestAnswer()
  MetaStablePool::totalSupply() -> 5445998568169458716380
  BalancerVault::getPoolTokens(...) -> [wstETH=2423240452850875484052, WETH=2740951628276599438098]
  ...
  <- [Return] 191099705466

After the attacker swapped 39724.937330688241115794 WETH into the Balancer pool, the pool balances and oracle output changed immediately:

BalancerVault::swap(... amountIn = 2423002389146086585543 wstETH ...)
  MetaStablePool::onSwap(...) -> 39724937330688241115794
...
0x17df2B52f5D756420846c78c69F4fE4fF10e57A4::latestAnswer()
  MetaStablePool::totalSupply() -> 5500663661082106376835
  BalancerVault::getPoolTokens(...) -> [wstETH=61447309529146061, WETH=42252095142331057715112]
  ...
  <- [Return] 1468795169887

That inflated LP price was then consumed directly in Themis's borrow path:

0x75F805e2fB248462e7817F0230B36E9Fae0280Fc::borrow(WETH, 317620373410593518825, 2, 0, 0x2132...)
  0xC8f42dB9eB6aB58bBFA6E2642107A6086CB4473B::getAssetPrice(0x8F0B53..., 0x2132...)
    0x17df2B52f5D756420846c78c69F4fE4fF10e57A4::latestAnswer()
      <- [Return] 1468795169887
  ...
  0xe611e633C1E88d4f026fec5Bc1E40E8A477f41aD::transferUnderlyingTo(0x2132..., 317620373410593518825)

This is the concrete code-level breakpoint described in the root cause artifact: latestAnswer() read live Balancer balances, getAssetPrice() consumed the manipulated value, and borrow() trusted it for collateral accounting. Once the WETH borrow completed, the attacker routed the borrowed WETH back out of the subaccount, reversed the Balancer skew, repaid Aave and Uniswap, and kept the residual assets.

The realized-profit predicate is fully supported by the seed balance diff. The sender EOA started with 0.4984956909 ETH, finished with 94.820454696766716128 ETH, and also ended the transaction with 130471.920034 USDC and 58824.329320 USDT. Using the same-transaction Chainlink WETH price 1900.07440273 USD, the collector-backed valuation is:

  • pre-tx value: 947.178902150296196157 USD
  • post-tx value: 369462.16817854604140905305822944 USD
  • realized delta: 368514.9892763957 USD

The root cause therefore is not the presence of flash loans themselves. Flash liquidity only made the attack large enough to matter; the actual failure was Themis allowing a same-transaction spot LP oracle to determine collateral value.

5. Adversary Flow Analysis

The attacker cluster identified in the artifact is:

  • 0xdb73eb484e7dea3785520d750eabef50a9b9ab33: sender EOA and final profit recipient
  • 0x05a1b877330c168451f081bfaf32d690ea964fca: exploit orchestration contract targeted by the seed tx
  • 0x33f3fb58ea0f91f4bd8612d9f477420b01023f25: helper contract deployed mid-transaction
  • 0x2132d49157d6148dee8753f059fad1c1b09c477c: freshly created Themis subaccount used for collateral and borrowing

The on-chain execution flow is:

  1. Flash liquidity acquisition. The orchestrator borrows 22000 WETH from Aave V3 and another 18000 WETH from two Uniswap V3 pools.
  2. Collateral setup. The attacker deploys helper 0x33f3..., creates subaccount 0x2132... through the public Themis subaccount proxy 0xdE85..., joins Balancer with 55 WETH, deposits the resulting BPT into the gauge, and supplies 54.665092912647660455 gauge shares to Themis.
  3. Oracle manipulation. The attacker swaps 39725 WETH into the Balancer wstETH-WETH stable pool, pushing the LP oracle from 191099705466 to 1468795169887.
  4. Over-borrow. While the manipulated price is still live, Themis lets the subaccount borrow 317.620373410593518825 WETH from tArbWETH.
  5. Unwind and realization. The attacker reverses the Balancer swap, repays the flash liquidity, and transfers the remaining ETH, USDC, and USDT profit to the originating EOA.

This is a fully permissionless ACT sequence. No admin action, no off-chain privileged data, and no private attacker artifact was required to make the victim protocol consume the manipulated price.

6. Impact & Losses

The conservative realized losses captured in the artifact are:

  • ETH: 94321959005866716128 wei (94.321959005866716128 ETH)
  • USDC: 130471920034 (130471.920034 USDC)
  • USDT: 58824329320 (58824.329320 USDT)

The exploit-specific protocol loss was the under-collateralized 317.620373410593518825 WETH borrow from Themis WETH reserve 0xe611e633c1e88d4f026fec5bc1e40e8a477f41ad. The broader attacker PnL after unwind is visible in the balance diff:

{
  "native_balance_delta_wei": "94321959005866716128",
  "usdc_delta": "130471920034",
  "usdt_delta": "58824329320"
}

Those values match the root cause artifact's realized-profit section and show that the attacker walked away with material value after every flash-liquidity obligation had been repaid.

7. References

  • [1] Seed transaction metadata: /workspace/session/artifacts/collector/seed/42161/0xff368294ccb3cd6e7e263526b5c820b22dea2b2fd8617119ba5c3ab8417403d8/metadata.json
  • [2] Seed transaction trace: /workspace/session/artifacts/collector/seed/42161/0xff368294ccb3cd6e7e263526b5c820b22dea2b2fd8617119ba5c3ab8417403d8/trace.cast.log
  • [3] Seed transaction balance diff: /workspace/session/artifacts/collector/seed/42161/0xff368294ccb3cd6e7e263526b5c820b22dea2b2fd8617119ba5c3ab8417403d8/balance_diff.json
  • [4] Collector seed index: /workspace/session/artifacts/collector/seed/index.json
  • [5] Public secondary PoC reference cited by the auditor: https://raw.githubusercontent.com/SunWeb3Sec/DeFiHackLabs/main/src/test/2023-06/Themis_exp.sol

The only exploit transaction required to reproduce the ACT opportunity is:

  • Arbitrum 42161: 0xff368294ccb3cd6e7e263526b5c820b22dea2b2fd8617119ba5c3ab8417403d8