{"id":1027,"date":"2025-04-08T14:24:03","date_gmt":"2025-04-08T12:24:03","guid":{"rendered":"https:\/\/ackee.xyz\/blog\/?p=1027"},"modified":"2025-04-10T13:50:21","modified_gmt":"2025-04-10T11:50:21","slug":"a-safe-native-solution-to-the-bybit-hack","status":"publish","type":"post","link":"https:\/\/ackee.xyz\/blog\/a-safe-native-solution-to-the-bybit-hack\/","title":{"rendered":"A Safe-native Solution to the Bybit Hack"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">In February 2025, nearly $1.5B was stolen from the Bybit exchange in what has been called the largest cryptocurrency hack in history. Paradoxically, it wasn\u2019t enabled by a smart contract vulnerability, but by social engineering. <\/span><span style=\"font-weight: 400;\">Given our <a href=\"https:\/\/ackee.xyz\/blog\/safe-contracts-1-4-0-audit-summary\/\">experience<\/a> with <\/span><span style=\"font-weight: 400;\">auditing Safe&#8217;s smart contracts,<\/span><span style=\"font-weight: 400;\"> which Bybit uses, we decided to investigate the breach in more detail.<\/span><\/p>\n<p><b>TLDR: Projects using Safe Wallets \u2013 especially those managing large funds \u2013 need to actively configure built-in security features like Safe Guards and timelocks. These features exist for a reason.<\/b><\/p>\n<h2><span style=\"font-weight: 400;\">What happened?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Here&#8217;s how the incident unfolded (timeline by <a href=\"https:\/\/newsletter.blockthreat.io\/p\/blockthreat-week-8-2025\">BlockThreat<\/a>):<\/span><\/p>\n<ul>\n<li><span style=\"font-weight: 400;\">The attacker first compromised the development machine of a single Safe developer. This granted access to an <\/span><a href=\"https:\/\/x.com\/safe\/status\/1897663514975649938\"><span style=\"font-weight: 400;\">AWS session key<\/span><\/a>,<span style=\"font-weight: 400;\"> though initially it didn\u2019t allow changes to the frontend.<\/span><\/li>\n<li><span style=\"font-weight: 400;\">Over the span of two weeks, the attacker mimicked this developer&#8217;s online activity patterns and probing for weaknesses in AWS security.<\/span><\/li>\n<li><span style=\"font-weight: 400;\"> Using a time-limited AWS key and 2FA confirmation (using the compromised developer\u2019s credentials), the attacker was eventually able to deploy malicious code to the Safe frontend.<\/span><\/li>\n<li><span style=\"font-weight: 400;\">The attacker injected malicious frontend code that generated a targeted signature request specifically crafted for the Bybit account.<\/span><\/li>\n<li><span style=\"font-weight: 400;\">It&#8217;s likely that the attacker used social engineering to identify that Bybit signers didn\u2019t properly verify transactions on their hardware wallets. This allowed malicious signature requests to slip through.<\/span><\/li>\n<li><span style=\"font-weight: 400;\">The final step required signatures from three Bybit cold storage signers. Through the compromised Safe frontend, they were likely shown a benign-looking transaction \u2013 but in reality, it executed a contract upgrade using <\/span><span style=\"font-weight: 400;\"><code class=\"codehl\">delegatecall<\/code><\/span><span style=\"font-weight: 400;\">, swapping in a malicious implementation.<\/span><\/li>\n<li><span style=\"font-weight: 400;\"> With control of the vault, the attacker drained all assets. The address and related transactions can be <\/span><a href=\"https:\/\/etherscan.io\/address\/0x0fa09c3a328792253f8dee7116848723b72a6d2e\"><span style=\"font-weight: 400;\">viewed on Etherscan<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/li>\n<\/ul>\n<h2><span style=\"font-weight: 400;\">How could have it been prevented?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Let&#8217;s take a closer look at the security features Safe offers in mitigating smart contract security risks.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The most critical issue to the hack was <\/span><b>blind-signing<\/b><span style=\"font-weight: 400;\">, a longstanding problem in the ecosystem. Cold wallets often have poor UX for reviewing transactions, making it easy for signers to approve malicious payloads during routine operations without validating what they&#8217;re actually signing.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Thankfully, there are tools designed to address this. One example is the <\/span><a href=\"https:\/\/github.com\/pcaversaccio\/safe-tx-hashes-util\"><span style=\"font-weight: 400;\">Safe script validator<\/span><\/a><span style=\"font-weight: 400;\">, originally built by<\/span><a href=\"https:\/\/github.com\/pcaversaccio\"> <span style=\"font-weight: 400;\">@pcaversaccio<\/span><\/a><span style=\"font-weight: 400;\"> and now hosted by <\/span><a href=\"https:\/\/safeutils.openzeppelin.com\/\"><span style=\"font-weight: 400;\">OpenZeppelin<\/span><\/a><span style=\"font-weight: 400;\">. This tool allows signers to verify the payload being signed, byte-by-byte, against the expected Safe script before confirming it on a hardware wallet.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Beyond user-level tooling, there&#8217;s also room to improve <\/span><b>multisig thresholds<\/b><span style=\"font-weight: 400;\"> and introduce automatic payload validation at the <\/span><b>signing machine level<\/b><span style=\"font-weight: 400;\">, reducing the risk of human error.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We should also look <\/span>beyond Web2-style defenses<span style=\"font-weight: 400;\">. Safe offers <strong>Safe Guards<\/strong>, built-in <\/span><b><\/b><span style=\"font-weight: 400;\">on-chain security protocols which, if properly configured, could have outright prevented the loss of Bybit&#8217;s funds. Despite being available, they\u2019re often left unused or misunderstood. That needs to change.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Hardening multisig with Safe Guards<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Safe Wallet can be natively extended by a <strong>Safe Module<\/strong> or <strong>Safe Guard<\/strong>. Modules allow arbitrary conditions for execution from Safe (based on the Module logic), and multiple Modules can be defined for one wallet. There is always one Guard, and it can only block transactions. We have already described security best practices around Safe in a <\/span><a href=\"https:\/\/ackee.xyz\/blog\/staying-safe-with-safe\/\"><span style=\"font-weight: 400;\">recent blog post<\/span><\/a><span style=\"font-weight: 400;\"> and also <\/span><a href=\"https:\/\/www.youtube.com\/watch?v=PiDOJU-0nLw&amp;list=PL0knnt70iEZry_pACXB4sKGSuZz5XNoOd&amp;index=9\"><span style=\"font-weight: 400;\">discussed it at SafeCon 2023<\/span><\/a><span style=\"font-weight: 400;\"> in Berlin. Let\u2019s look at how Guards can help us secure our wallets.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">As <a href=\"https:\/\/docs.safe.global\/advanced\/smart-account-guards\" target=\"_blank\" rel=\"noopener\">stated<\/a> in the official documentation: \u201cSafe Guards are used when there are restrictions on top of the n-out-of-m scheme\u201d, letting us <\/span><span style=\"font-weight: 400;\">limit certain operations on-chain. Safe Guards provide us, by design, pre-checks and post-checks, and maintain their own state. A great example of Safe Guard is <strong>ScopeGuard<\/strong>:<\/span><\/p>\n<pre><code class=\"language-solidity\">function checkTransaction(\n        address to,\n        uint256 value,\n        bytes memory data,\n        Enum.Operation operation,\n        uint256,\n        uint256,\n        uint256,\n        address,\n        \/\/ solhint-disallow-next-line no-unused-vars\n        address payable,\n        bytes memory,\n        address\n    ) external view override {\n        require(\n            operation != Enum.Operation.DelegateCall ||\n                allowedTargets[to].delegateCallAllowed,\n            &quot;Delegate call not allowed to this address&quot;\n        );\n        require(allowedTargets[to].allowed, &quot;Target address is not allowed&quot;);\n        if (value &gt; 0) {\n            require(\n                allowedTargets[to].valueAllowed,\n                &quot;Cannot send ETH to this target&quot;\n            );\n        }\n        if (data.length &gt;= 4) {\n            require(\n                !allowedTargets[to].scoped ||\n                    allowedTargets[to].allowedFunctions[bytes4(data)],\n                &quot;Target function is not allowed&quot;\n            );\n        } else {\n            require(data.length == 0, &quot;Function signature too short&quot;);\n            require(\n                !allowedTargets[to].scoped ||\n                    allowedTargets[to].fallbackAllowed,\n                &quot;Fallback not allowed for this address&quot;\n            );\n        }\n    }<\/code><\/pre>\n<p><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">This guard is already well-established and used by projects such as Immunefi, where we have<\/span> <a href=\"https:\/\/github.com\/immunefi-team\/vaults\/blob\/main\/audits\/ackee-blockchain-immunefi-vault-final-report.pdf\"><span style=\"font-weight: 400;\">audited this guard<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">However, the guards can be more complex. They can also implement the <code class=\"codehl\">checkAfterExecution<\/code> function, or check the signatures and other values provided by the interface. This makes it possible to build <strong>invariants that not only check passed arguments<\/strong>, but also <strong>check if the state transition after the transaction was allowed and correct<\/strong>.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Another good example is a Safe Guard in the Mixin protocol, which <\/span><a href=\"https:\/\/github.com\/MixinNetwork\/audits\/blob\/main\/mixin-safe-ethereum-ackee.pdf\"><span style=\"font-weight: 400;\">we have also audited<\/span><\/a><span style=\"font-weight: 400;\">. It accesses the aggregated signatures and recovers the signers. <\/span><span style=\"font-weight: 400;\">If there&#8217;s a specific address in the aggregated signature, and it matches the one saved in the state of the guard<\/span><span style=\"font-weight: 400;\">, then it allows you to execute the transaction after a certain timelock.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This approach can be critically helpful when managing huge portfolios in a multisig. Transaction delay with monitoring infrastructure help react to potential security incidents. But more importantly, the discussed target scoping can completely prevent unwanted execution.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Case studies<\/span><\/h2>\n<p><em><span style=\"font-weight: 400;\">&#8220;I\u2019m not sure if I want to give up flexibility, but I certainly don&#8217;t ever call delegatecalls with the multisig.&#8221;<\/span><\/em><\/p>\n<p><strong>Start using a guard that prevents delegate calls. Make sure the guard is audited.<\/strong><\/p>\n<p><em><span style=\"font-weight: 400;\">&#8220;I have a list of addresses I need to call, otherwise, I don\u2019t need to call any others.&#8221;<\/span><\/em><\/p>\n<p><span style=\"font-weight: 400;\"><strong>Start using ScopeGuard<\/strong>. Be aware that this guard is permissioned, so take appropriate security measures on the guard as well (a malicious guard implementation can block Safe transactions). When the parameters in the guard are settled, in some cases it is possible to renounce ownership of the guard to prevent any changes in the guard\u2019s behavior.<\/span><\/p>\n<p><em><span style=\"font-weight: 400;\">&#8220;I have special requirements, such as different multisig thresholds for different actions or invariant checking.&#8221;<\/span><\/em><\/p>\n<p><strong>Implement your own Safe Guard and get it audited.<\/strong><\/p>\n<h2><span style=\"font-weight: 400;\">Summary<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Relying solely on off-chain security practices is not enough. Embedding protective constraints directly into blockchain protocols can offer a far more robust defense against sophisticated attacks.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Safe\u2019s modular and flexible architecture is intentional by placing responsibility on integrators to configure it securely, allowing only the operations that are absolutely necessary. By following the principle of least privilege and minimizing unnecessary functionality, projects can significantly reduce their attack surface and improve overall security.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This particular exploit could have been prevented had the available security features been properly understood and configured to match the specific needs of the project. <\/span><span style=\"font-weight: 400;\">While Safe Guards are a powerful native solution, they aren\u2019t the only one. <\/span><a href=\"https:\/\/docs.safe.global\/advanced\/smart-account-modules\"><span style=\"font-weight: 400;\">Safe Modules<\/span><\/a><span style=\"font-weight: 400;\"> offer even greater control and customization \u2013 and with that greater power comes greater complexity.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In February 2025, nearly $1.5B was stolen from the Bybit exchange in what has been called the largest cryptocurrency hack in history. Paradoxically, it wasn\u2019t enabled by a smart contract vulnerability, but by social engineering. Given our experience with auditing Safe&#8217;s smart contracts, which Bybit uses, we decided to investigate the breach in more detail. TLDR: Projects using Safe Wallets \u2013 especially&hellip;<\/p>\n","protected":false},"author":30,"featured_media":1029,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[61,10,84],"tags":[150,24,14,101],"class_list":["post-1027","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-education","category-ethereum","category-hacks","tag-bybit","tag-ethereum","tag-exploit","tag-safe"],"aioseo_notices":[],"featured_image_src":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/04\/safe-blog-600x400.png","featured_image_src_square":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2025\/04\/safe-blog-600x600.png","author_info":{"display_name":"Tom\u00e1\u0161 Kova\u0159\u00edk","author_link":"https:\/\/ackee.xyz\/blog\/author\/tomas-kovarik\/"},"_links":{"self":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/1027","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\/30"}],"replies":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/comments?post=1027"}],"version-history":[{"count":0,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/1027\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media\/1029"}],"wp:attachment":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media?parent=1027"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/categories?post=1027"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/tags?post=1027"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}