{"id":444,"date":"2023-01-30T11:01:20","date_gmt":"2023-01-30T09:01:20","guid":{"rendered":"https:\/\/ackeeblockchain.com\/blog\/?p=444"},"modified":"2023-01-31T14:50:15","modified_gmt":"2023-01-31T12:50:15","slug":"2022-solana-hacks-explained-wormhole","status":"publish","type":"post","link":"https:\/\/ackee.xyz\/blog\/2022-solana-hacks-explained-wormhole\/","title":{"rendered":"2022 Solana Hacks Explained: Wormhole"},"content":{"rendered":"<p><span style=\"font-weight: 400;\"><a href=\"https:\/\/wormhole.com\/\"><strong>Wormhole<\/strong><\/a> is a <strong>message-passing protocol<\/strong> enabling the<strong> transfer of tokenized assets<\/strong> between <strong>supported chains<\/strong>. In other words, it allows one to send ETH from Ethereum and receive a wrapped wormhole ETH (wETH) on Solana. Thanks to that, developers can also create <strong>cross-chain dApps<\/strong>.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The basic principle of operation is that <strong>each<\/strong> supported <strong>chain<\/strong> has a <strong>wormhole<\/strong> <strong>contract<\/strong> that emits or processes wormhole messages. The system relies on so-called &#8216;<strong>Guardians<\/strong>&#8216;, a distributed set of nodes that monitor the state of transactions and after consensus sign the messages emitted by the wormhole contracts. When a user sends ETH to Solana, the system <strong>locks<\/strong> the <strong>tokens<\/strong> <strong>into a contract<\/strong> on the source chain (Ethereum) and when the Guardian nodes approve that it happened, they <strong>sign<\/strong> the message, and another contract on the destination chain is called to <strong>issue the parallel &#8216;wrapped&#8217; token<\/strong>.<\/span><\/p>\n<p><strong>What happened<\/strong><\/p>\n<p><span style=\"font-weight: 400;\">In February 2022, an attacker <strong>stole<\/strong> assets worth roughly <strong>$338M<\/strong> (at the time of the exploit) by <strong>circumventing<\/strong> the <strong>signing<\/strong> <strong>process<\/strong> and minting 120k wrapped ETH tokens on Solana without locking an equivalent on Ethereum. Even though the Wormhole team sent a <a href=\"https:\/\/www.notifi.xyz\/messages\/1497\">message<\/a> to the hacker offering<strong> a white hat agreement<\/strong>, there was no response. The hacker used 93,750 of the minted wrapped ETH to redeem back to equivalent ETH tokens on Ethereum, thus <strong>draining<\/strong> the money from <strong>Ethereum liquidity pool<\/strong>. The rest of the tokens were sold for SOL and USDC, as seen in the hacker&#8217;s <a href=\"https:\/\/solscan.io\/account\/CxegPrfn2ge5dNiQberUrQJkHCcimeR4VXkeawcFBBka#splTransfers\">wallet<\/a>. Wormhole&#8217;s investor, <strong>Jump<\/strong> <strong>Crypto<\/strong> replenished all <strong>120k<\/strong> <strong>ETH<\/strong>, virtually <strong>bailing<\/strong> <strong>Wormhole<\/strong> <strong>out<\/strong>.<\/span><\/p>\n<p><b>Exploit Details<\/b><\/p>\n<p><span style=\"font-weight: 400;\">The Wormhole project <strong>is open-source,<\/strong> and the <strong>public <a href=\"https:\/\/github.com\/wormhole-foundation\/wormhole\">repository<\/a> <\/strong>is accessible to anyone. Interestingly enough, the hack happened only a few hours after <strong>a critical bug was <a href=\"https:\/\/github.com\/certusone\/wormhole\/commit\/7edbbd3677ee6ca681be8722a607bc576a3912c8#diff-0d27d8889edd071b86d3f3299276882d97613ad6ab3b0b6412ae4ebf3ccd6370R92-R103\">fixed<\/a><\/strong> and committed to the public repository and <strong>before a new version<\/strong> of the program was deployed.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The bug&#8217;s root cause was <a href=\"https:\/\/github.com\/solana-labs\/solana\/blob\/master\/sdk\/program\/src\/sysvar\/instructions.rs#L240\"><strong>a deprecated function<\/strong><\/a> <\/span><\/p>\n<pre><span style=\"font-weight: 400;\">load_instruction_at <\/span><\/pre>\n<p>being used during the Wormhole <strong style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;\">signature verification<\/strong><span style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;\">. The way to build custom instructions that &#8216;do&#8217; signature <strong>verification<\/strong> is by sending a transaction made of (at least) <strong>two instructions<\/strong> and <strong>checking<\/strong> that the native program instruction was sent. As described in the <a href=\"https:\/\/github.com\/solana-labs\/solana\/blob\/master\/sdk\/src\/secp256k1_instruction.rs\"><em><strong>secp256k1_instruction.rs<\/strong><\/em><\/a> documentation, it is necessary to:<\/span><\/p>\n<ul>\n<li><span style=\"font-weight: 400;\">Load the secp256k1 instruction data <\/span>with<\/li>\n<\/ul>\n<pre>load_instruction_at_checked <strong>or<\/strong> get_instruction_relative;<\/pre>\n<ul>\n<li>Check that the<\/li>\n<\/ul>\n<pre>secp256k1<\/pre>\n<p>program ID is equal to<\/p>\n<pre>secp256k1_program::ID<\/pre>\n<p><strong>so that the signature verification cannot be faked<\/strong> with a malicious program;<\/p>\n<ul>\n<li><strong>Check<\/strong> that the <strong>public keys and messages<\/strong> are the expected values per the program&#8217;s requirements.<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Unfortunately, Wormhole&#8217;s <strong>implementation of the signature verification did not use<\/strong> the suggested<\/span><\/p>\n<pre><span style=\"font-weight: 400;\">load_instruction_at_checked <\/span><\/pre>\n<p><span style=\"font-weight: 400;\">function and instead <strong>used <a href=\"https:\/\/github.com\/solana-labs\/solana\/blob\/master\/sdk\/program\/src\/sysvar\/instructions.rs#L240\">a deprecated version<\/a><\/strong> <\/span><\/p>\n<pre><span style=\"font-weight: 400;\">load_instruction_at. <\/span><\/pre>\n<p><span style=\"font-weight: 400;\">This function <strong>does not check that the origin of data<\/strong> to deserialize is actually from the sysvar account and not a fake account. That way, the attacker was able to:<\/span><\/p>\n<ul>\n<li><span style=\"font-weight: 400;\">Create an account <em><strong>2tHS1cXX2h1KBEaadprqELJ6sV9wLoaSdX68FqsrrZRd<\/strong><\/em> with <\/span><\/li>\n<\/ul>\n<pre><span style=\"font-weight: 400;\">secp256k1 <\/span><\/pre>\n<p><span style=\"font-weight: 400;\">instruction data earlier within different transaction;<\/span><\/p>\n<ul>\n<li>Call<\/li>\n<\/ul>\n<pre>verify_certificate<\/pre>\n<p>where he replaced the sysvar::instructions account with the malicious account created in the first step.<\/p>\n<p><span style=\"font-weight: 400;\">Signature verification normally requires <strong>two<\/strong> instructions within one transaction: <\/span><\/p>\n<pre><span style=\"font-weight: 400;\">secp256k1 <strong>and<\/strong> verify_certificate. <\/span><\/pre>\n<p><span style=\"font-weight: 400;\">However, the attacker managed to call only <strong>the single<\/strong> <\/span><\/p>\n<pre><span style=\"font-weight: 400;\">verify_certificates <\/span><\/pre>\n<p><span style=\"font-weight: 400;\">instruction and fake the instruction input data, so the <\/span><\/p>\n<pre><span style=\"font-weight: 400;\">verify_certificates<\/span><\/pre>\n<p><span style=\"font-weight: 400;\"><strong>did not recognize<\/strong> it was not called together with the <\/span><\/p>\n<pre><span style=\"font-weight: 400;\">secp256k1 <\/span><\/pre>\n<p><span style=\"font-weight: 400;\">instruction and <strong>passed the verification.<\/strong> You can check <a href=\"https:\/\/solscan.io\/tx\/25Zu1L2Q9uk998d5GMnX43t9u9eVBKvbVtgHndkc2GmUFed8Pu73LGW6hiDsmGXHykKUTLkvUdh4yXPdL3Jo4wVS\">the transaction<\/a> yourself. Once the <strong>verification was complete<\/strong>, it was possible to <strong>call the usual instruction<\/strong> <\/span><\/p>\n<pre><span style=\"font-weight: 400;\">post_vaa<\/span><\/pre>\n<p><span style=\"font-weight: 400;\">to <strong>obtain<\/strong> the so-called &#8216;<strong>validator action approval&#8217;<\/strong> and subsequently complete_wrapped instruction to <strong>mint the 120,000 ETH.<\/strong><\/span><\/p>\n<p><b>In simple words, <\/b><span style=\"font-weight: 400;\">the Wormhole hack was quite sophisticated on one side, but after all, <strong>the root cause<\/strong> was a <strong>missing<\/strong> <strong>ownership<\/strong> <strong>check<\/strong> on one account, which allowed the hacker to <strong>fake<\/strong> that he had money on his account on one blockchain. And due to the <strong>lack of checks<\/strong>, he managed to mint the money on another blockchain.\u00a0<\/span><\/p>\n<p><a href=\"https:\/\/www.linkedin.com\/pulse\/320-million-wormhole-hack-explained-giap-nguyen\/\"><strong>Reference<\/strong><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wormhole is a message-passing protocol enabling the transfer of tokenized assets between supported chains. In other words, it allows one to send ETH from Ethereum and receive a wrapped wormhole ETH (wETH) on Solana. Thanks to that, developers can also create cross-chain dApps. The basic principle of operation is that each supported chain has a wormhole contract that emits or processes wormhole&hellip;<\/p>\n","protected":false},"author":15,"featured_media":445,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[84,5],"tags":[86,28,6],"class_list":["post-444","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hacks","category-solana","tag-hack","tag-smart-contract","tag-solana"],"aioseo_notices":[],"featured_image_src":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2023\/01\/Wormhole-600x400.png","featured_image_src_square":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2023\/01\/Wormhole-600x600.png","author_info":{"display_name":"Aleksandra Yudina","author_link":"https:\/\/ackee.xyz\/blog\/author\/aleksandra-yudina\/"},"_links":{"self":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/444","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/comments?post=444"}],"version-history":[{"count":0,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/444\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media\/445"}],"wp:attachment":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media?parent=444"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/categories?post=444"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/tags?post=444"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}