Synthetix Logic Error Bugfix Review
On May 2, 2022, whitehat thunderdeep14 submitted a critical bug in Synthetix via Immunefi, which the Synthetix team then shortly patched.
For his bug find, thunderdeep14 was paid $100,000, which is the max critical reward for Synthetix’s bug bounty program on Immunefi. Additionally, since Synthetix is eligible for matching payouts under Nexus Mutual’s Matching Program, the whitehat will be earning an additional $50,000, making his total payout $150,000.
Immunefi is pleased to have facilitated this responsible disclosure with our platform. Our goal is to make Web3 safer by incentivizing hackers to responsibly disclose bugs and receive clean money and reputation in exchange.
Now, on to an explanation about the vulnerability itself.
Synthetix’s SIP 236 illustrates how fee reclamation and rebate features work in Synthetix.
One of the important features of the SIP is the following section:
The calculation of owing or owed is as follows:Amount * (1 — feeRate) * (srcRate/destRate — newSrcRate/newDestRate)If the result is negative, the amount is owed as a rebate.
This means that if the price of the destination token falls, the synth would rebate the destination token.
The vulnerability happens because Synthetix used the wrong variable, i.e., if the destination price decreases, then synth would rebate the difference in the destination token.
If a user were to call
exchange before reclaiming their rebate, the exchange
sourceAmount would be inflated. When an exchange occurs, the amount to be received by the user,
amountReceived, is being calculated inside the internal function call to
amountReceived is being assigned on [L486]:
amountReceived = _deductFeesFromAmount(entry.destinationAmount, entry.exchangeFeeRate);
If we look at how
entry.destinationAmount is being calculated [L442], we see
effectiveValueAndRatesAtRound is being called with the parameters
The vulnerability stems from passing the
sourceAmount here instead of
sourceAmountAfterSettlement. This means the full source amount (before the debts are applied) is being used to calculate the destination amount. When the call to
_convert occurs, only the
sourceAmountAfterSettlement is burned, but the
amountReceived calculated from the full
sourceAmount is being issued in the function.
To summarize, the function
_exchange utilized the
sourceAmount variable to calculate the amount that has to be rebated to the destination but did not do a post-reclaim check on it to see whether the amount is accurate relative to the dest token value.
To give an example of this vulnerability in action, let’s assume we are exchanging sUSD to sBTC, and the price of sBTC dropped in the cooldown period. Synth would rebate the difference amount in sBTC but the issue here is that the difference would be rebated in the destination token and synth failed to calculate the rebate value of sBTC in sUSD.
Synthetix fixed the vulnerability at the following commit.
We would like to thank thunderdeep14 for doing an amazing job and responsibly disclosing such an important bug. Big props also to the Synthetix team who responded quickly to the report and patched the bug.
If you’re a Web2 or Web3 developer who is finally thinking about a bug-hunting career in Web3, we got you. Check out our ultimate blockchain hacking guide, and start taking home some of the $135m in rewards available on Immunefi — the leading bug bounty platform for Web3.
And if you’re feeling good about your skillset and want to see if you will find bugs in the code, check out the bug bounty program from Synthetix.