About a week after the launch of UniswapX I submitted a bug report to Uniswap Labs’ bug bounty program. The bug was regarding an unsafe user balance check performed during order settlement. This was a high risk vulnerability that put any user creating an order on UniswapX at risk. The early disclosure of the bug prevented it ever being exploited.
A bit of back story…
UniswapX is a signature-based order book protocol. It also allows order fillers to perform arbitrary execution during fulfillment to source the consideration tokens the offerers (order creators) request. Quite interestingly, a month before UniswapX launched, I had developed FloodPlain with Flood team. FloodPlain is very similar to UniswapX: They both use Permit2 witness signature for orders, and they both allow arbitrary execution during order fulfillment. They also had the same bug.
Yes, I wrote a very similar protocol to UniswapX and made the same mistake as Uniswap developers. Luckily, Flood team sent the code to a security review by Spearbit, which uncovered my mistake. This was a good lesson for me. I even shared this lesson in an EthCC 2023 side event presentation, one day after UniswapX launch. I gave all the breadcrumbs to my audience (and to myself) to find this bug. All we needed to do was to review UniswapX. My audience was kind enough to not frontrun me.
All the stars were aligned for me to discover this vulnerability. So I did discover it by a quick first glance at the contract. I was actually on a live call with Flood team trying to asses how difficult it was going to be to integrate UniswapX with FloodPlain. After the call I quickly wrote a coded POC and emailed it to the bug bounty program. Uniswap Labs was then quick to disable the UniswapX toggle from the UI. Within a week, they fixed the issue and redeployed the contracts. It was imperative that this issue was not publicized to put any users with active orders potentially at risk. In the end, no user funds were lost.
Enough story telling… What is the bug?
It is quite simple. Before and after doing a callback to the fill contract, UniswapX was checking the balance of offerer to see if the offerer address received the correct amounts of consideration tokens. This is exactly the same method used in UniswapV3 pools. However, a safe design for UniswapV3 pools turns out to be a fundamental design flaw in this context. The difference is that a UniswapX offerer (a regular DeFi user) can expect to receive tokens for other purposes, whereas UniswapV3 pools are expected to only receive tokens through their own functions which are all guarded with reentrancy locks.
The balance check flaw can be exploited easily. If a UniswapX v1.0 offerer has an order on another protocol with the same consideration token, during callback, the fill contract can just fill that external order and trick UniswapX balance check. In effect, the user would be giving away the offer token, but would not be getting the consideration tokens it signed for in the UniswapX order.
Below mock chat box explains this phenomenon. A user signs a UniswapX order with intent to give 35 WETH and get 70k USDC in return. The user gives the 35 WETH but does not get the 70k USDC.
17 days after my initial report, Uniswap Labs shared their verdict on the bounty amount.
As a reward for reporting this UniswapX bug, would like to award you a one time sum of 200,000 USDC. This includes a 50,000 USDC earlybird bonus for submitting the bug within the first 2 weeks of the UniswapX launch.
Uniswap Labs has classified this as a high severity bug. As a result of your great writeup, and the early submission, we included the additional bonus.
I am grateful to Uniswap Labs for this reward.
That’s all frens. Enjoy this rekt-free day 😇