BT.Finance Exploit analysis report

BT Finance
5 min readFeb 12, 2021

The exploiter bypassed our defenses through abnormal approaches and implemented his attacks. We have taken multiple preventive measures in the bVault V2 contract (audited):

  • The contract address deposit is not allowed
  • Sets no withdrawals within one minute
  • 0.5% withdrawal fees within 24 hours.

So to be precise, this attack was not exactly the same as the exploit of Yearn as reported by the media.

We would like to thank PeckShield for their support. After the attack, the PeckShield team immediately assisted us in analyzing and positioning the attack address, assisting our Devs in fixing the bug.

The specific attack principle is as follows:

1. Bypass contract address deposit

By building the constructor self-destruction contract, the exploiter bypassed our defense, which was the same as yDAI attack principle.

Repair suggestions:

Firstly use setmin(0) to prevent from attacking again.

We previously had used the above function to limit contract deposits. However, It has been bypassed by the exploiter, this is the key error of this attack.

After taking PeckShield’s suggestions and referring to the contract of badger.finance, we have updated to use msg.sender == tx.origin to perform contract judgment again.

Lastly, restrict Earn authority, only GOV address are allowed.

2. Break through NO withdrawals within one minute

The exploiter(s) made a transfer from A to B, and the deposit Time of B is equivalent to 0

3. The withdrawal fee set at 0.5% within 24 hours played an important role.

We had set a 24-hour handling fee of 0.5%. Thus, the exploiter(s) left nearly10% withdrawal service fee. Hereby, It is advised to increase the 24-hour withdrawal fee to 10%.

All above fixes will be updated in Vault V3.

In addition, we need to point out that the attacked curve strategy is not within the scope of PeckShield’s audit, but they also gave us repair suggestions at the first time. In the following update, we will add multiple slippage controls to increase more defenses.

In this exploit, the exploiter(s) made a total profit of 31.87renBTC and 211 ETH, and used REN and Tornado.Cash to transfer assets anonymously.

https://etherscan.io/tx/0xa1821af44f6dc6ea4b1ea64a3b49640b9cb791d5e4bccf57cdcfdb3d173323c3

https://etherscan.io/tx/0xea1a4844e6cbb705ec80bcb631c813f8ad88e904eb8c135bc7d15a64806963a7

We have tracked the address of the stolen BTC fund:

https://btc.com/7c849b024e7f3a56287b52bc7f55db5425a4204efcd2c117f5621ef821fbe0cd

And contacted dozens of exchanges such as @binance @coinbase for help, once the stolen assets are deposited in these exchanges, it will be frozen.

Meanwhile, We have already called the police in Singapore and will entrust a lawyer to deal this exploit event. We still hope that the hacker can return the accets to our users initiatively, In return, we will use $BT tokens as a bug bounty for him.

Below we take ETH Vault Exploit as a case to show the attack process in detail:

— — — -

Analyze tx: 0xc71cea6fa00d11e98f6733ee8740f239cb37b11dec29e7cf85d7a4077977fa65

— — — -

exploit contract:

0x54b5ae5ebe86d2d86134f3bb7e36e7c83295cbcb

Curve SETH Pool (ETH, sETH):

0xc5424b857f758e906013f3555dad202e4bdb4567

bETH iVault:

0xded1219b5ba4da3a9d57c9f12c0d5da50f2fc4a2

StrategyETHCurve:

0xaeb39e44aee822cf4443deb92159a710ef50a55e

Prepare

— — — -

0.1 dYdX.borrow(100,000 ETH)

Round 1: 0x9381fb80871bce0c10e730185c608e4fe7940dc4 (62,000 ETH)

1.1 Swap in Curve ETH Pool: 57,666.0 ETH -> 35294.232881891396 sETH

. add_liquidity(57,666.0 ETH, 0 sETH) -> 57028.906978154126 eCRV

. remove_liquidity_one_coin(57028.906978154126 eCRV) -> 35294.232881891396 sETH

. Controller.balanceOf(WETH) = 1099.930468663005779217

1.2 bETH.deposit(4,340.0 ETH) -> 4259.885997221933 bETH

. earn() -> StrategyETHCurve -> 3216.321690633709 eCRV

1.3 Swap in Curve ETH Pool: 35294.232881891396 sETH -> 58469.69262068282 ETH

. add_liquidity(0 ETH, 35294.232881891396 sETH) -> 57805.602266091555 eCRV

remove_liquidity_one_coin(57805.602266091555 eCRV) -> 58469.69262068282 ETH

1.4. bETH.withdraw() -> 3616.5580435104393 ETH

. burn 3394.101869098816 eCRV

Round 1 Hack Gain: 62,000 ETH -> 62086.25066419326 ETH

Round 1 Pool Loss: 3394.101869098816–3216.321690633709 = 177.78017846510738 eCRV

Round 2: 0x2a98b811692b5ee8ea6e5cec884805bfaaa4de20 (62,000 ETH)

