Cross-chain bridges are fundamental infrastructure components in the blockchain ecosystem, enabling users to transfer assets between different blockchains. These bridges often rely on centralized mechanisms to facilitate interoperability. Since cross-chain bridges typically hold significant user-deposited assets, they have become prime targets for Web3 hackers. In recent years, numerous attacks and vulnerabilities related to cross-chain bridges have emerged. This article examines two major incidents: the Harmony bridge hack and the Wormhole proxy contract initialization vulnerability.
The Harmony Cross-Chain Bridge Attack
On June 23, 2022, a malicious attack targeted the cross-chain bridge connecting Harmony and Ethereum. The attacker gained control of the bridge owner's private keys and used administrative privileges to transfer vast amounts of various tokens held by the bridge. This security breach resulted in approximately $97 million in assets being stolen from the Harmony chain.
The bridge was controlled by a multi-signature wallet designed to require authorization from at least N administrators to execute operations, with N set to 2 in the actual implementation. This security measure was intended to enhance wallet security and raise the barrier for attacks.
The multi-signature wallet was implemented as a contract (MultiSigWallet.sol), where the asset transfer function executeTransaction() included confirmed(transactionId, msg.sender) validation. This required two different owners to sequentially call the submitTransaction and confirmTransaction functions to pass permission checks and initiate asset transfers or other operations.
Evidence suggests that the attacker likely gained control of two owner private keys through off-chain attack methods, effectively becoming the bridge owner. With these privileges, the attacker could legitimately manage assets, including transferring all holdings to their own account.
Key Takeaways
In the cryptography-based world of blockchain, nothing is more critical than ensuring private key confidentiality. Private key leakage represents both the most challenging and most damaging security vulnerability. Cross-chain bridge developers must address project-specific centralization risks and ensure the security of private keys used. Additionally, every blockchain user should understand this fundamental principle: among countless security considerations, private key protection remains paramount.
Wormhole Proxy Contract Initialization Vulnerability
On February 24, 2022, white-hat hacker satya0x responsibly disclosed an uninitialized proxy contract vulnerability in Wormhole's main bridge (Ethereum side), claiming the highest single bounty in Web3 vulnerability platform Immunefi's history: $10 million. This extraordinary reward naturally raises the question: what kind of critical vulnerability could warrant such a monumental bounty?
According to analysis by blockchain security researchers at Changting Technology, this vulnerability resulted from an uninitialized proxy contract on the Ethereum side of the Wormhole cross-chain project. Attackers could potentially gain control of the actual implementation contract behind the proxy contract and call an update function to force the implementation contract to self-destruct. This would freeze all Ethereum funds in the Wormhole project, permanently locking user assets transferred to Wormhole.
Based on the National Blockchain Vulnerability Database's Smart Contract Vulnerability Classification Guidelines v1.0, this vulnerability would prevent normal operation of core smart contract business functions, causing substantial asset freezes. Considering both the severe harm potential and low exploitation difficulty with consistent triggerability, this vulnerability was classified as high severity.
Understanding Wormhole
The Wormhole cross-chain protocol serves as a bridge connecting Solana and Ethereum. Through Wormhole, DeFi projects can avoid Ethereum's congestion and high fees while benefiting from Solana's low costs, high throughput, and fast transaction experience.
— Solana Wormhole Project Homepage
As the project description indicates, Wormhole connects two incompatible public chains—Ethereum and Solana—through a protocol deploying contract programs on both chains. The program running on Ethereum is called an Ethereum smart contract, while the program on Solana is called a Solana program. Both programs work together to fulfill user cross-chain asset requests. The proxy contract initialization vulnerability occurred in Wormhole's Ethereum smart contract.
Ethereum Smart Contracts Explained
Ethereum remains the most widely recognized public chain system, with its native currency Ether being the second-largest cryptocurrency by market capitalization. Ethereum smart contracts are programs running on the Ethereum Virtual Machine (EVM), primarily implementing various asset management functions. Readers new to smart contracts can conceptually understand them as Java-like programs running within a Java Virtual Machine (analogous to the EVM).
A distinctive feature of Ethereum is that once a smart contract program is deployed on the chain, its code logic becomes immutable—meaning the code logic of a running Ethereum smart contract cannot be updated.
Implementing Contract Updates Through Proxy Contracts
The immutability of Ethereum smart contracts after deployment creates challenges for developers needing to update their smart contract code upon discovering security vulnerabilities. However, complete inability to update smart contract code isn't absolute. Through a special Ethereum EVM call—Delegatecall—an Ethereum program can indirectly achieve smart contract code updates.
Ethereum smart contracts use a code-and-storage coupled programming model, meaning a contract's code logic and data storage are stored at the same address. A contract can only modify data storage at its own address using the code logic stored at that address.
But this isn't always the case. When Ethereum smart contracts interact, beyond the most common Call method, there's a special调用方式: Delegatecall, which separates smart contract code from smart contract storage. Essentially, it uses someone else's code to modify your own data storage.
- Ordinary CALL between contracts: As shown in the left diagram below, when Contract A CALLs Contract B, Contract B's program logic runs within Contract B's address data storage, reading and modifying data belonging to Address B.
- Delegatecall between contracts: As shown in the right diagram below, when Contract A delegatecalls Contract B, the running program code remains B's code, but the fundamental difference from CALL is that all code runs in A's context. When using Delegatecall, B's code modifies the caller A's data storage state.
Using Delegatecall allows running Contract B's contract code while modifying Contract A's contract storage. This feature naturally enables contract upgrades. Here, Contract A serves as the proxy contract, while Contract B serves as the implementation contract. During each contract call, the proxy contract A uses Delegatecall to pass actual call parameters to implementation contract B specified in Contract A's data storage, with B's contract code modifying Contract A's data storage.
When serious vulnerabilities are discovered in implementation contract B's code requiring upgrade, changing the implementation contract specified in Contract A's data storage from B to new B' achieves contract upgrade. The original contract B remains保存ed on the chain but is essentially abandoned, as the proxy contract no longer forwards calls to Contract B. The data originally needed for B's operation remains stored in A and can be seamlessly inherited and used by the new contract B'.
This constitutes the basic principle of implementing smart contract code upgrades. Currently, the Ethereum community has two mainstream standards for implementing smart contract upgrades through proxy contracts: Transparent Proxy Pattern (TPP) and Universal Upgradeable Proxy Standard (UUPS).
- In the Transparent Proxy Pattern (TPP), the proxy contract A implements contract upgrade-related logic. The drawbacks include high overhead and inability to call functions with the same name in proxy and implementation contracts.
- In the Universal Upgradeable Proxy Standard (UUPS), the proxy contract A is standardized by EIP-1822 and implements no logic, merely delegating all calls to the target contract via DELEGATECALL. Update logic is also implemented in the target contract.
The Wormhole protocol mentioned initially uses the second UUPS模式.
UUPS Proxy Contract Pattern
In the UUPS pattern, the proxy contract A implements no logic. All contract functions are implemented by developers in the implementation contract B, primarily including: contract initialization logic and contract update logic.
When updating the actual contract B, the administrator pre-deploys the updated contract B' and calls the upgradeToAndCall function using the new contract B's address as a parameter. This function changes the implementation contract recorded in proxy contract A's storage from B to B', then uses delegatecall to invoke the new contract B's initialization function initialize to complete initialization. The initialize function updates the administrator account in Contract A's storage.
Risks in UUPS Pattern Implementation
Let's focus on the role of the authorization variable upgrader in proxy contract A and implementation contract B: because upgradeable smart contracts split logic originally completed in one contract across two contracts (proxy contract A and actual contract B), both contracts maintain their own upgrader storage variables in their respective storage.
When the administrator completes the update through the upgradeToAndCall process, due to delegatecall's characteristics, the upgrader variable in proxy contract A is successfully updated, but the upgrader in implementation contract B' is not updated. Although B's storage isn't actual storage in the UUPS pattern, B' remains a publicly accessible ordinary contract besides being proxy contract A's implementation contract.
Actually, when using the UUPS pattern to update contracts, two separate crucial steps exist for the upgrader storage variable:
- Update the upgrader in A's storage to msg.sender (implemented in upgradeToAndCall)
- Update the upgrader in B's storage to msg.sender (additional step required by administrator)
The administrator needs not only to change the upgrader variable in A's storage (step 1) by calling the upgradeToAndCall function in the original implementation contract B through the new implementation contract B's initialize function, but also needs to additionally call the initialize function externally once to change the upgrader variable in B's storage (step 2).
Without step 2, the proxy contract A is correctly initialized, and users cannot perform any malicious actions through proxy contract A, but B' undergoes no initialization. Users can still directly call B's initialization function initialize, updating the upgrader in B's storage to themselves. By controlling B's upgrade behavior to call self-destruct操作, they can implement destruction of B' contract, making the implementation contract B' pointed to by proxy A disappear. The remaining data storage in proxy contract A will become useless.
Actual Vulnerability Situation in Wormhole
In Wormhole's source code, the actual function name responsible for implementing the UUPS standard upgradeToAndCall function is submitContractUpgrade, and authorization uses parseVM and other operations related to custom virtual machines.
In the implementation contract, initialize is responsible for initializing the administrator variable.
Wormhole had last called submitContractUpgrade() to update at block height 13818843 (December 16, 2021). Afterward, the actual contract B' remained uninitialized.
An attacker could unauthorizedly call the actual contract B's initialization function initialize() to obtain B' contract administrator permissions. Then, using the acquired administrator permissions, they could maliciously call the update function submitContractUpgrade(). The delegatecall in this update function allows executing any code specified by the attacker, with the most harmful being executing selfdestruct to make the actual contract B' self-destruct, freezing assets in the Wormhole project.
The Wormhole project team called the initialization function at height 14269474 (February 24, 2022) to fix this issue.
When using the UUPS proxy pattern, special attention must be paid to key steps. Without completing necessary initialization, hackers can obtain administrator permissions through initializing implementation contracts and maliciously call update functions to destroy implementation contracts, resulting in permanent locking of assets within proxy contracts.
Frequently Asked Questions
What makes cross-chain bridges particularly vulnerable to attacks?
Cross-chain bridges often hold significant liquidity to facilitate asset transfers between blockchains, creating attractive targets for hackers. Their complex architecture involving multiple smart contracts and sometimes centralized components increases the attack surface. Additionally, many bridges implement novel mechanisms that may contain undiscovered vulnerabilities.
How can users protect their assets when using cross-chain bridges?
Users should research bridges thoroughly before use, preferring established protocols with strong security audits and insurance coverage. They should diversify assets across multiple bridges rather than concentrating funds in one protocol, and always verify transaction details carefully. Using bridges during periods of lower congestion can also reduce risk.
What security measures should cross-chain bridge developers implement?
Developers should conduct multiple independent security audits, implement robust key management systems, and establish bug bounty programs. They should use time-locked upgrades for critical changes, incorporate emergency pause mechanisms, and ensure proper initialization of all contracts. Regular security monitoring and incident response plans are also essential.
Are there completely decentralized cross-chain bridge solutions?
While no solution is entirely decentralized, some bridges incorporate more decentralized elements than others. These might include decentralized oracle networks, validator sets with distributed control, or community-governed upgrade mechanisms. However, most practical implementations still incorporate some centralized components for efficiency and upgradability.
How does the future of cross-chain bridge security look?
The security landscape continues evolving with new approaches like zero-knowledge proofs for verification, multi-party computation for key management, and insured bridge protocols. Standardization efforts and shared security models across multiple bridges also show promise for enhancing overall ecosystem security.
What should I do if I suspect a vulnerability in a cross-chain bridge?
Immediately report your findings through the project's official bug bounty program or security contact channel. Avoid testing on mainnet without explicit permission, and never exploit vulnerabilities you discover. Responsible disclosure helps protect user funds and often provides significant rewards for identifying critical issues.
Conclusion
Cross-chain bridges introduce centralization risks into the decentralized Web3 world through their inherent centralized characteristics. Their security largely depends on the cross-chain bridge project teams themselves. Thus, cross-chain bridge developers play a decisive role in cross-chain bridge security. As the ecosystem evolves, explore more strategies for securing cross-chain assets and implementing robust blockchain infrastructure.
The continuous development of security practices and view real-time tools for monitoring bridge operations will be crucial for maintaining user trust and ensuring the long-term viability of cross-chain interoperability solutions. By learning from past incidents and implementing comprehensive security measures, the blockchain community can build more resilient bridges that safely connect diverse ecosystems.