Lessons from the Wormhole Exploit
TL;DR
Wormhole didn’t upgrade to the latest version of Solana standard lib, therefore it didn’t check stuff properly and allowed the attacker to bypass the signature verification process, thus stealing around $300M worth of ETH from the Wormhole Bridge.
I finally had some spare time this weekend and decided to do a full dive on the recent Solana & Wormhole Bridge hack.
Here is my understanding of what happened.
First things first. What is Wormhole?
Wormhole is a decentralized, cross-chain message passing protocol which enables applications to send messages from one chain to another. The network is operated by a decentralized group of nineteen Guardians who sign each transmitted message to attest to its authenticity.
Portal is a token bridge constructed on top of the Wormhole network. Portal enables users to deposit funds into a contract on a source chain, then mint a Wormhole-wrapped version of the token on a destination chain.
For example, if you want to move $ETH from Ethereum to Solana, Wormhole guardians will sign the transfer message, thus approving that you deposited ETH on Ethereum before you receive wrapped WeETH on the Solana chain.
Things are a bit more complicated but for the sake of general understanding, I will sacrifice some of the technical stuff.
Now, who is checking these signatures of the “guardians”?
Solana has native programs within the Instructions sysvar, something like a precompile. One of the native programs is secp256k1 which is responsible for validating the signatures.
P.S. In Solana, smart contracts are called programs.
Root cause
Wormhole Bridge used the recently deprecated function load_instruction_at to call the secp256k1 program. The main reason for being deprecated is that it does not check if the program address is actually a sysvar program or a fake program.
This allowed the attacker to bypass signature validation process. He created a fake program (2tHS1cXX2h1KBEaadprqELJ6sV9wLoaSdX68FqsrrZRd) which contained a single serialized instruction corresponding to a call to the Secp256k1 contract.
The attacker then passed this fake program instead of the Instruction sysvar that was supposed to validate the signatures. The rest is history.
Bypassing the signature validation, the attacker was able to “convince” the Wormhole Bridge to mint 120,000 ETH on the Solana chain into his account even though he didn’t deposit the equivalent on Ethereum.
He immediately withdrew 80,000 ETH back to the Ethereum network.
In total, around 93,751 ETH were withdrawn.
Fun fact #1
Before deploying the exploit, the attacker did a legit deposit of 0.1 ETH into the Wormhole Bridge (corresponding Solana transaction here), most probably testing his setup.
Notice the correct passing of Sysvar:Instructions account in the figure below.
Now compare with the transaction for the fake deposit of 120,000 ETH 👇
Fun fact #2
The Wormhole team replaced the deprecated load_transaction_at function with load_transaction_at_checked almost 3 weeks ago. From the community conversations, it seems that they were simply updating to the latest function versions to clear up the deprecation warnings.
Most likely the Wormhole team was unaware that there was security-critical content and it shouldn’t have been made public.
The attacker probably spotted the change and knew what kind of vulnerabilities the deprecated function enabled, and was able to execute the exploit before the team had the chance to deploy the fix.
Fun fact #3
Post exploit, the Wormhole team offered the attacker a $10M bug bounty to return the funds. The text message was encoded within a transaction sent to the attacker’s address.
Fun fact #4
The first hours after the exploit were critical since around $300 million worth of WeETH on Solana were unbacked with ETH for a period of time which meant that a number of Solana-based platforms that accepted WeETH as collateral could have become insolvent.
After the attempt to pay the attacker a bounty in return for the stolen funds was ignored, the Wormhole’s parent company, Jump Trading, stepped up and supplied Ether to replace stolen funds to prevent a systemic crash of Solana’s DeFi ecosystem if users rushed to sell their WeETH, crashing its value.
My takeaways
1. If you develop applications on top of Solana, make sure you replace the deprecated load_instruction_at function. Continuously check what functions are deprecated and assess the security impact if you still use them in your code.
2. The code review process should also be performed by a security expert who can assess the impact and mark changes as security-critical before making them public.
3. Offering a bug bounty after the exploit has been executed makes no sense, the attacker knew it can still face accusations and legal consequences even if Wormhole promised to forgive him in return of the stolen money.
Besides periodic audits, a good practice is to set up a proper bug bounty program beforehand (for example, use Immunefi — a web3 dedicated bug bounty platform) or at least state a 10% bounty of the potential damage, preferrably before it happens.