1Inch is a limit order swap DeFi platform. 1Inch Fusion is a gasless swap protocol built on top of the core Limit Order Protocol. This version was deprecated in 2023 but was kept alive for backwards compatibility reasons. The original implementation had over 9 audits.
Recently, the V1 protocol was hacked. Looking at the exploit transactions, it starts off looking very normal. Then, came a red flag: both the taker and the maker on the swap were the same. Additionally, the attacker had made over 1M USDC on the trade.
Upon trying to make the Settlement of the limit order, a call to resolveOrders is made to a contract. This code appeared to be very similar to the example integration and looked pretty safe. Upon closer inspection, the victim contract had not updated the protocol even when the interfaces had changed.
The vulnerability appears to be around lack of validation on resolver. This is a NOT intended to be a controlled element of settleOrder. In fact, it's passed in with new bytes(0) since only the contract itself should be able to set it. How was this controlled? What gives? The woes of multiple versions!
Much of this code is written in Yul, the Assembly of Solidity. In the Yul, there is a ptr. When writing a value called the suffix, it's written at an offset depending on the passed in interactionLength. interactionLength is a full 32 byte word, which can overflow when doing ptr + interactionOffset + interactionLength. Because of this overflow, the pointer can be decreased to write the user controlled suffix to any location! Sounds like memory corruption!
Here's the full flow of the attack:
- Create a swap order that swaps a few wei for millions. Normally, this would obviously be rejected.
- Specify an invalid
interactionLength value to overflow to point to the resolver address.
- Add a fake suffix structure to overwrite the resolver address.
The authors of this post did the internal investigation but also did several of the audits on the protocol. So, what happened? Initially, the resolver contract code wasn't in scope for audits, so it was ignored. In March of 2023, these auditors actually found the integer overflow while assessing the scope. However, shortly after, the code was completely rewritten so they didn't feel it was necessary to callout.
Here's the interesting twist: this previous version of the contract had already been deployed and the auditors didn't know it. Additionally, they were unsure about the impact of the vulnerability, so they moved on from it.
How can we prevent this type of thing from happening in the future?
- Clearly defined what code is being used. It's acceptable to have multiple versions but both need to be audited.
- Informational findings are helpful. There may be more impact of something than an auditor initially realizes.
Overall, a super cool postmortem on the exploitation of this vulnerability. The vulnerability is unique and I love the analysis on how this slipped through the cracks.