2.1 Swap in Curve ETH Pool: 57,666.0 ETH -> 35288.77748281687 sETH

. add_liquidity(57,666.0 ETH, 0 sETH) -> 57009.652363748966 eCRV

. remove_liquidity_one_coin(57009.652363748966 eCRV) -> 35288.77748281687 sETH

. Controller.balanceOf(WETH) = 944.967790279308215077

2.2 bETH.deposit(4,340.0 ETH) -> 4981.450006575986 bETH

. earn() -> StrategyETHCurve -> 3228.614072041635 eCRV

2.3 Swap in Curve ETH Pool: 35288.77748281687 sETH -> 58474.2351424399 ETH

. add_liquidity(0 ETH, 35288.77748281687 sETH) -> 57790.48017896747 eCRV

. remove_liquidity_one_coin(57790.48017896747 eCRV) -> 58474.2351424399 ETH

2.4. bETH.withdraw() -> 3590.941395508768 ETH

. burn 3367.6215042828794 eCRV

Round 2 Hack Gain: 62,000 ETH -> 62065.17653794867 ETH

Round 2 Pool Loss: 3367.6215042828794–3228.614072041635 = 139.00743224124426 eCRV

Round 3: 0x4679d4051a6a0d7df5f76fdb4df7c3adbd4f580c (62,000 ETH)

3.1 Swap in Curve ETH Pool: 57,666.0 ETH -> 35282.7714531728 sETH

3.2 bETH.deposit(4,340.0 ETH) -> 5842.9280950491275 bETH

. earn() -> StrategyETHCurve -> 3226.8233870053946 eCRV

3.3 Swap in Curve ETH Pool: 35282.7714531728 sETH -> 58474.70731849393 ETH

3.4. bETH.withdraw() -> 3570.3538891253834 ETH

. burn 3345.9209580057573 eCRV

Round 3 Hack Gain: 62,000 ETH -> 62045.061207619314 ETH

Round 3 Pool Loss: 3345.9209580057573–3226.8233870053946 = 119.09757100036268 eCRV

Round 4: 0xf67c580f75afa35264c0799d1abcf7a07f8f424b (62,000 ETH)

4.1 Swap in Curve ETH Pool: 57,666.0 ETH -> 35276.483209232 sETH

4.2 bETH.deposit(4,340.0 ETH) -> 6859.570735225316 bETH

. earn() -> StrategyETHCurve -> 3225.1351310633063 eCRV

4.3 Swap in Curve ETH Pool: 35276.483209232 sETH -> 58475.11542573982 ETH

4.4. bETH.withdraw() -> 3551.7975688851066 ETH

. burn 3326.2596594667602 eCRV

Round 4 Hack Gain: 62,000 ETH -> 62026.91299462493 ETH

Round 4 Pool Loss: 3326.2596594667602–3225.1351310633063 = 101.12452840345395 eCRV

Round 5: 0x02246319c150ab94e874bb6545d2c92f2dad96ac (62,000 ETH)

5.1 Swap in Curve ETH Pool: 57,666.0 ETH -> 35269.94068723891 sETH

5.2 bETH.deposit(4,340.0 ETH) -> 8049.204763163109 bETH

. earn() -> StrategyETHCurve -> 3223.527417036188 eCRV

5.3 Swap in Curve ETH Pool: 35269.94068723891 sETH -> 58475.46214445452 ETH

5.4. bETH.withdraw() -> 3535.3270632472872 ETH

. burn 3308.6902397328777 eCRV

Round 5 Hack Gain: 62,000 ETH -> 62010.78920770181 ETH

Round5 Pool Loss: 3308.6902397328777–3223.527417036188 = 85.16282269668955 eCRV

6. repay dYdX(100,000 ETH)

Summary:

Round 1 Hack Gain: 62,000 ETH -> 62086.25066419326 ETH

Round 2 Hack Gain: 62,000 ETH -> 62065.17653794867 ETH

Round 3 Hack Gain: 62,000 ETH -> 62045.061207619314 ETH

Round 4 Hack Gain: 62,000 ETH -> 62026.91299462493 ETH

Round 5 Hack Gain: 62,000 ETH -> 62010.78920770181 ETH

→ 234.190612087984 ETH

Round 1 Pool Loss: 177.78017846510738 eCRV

Round 2 Pool Loss: 139.00743224124426 eCRV

Round 3 Pool Loss: 119.09757100036268 eCRV

Round 4 Pool Loss: 101.12452840345395 eCRV

Round 5 Pool Loss: 85.16282269668955 eCRV

→ 622.1725328068578 eCRV

Media reports and related materials:

https://twitter.com/defiprime/status/1358881740178132993

https://twitter.com/bneiluj/status/1358890501609574402

https://twitter.com/doug_storming/status/1358896348276391939

https://github.com/iearn-finance/yearn-security/blob/master/disclosures/2021-02-04.md

https://github.com/peckshield/publications/blob/master/audit_reports/peckshield-audit-report-btdotfinance-v1.0.pdf

--

--