Open code423n4 opened 1 year ago
0xA5DF marked the issue as primary issue
I have some doubts about severity, since the auction's final bid is expected to be at about the worth of the collateral. So the challenger isn't expected to lose anything but the challenge reward.
This is actually a high risk issue as the challenge is ended early as soon as the highest bid reaches the liquidation price.
I would even say that this is one of the most valuable findings I've seen so far!
The fix is to add front-running protection to the launchChallenge function:
function launchChallenge(address _positionAddr, uint256 _collateralAmount, uint256 expectedPrice) external validPos(_positionAddr) returns (uint256) { IPosition position = IPosition(_positionAddr); if (position.price() != expectedPrice) revert UnexpectedPrice();
luziusmeisser marked the issue as sponsor confirmed
Since the owner lower the price of the position, the collateral for a challenge worth nothing, and the challengers might lose their collateral. So I am agree with the sponsor.
hansfriese marked the issue as satisfactory
hansfriese marked the issue as selected for report
Lines of code
https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/MintingHub.sol#L140-L148
Vulnerability details
Impact
Challenges, once created, cannot be closed. Thus once a challenge is created, the challenger has already transferred in a collateral amount and is thus open for losing their collateral to a bidding war which will most likely close below market price, since otherwise buying from the market would be cheaper for bidders.
Position owners can take advantage of this fact and frontrun a
launchChallenge
transaction with anadjustPrice
transaction. TheadjustPrice
function lets the user lower the price of the position, and can pass the collateral check by sending collateral tokens externally.As a worst case scenario, consider a case where a position is open with 1 ETH collateral and 1500 ZCHF minted. A challenger challenges the position and the owner frontruns the challenger by sending the contract 1500 ZCHF and calling
repay()
and then callingadjustPrice
with value 0, all in one transaction with a contract. Now, the price in the contract is set to 0, and the collateral check passes since the outstanding minted amount is 0. The challenger's transaction gets included next, and they are now bidding away their collateral, since any amount of bid will pass the avert collateral check.The position owner themselves can backrun the same transaction with a bid of 1 wei and take all the challenger's collateral, since every bid checks for the
tryAvertChallenge
condition.Since price is set to 0, any bid passes this check. This sandwich attack causes immense losses to all challengers in the system, baiting them with bad positions and then sandwiching their challenges.
Since sandwich attacks are extremely commonplace, this is classified as high severity.
Proof of Concept
The attack can be performed the following steps.
adjustPrice
call lowering the price.Tools Used
Manual Review
Recommended Mitigation Steps
When launching a challenge, ask for a
expectedPrice
argument. If the actual price does not match this expected price, that means that transaction was frontrun and should be reverted. This acts like a slippage check for challenges.