BOOK THIS SPACE FOR AD
ARTICLE ADSummary
Whitehat Turbo (Discord: turbo#1177) submitted a “critical” and a “high” vulnerability in Sovryn’s smart contract to Immunefi on March 11, 2021. The critical vulnerability consisted of a failure to validate that the receiver of the proceeds of a collateralized loan was the same entity as the borrower, meaning a malicious user could request a loan based on unused collateral from another user. Effectively, this let a malicious user steal an amount equal to the amount of the collateral. The high vulnerability applied to margin trades on Sovryn. While users could be pushed into undesirable positions, no funds could have been stolen, so this vulnerability was ranked as “high”.
The critical vulnerability was not exploited, and the risk to user funds was relatively low — 6,798.17 USDT. Sovryn swiftly implemented a fix after the bug was reported.
With recommendations from Immunefi, the whitehat was rewarded with a base payout of $50,000 for the critical vulnerability, bumped up to $76,568 because of the high vulnerability bug, which then had the 15% bonus that was in place at the time of submission and the discovery of the high vulnerability. At the time of the submission, Sovryn had the largest bug bounty on Immunefi. In addition, Sovryn is creating two custom NFTs for Turbo to commemorate the findings. The high payout, given the actual funds at risk, reflects the overall risk to the protocol over time and the damage that would result if the vulnerability were exploited in the future.
We want to thank Sovryn for generously rewarding whitehats who do the right thing and report vulnerabilities. Such generosity represents an important investment in Sovryn’s security, the Sovryn community, the Bitcoin community, and the RSKDeFi community at large. Sovryn places a strong emphasis on security.
Vulnerability Analysis
Sovryn is an on-chain decentralized trading and lending protocol deployed on RSK, a side chain of the Bitcoin blockchain. As a lending platform, users can both lend and borrow to and from a pool. Lending is how users earn interest on their BTC, and that interest comes from the fees which borrowers pay when they borrow BTC to engage in margin trading.
The vulnerability itself was present in LoanTokenLogicStandard.sol under the borrow() and marginTrade() functions. The function borrow() has a number of normal parameters, such as loanId, which identifies a pool of collateral that can be drawn against. A malicious user could call borrow(), pass a valid loanId/borrower pair (each loanId maps to a borrower address), and then enter an arbitrary address for the receiver parameter. This might make sense if a regular user wants to send the loan amount to a different address of theirs, but that same functionality could have been used by a malicious user to pass a loanId of unused collateral and then send to the malicious user’s address.
The step-by-step exploitation of the vulnerability is as follows:
1: Identify a loan based on loanId that has unused collateral
2: Call borrow(), passing valid parameters for that loan. While borrower has to match loanId, any address can be passed as receiver. In this case, a malicious user passes their own address in receiver
3: The malicious user now has a loan using someone else’s collateral, so there is no penalty to the malicious user for failing to pay the loan back, and that malicious user can steal an amount of another token to the amount of the collateral.
The same vulnerability applies to margin trading under marginTrade(), which didn’t check the caller of the function. A malicious user could have put someone else into an unfavorable position (though not stolen any funds) and then exploited the fact that they were in that position. In other words, a malicious user could have forced a particular user to enter a margin position because, as with borrow(), the only thing that the contract verified is that a malicious user knows the borrower address, not that they are the borrower.
Vulnerability Fix
For both borrow() and marginTrade(), the fixes were the same:
require(loanId == 0 || msg.sender == borrower, “13”);
require(loanId == 0 || msg.sender == trader, “13”);
This fix preserves the ability for the loan originator to specify a receiver of the funds of the loan while preventing any malicious use of the collateral.
Acknowledgements
We’d like to thank the Sovryn team for their professionalism in response to the bug submission. To report additional vulnerabilities, please see Sovryn’s bug bounty program with Immunefi. If you’re interested in protecting your project with a bug bounty like Sovryn, visit the Immunefi services page and fill out the form.