Wormhole is a communication bridge between Solana and other blockchains. As such a big service, it needs to be updated. The issue, is that blockchains are immutable. So, how is this done? In some complicated and convoluted ways that have some weird negatives.
The goal is to change the code in use at a specific address but keep the state of the contract. To make this possible, there is one address that acts as the proxy and the other that acts as the contract code itself. The proxy holds the state of the contract. This is done via making a call to delegateCall() from the proxy to the contract. A final point is that the initialization function has to be a regular function instead of a constructor.
There are two common patterns for this: TPP and UUP. Transparent Proxy Pattern (TPP) ensures that all calls by a user execute the implementation logic and all calls by the admin implement the proxy contracts logic. TPP has to store information about the admin on the proxy side, making it much so efficient.
To combat this problem, the prevailing pattern is
Universal Upgradeable Proxy Standard (UUPS). The main difference is that the upgrade logic is in the
implementation contract instead of the
proxy contract. This makes the cost of updating cheaper and we no longer have name collisions. For more on these patterns, read
Transparent vs UUPS Proxies.
OpenZeppelin's (smart contract templates that are widely used) UUPS implementation had an access control problem. The init function set the caller to be the owner of the contract; this is another constructor related problem! With this, the attacker could upgrade the contract to their own malicious values, then set the new location to be their own contract. Finally, calling selfdestruct would drain the funds and make the proxy useless.
In the case of Wormhole, they had the same exact problem. An initialization function was never called and left open to the world. Using SELFDESTRUCT would have bricked the contract and stolen all of the money. To fix this, they simply had to call initialize themselves.
Overall, the proxy pattern is interesting but adds a lot of complexity. Good find for 10 million dollars.