@taichiaudit Founder | Core Member @DeFiHackLabs

GMT+8
Joined January 2018
Easter egg trigged
11 crit/high reports submitted to the same program, 3 dups, others waiting review馃ぃ
6
46
2,634
Paid $250 bucks for a mediation and got closed after 25 minutes with this response, stay away from this scam. Do not pay for mediation.
8
1
3
90
5,846
Replying to @chrisdior777
Need to be a good lawyer to get beg bounty
1
6
104
Glad to join the @HackenProof Vanguard program! Will spend more time hunting bugs on HP and share knowledge with the community馃
2
1
29
1,539
Replying to @brandon_shi
Immunefi triaged ones like this?
61
Passed Daml fundamentals cert exam馃榿Thanks @digitalasset for the great intro course
2
1
23
1,711
Translation: you passed verification but we won't allow you to do your regular job, what you gonna do about it kid?
2
1
7
1,462
There is a second bug that makes this path reachable much more easily: the insolvency gate is broken. The comments say liquidation should depend on the borrower鈥檚 shortfall (the underwater amount), but calculateDiscountedRepayToEvenAmount() reads the target account鈥檚 shortfall and then never uses it. You can ctrl+f for accountShortfall_TargetUser and see it is not used anywhere in the contract. Instead, the close cap (the maximum amount of the borrower鈥檚 debt that this liquidation is allowed to repay in one call) is effectively derived from close factor and borrow balance.
1
1
180
During the write phase it stores the borrower-side result, then stores the liquidator-side result. Because both storage variables point to the same slot, the second write overwrites the first one. Net result: the slot ends up with a + b, not a neutral self-transfer. So the liquidation repays real debt but credits extra internal collateral to the attacker.
1
1
98
After that, the code first computes localResults.updatedSupplyBalance_TargetCollateralAsset = localResults.currentSupplyBalance_TargetCollateralAsset - localResults.seizeSupplyAmount_TargetCollateralAsset. It then computes localResults.updatedSupplyBalance_LiquidatorCollateralAsset = localResults.currentSupplyBalance_LiquidatorCollateralAsset + localResults.seizeSupplyAmount_TargetCollateralAsset. You can think of this as localResults.updatedSupplyBalance_TargetCollateralAsset = a - b and updatedSupplyBalance_LiquidatorCollateralAsset = a + b.
1
1
108
Then the function converts checkpointed storage balances into accumulated local balances. borrowBalance_TargeUnderwaterAsset becomes localResults.currentBorrowBalance_TargetUnderwaterAsset via calculateBalance(borrowBalance_TargeUnderwaterAsset.principal, borrowBalance_TargeUnderwaterAsset.interestIndex, localResults.newBorrowIndex_UnderwaterAsset). Likewise, supplyBalance_TargetCollateralAsset becomes localResults.currentSupplyBalance_TargetCollateralAsset, and supplyBalance_LiquidatorCollateralAsset becomes localResults.currentSupplyBalance_LiquidatorCollateralAsset, by calling calculateBalance() with each balance's stored principal, stored interestIndex, and the shared localResults.newSupplyIndex_CollateralAsset. Here you can see that localResults.currentSupplyBalance_TargetCollateralAsset and localResults.currentSupplyBalance_LiquidatorCollateralAsset will have the same value.
1
1
1
383
The exploitable case is targetAccount == msg.sender and assetBorrow == assetCollateral. Under that condition, supplyBalance_TargetCollateralAsset and supplyBalance_LiquidatorCollateralAsset are not two independent positions. They are two storage references to the exact same supplyBalances slot. So the function is effectively trying to transfer collateral from an account back to itself using two aliased pointers.
1
1
135
High level understanding first: Alkemi tracks user positions in internal balance records: - supplyBalances[account][asset] for supplied collateral - borrowBalances[account][asset] for debt Each record is just a checkpointed principal plus an interestIndex. When the protocol needs the live amount, it accrues interest into a local variable with calculateBalance(). That function just computes the formula below:
1
3
311