Open code423n4 opened 1 year ago
I’m kinda skeptical of this but I think its possible in theory.
However:
stabilize
can only be called via EOA due to msg.sender == tx.origin
check (in onlyEOA
modifier)I would be very curious to see a real PoC of this rather than just a theoretical threat.
Regarding the previous comment:
onlyEOA
check can be bypassed using a sandwich attack instead of a flashloan so the possibility of a MEV attack still existsAlthough the possibility of this being implemented depends on the size of the incentives and the cost of manipulating the AMM, it does not seem so unlikely. It could lead to a significant loss for the protocol, so I agree that high severity is appropriate.
Picodes marked the issue as satisfactory
0xScotch marked the issue as sponsor acknowledged
Lines of code
https://github.com/code-423n4/2023-02-malt/blob/700f9b468f9cf8c9c5cffaa1eba1b8dea40503f9/contracts/StabilityPod/StabilizerNode.sol#L312
Vulnerability details
Impact
In StabilizerNode, the default behaviour when twap is below the lower peg threshold all transfers to the amm pool are blocked. However when
usePrimedWindow = true
, it will only block transfers forprimedWindow = 10
blocks. After 10 blocks, the block automatically stops and allow free market trading.The first one call to start this priming will receive
defaultIncentive
Malt and setprimedBlock
to start the priming. However, function_validateSwingTraderTrigger()
which used to validate and start the priming usinglivePrice
which is easy to be manipulated. Attacker can manipulate it to receivedefaultIncentive
in 2 consecutive blocks.Proof of Concept
Consider the scenario
maltDataLab.getSwingTraderEntryPrice()
, attacker callstabilize()
and receivedefaultIncentive
.primedBlock = block.number
._validateSwingTraderTrigger()
returntrue
and trigger swing trader to bring the price back to peg. It's also resetprimedBlock = 0
(stop blocking transfer to AMM pool)maltDataLab.getSwingTraderEntryPrice()
(because twap moves slowly and will not change immediately to current price)livePrice
to be larger thanentryPrice
(tranfer to AMM is not blocked) and callstabilize()
to receive incentive again then repay the flash loan.Attacker cost is only flash loan fee, since his call will start an auction but not trigger swing trader so the state of AMM pool when he repay the flash loan is still the same (only added flash loan fee).
https://github.com/code-423n4/2023-02-malt/blob/700f9b468f9cf8c9c5cffaa1eba1b8dea40503f9/contracts/StabilityPod/StabilizerNode.sol#L312-L334
Tools Used
Manual Review
Recommended Mitigation Steps
Consider not giving incentives for caller or reset the
primedBlock
at least afterprimedWindow
blocks.