
{"id":71316,"date":"2025-06-03T16:14:38","date_gmt":"2025-06-03T16:14:38","guid":{"rendered":"https:\/\/mycryptomania.com\/?p=71316"},"modified":"2025-06-03T16:14:38","modified_gmt":"2025-06-03T16:14:38","slug":"cetus-hack-post-mortem-of-a-223m-heist","status":"publish","type":"post","link":"https:\/\/mycryptomania.com\/?p=71316","title":{"rendered":"Cetus Hack\u200a\u2014\u200aPost-Mortem of a $223M Heist"},"content":{"rendered":"<h3>Cetus Hack\u200a\u2014\u200aPost-Mortem of a $223M\u00a0Heist<\/h3>\n<h4>$223 million was stolen in what might be one of the simplest hacks the crypto space has\u00a0seen.<\/h4>\n<p>All the attacker needed to do was come knocking at the door with a high liquidity position, and they were handed the entire Cetus treasury.<\/p>\n<p>While Cetus labeled the attack a \u201csophisticated smart contract exploit,\u201d in truth, the exploit was incredibly simple both in technique and execution.<\/p>\n<p>It earned the attacker the title of the second-largest exploit of the year, and the ninth-largest in crypto\u00a0history.<\/p>\n<p>Here\u2019s how they did\u00a0it.<\/p>\n<h3>A Flash Loan, a High L<strong>iquidity Position and a Left Shift Do The\u00a0Trick<\/strong><\/h3>\n<p>The attacker kicked off the hack by borrowing 10 million haSUI tokens through a flash\u00a0swap.<\/p>\n<p>They opened multiple massive liquidity positions during the attack, all within insanely narrow tick ranges\u200a\u2014\u200aone example being [300000, 300200], <a href=\"https:\/\/dedaub.com\/blog\/the-cetus-amm-200m-hack-how-a-flawed-overflow-check-led-to-catastrophic-loss\/\">as spotted by\u00a0Dedaub<\/a>.<\/p>\n<p>Cetus is a type of AMM that uses <strong>concentrated liquidity<\/strong>, meaning liquidity providers pick specific price ranges (ticks) where their funds are active. Unlike traditional AMMs that spread liquidity evenly across all prices, Cetus lets you focus your liquidity on tight price\u00a0windows.<\/p>\n<p>Because of that, the tick range really\u00a0matters.<\/p>\n<p>Narrow ticks mean liquidity is squeezed into a tiny price range, which makes the <strong>liquidity parameter in the calculations that follow much bigger<\/strong>\u200a\u2014\u200akeep that in\u00a0mind.<\/p>\n<p><em>The first step taken by a protocol after you open a position is calculating how much token A you need for the liquidity amount you want to\u00a0provide<\/em>.<\/p>\n<p>And this is where things went sideways.<\/p>\n<p>Because of the huge liquidity provided through the flash loan combined with that super narrow tick range\u200a\u2014\u200aa wider range would\u2019ve kept numbers smaller and safer\u200a\u2014\u200a<strong>the new liquidity position basically broke Cetus\u2019s math\u00a0engine<\/strong>.<\/p>\n<p>Here\u2019s how.<\/p>\n<p>The get_delta_a function within clmm_math.move c<strong>alculates the amount of token A necessary<\/strong>, which is commonly used in AMMs to compute token amounts from liquidity.<\/p>\n<p>The first calculation\u00a0: liquidity \u00d7 delta_sqrt_price produced a very large number,<strong> which is the raw\u00a0product.<\/strong><\/p>\n<p>In AMMs like Cetus or Uniswap v3, prices and liquidity are often represented as <strong>fixed-point numbers<\/strong> instead of simple integers or\u00a0floats.<\/p>\n<p>A fixed-point number stores decimals by representing them as large integers shifted by some number of\u00a0bits.<\/p>\n<p>Multiplying liquidity and price differences would lose decimal precision, so an intermediate calculation is needed, called <strong>left\u00a0shift<\/strong>.<\/p>\n<p>This left shift (usually by 64 bits or some other amount) scales up the number, effectively adding extra decimal places in integer form so that when you later divide by another large number, you keep accuracy without losing fractional parts.<\/p>\n<p><strong>Shifting left by 64 bits<\/strong> is equivalent to multiplying by 2\u2076\u2074(&lt;&lt; 64), to retain fixed-point precision.<\/p>\n<p>Left shift is calculation is then: shifted = raw product\u00a0&lt;&lt;64<\/p>\n<p><strong><em>That\u2019s where everything derails.<\/em><\/strong><\/p>\n<p>The Cetus AMM runs on a blockchain environment called the Move virtual machine, which uses fixed-size integers\u200a\u2014\u200aspecifically 256-bit integers\u200a\u2014\u200ato store numbers, which can represent values from 0 up to 2\u00b2\u2075\u2076\u200a\u2014\u200a1 (about 1.16 \u00d7\u00a010\u2077\u2077).<\/p>\n<p>If numbers go beyond this threshold, they are too big to be stored in the fixed amount of memory (bits) that the system has reserved for\u00a0them.<\/p>\n<p>Operations like addition, multiplication, subtraction, and division abort the program if they overflow or underflow, preventing errors.<\/p>\n<p><strong>However, left shift operations (&lt;&lt;) are special\u200a\u2014\u200athey do not abort on overflow.<\/strong><\/p>\n<p>Instead, if the shifted value exceeds the 256-bit storage limit, the excess bits (<em>Most Significant Bits (MSB) or overflow) <\/em>are silently dropped (truncated), which can cause incorrect values without triggering errors.<\/p>\n<p>This phenomenom is classically named MSB truncation.<\/p>\n<p>Due to the extremely large raw product created during the hack, when the left shift took place, it produced a number that went beyond the maximum 256-bit\u00a0number.<\/p>\n<p><strong>As the figure didn\u2019t fit, it wrapped around modulo\u00a02\u00b2\u2075\u2076.<\/strong><\/p>\n<p><em>Modulo 2\u00b2\u2075\u2076 means the system keeps only the lowest 256 bits, cutting off the highest bits as if they don\u2019t\u00a0exist.<\/em><\/p>\n<p>This causes values larger than 2\u00b2\u2075\u2076 to become much smaller in\u00a0storage.<\/p>\n<p>In the Cetus case, the stored value became very close to zero due to this wraparound\u200a\u2014\u200awhich we will see in a\u00a0minute.<\/p>\n<p>Normally, as the overflow issue is collectively known, they are safeguards to stop MSB truncation, but in the Cetus hack, the safeguard failed\u00a0them.<\/p>\n<h3><strong>Exploiting The Overflow Check Vulnerability<\/strong><\/h3>\n<p>The true root cause of the issue was a flawed overflow check inside the checked_shlw function, which was supposed to ensure the number was small enough to be safely left-shifted by 64 bits without overflowing.<\/p>\n<p><a href=\"https:\/\/cetusprotocol.notion.site\/Cetus-Incident-Report-May-22-2025-Attack-Disclosure-1ff1dbf3ac8680d7a98de6158597d416\">According to Cetus<\/a>, the contract uses a helper function from an open-source math library called <em>integer-mate<\/em>. This function was meant to verify that the input value wouldn\u2019t cause an overflow when left-shifted.<\/p>\n<p><strong><em>Oh well\u200a\u2014\u200athe \u201chelper function\u201d didn\u2019t help much. In fact, it did the opposite.<\/em><\/strong><\/p>\n<p>Here\u2019s where it broke down: Shifting a number left by 64 bits is like multiplying it by 2\u2076\u2074 as we have just seen. So, to keep the result within the 256-bit limit, <strong>the input must be less than\u00a02\u00b9\u2079\u00b2<\/strong>.<\/p>\n<p>That\u2019s the real safety threshold.<\/p>\n<p>But instead of directly checking whether the value was less than 2\u00b9\u2079\u00b2, the contract tried <strong>a masking trick<\/strong>\u200a\u2014\u200aa quick way to detect if any of the higher bits beyond 2\u00b9\u2079\u00b2 were set (meaning those bits have a value of 1, or are \u201cturned on\u201d)\u200a\u2014\u200ausing mask = 0xffffffffffffffff &lt;&lt; 192 to check for overflow.<\/p>\n<p>However, this mask did not work as intended.<\/p>\n<p>Because 0xffffffffffffffff is only 64 bits wide, shifting it left by 192 bits inside a 256-bit space pushes all those \u201con\u201d bits out of the allowed memory area, turning the mask into zero (no bits\u00a0set).<\/p>\n<p>Let\u2019s simplify\u00a0this.<\/p>\n<p>0xffffffffffffffff in hexadecimal is a <strong>64-bit number<\/strong> with all 64 bits set to 1 (that\u2019s 64 ones in\u00a0binary).<\/p>\n<p>Shifting left by some number of bits means moving all the bits in the number to the left by that many positions, and filling in the empty positions on the right with\u00a0zeros.<\/p>\n<p>If you have: 0001 and shift left by 2 bits, you get:\u00a00100.<\/p>\n<p>What does it mean to shift a 64-bit number left by 192\u00a0bits?<\/p>\n<p>If the number is stored in only <strong>64 bits of memory<\/strong>, and you try to shift it left by 192 bits, you are trying to move all its bits <strong>far beyond the size of its\u00a0memory<\/strong>.<\/p>\n<p>Because the memory only has space for 64 bits, pushing the bits left by 192 positions means all the bits get pushed <strong>outside<\/strong> of the 64-bit\u00a0space.<\/p>\n<p>So the number becomes: 00000000\u2026000 (all zeros) or simply\u00a0<strong>zero<\/strong>.<\/p>\n<p>Because the calculation results in zero, the mask detects nothing\u200a\u2014\u200ait\u2019s essentially all zeros after shifting.<\/p>\n<p>As a result, the mask is effectively nonexistent, and the code using it is unable to catch any bits or detect any overflow\u00a0risk.<\/p>\n<p><strong><em>TL;DR: There was no overflow safeguard.<\/em><\/strong><\/p>\n<p>If the mask is zero (meaning all bits are 0), then when you apply it to any number, the result will always be\u00a0zero.<\/p>\n<p>That means <strong>no bits get detected<\/strong> as set, regardless of how big the number\u00a0is.<\/p>\n<p>So, since the mask ended up as zero after shifting, it was completely ineffective at detecting whether any of the high bits (above 2\u00b9\u2079\u00b2) were\u00a0set.<\/p>\n<p>So the final calculation to determine the token <em>A <\/em>necessary was triggered, rather than\u00a0stopped.<\/p>\n<p><strong>Due to truncation,<\/strong> the numerator of the calculation became very small (close to\u00a00).<\/p>\n<p>numerator = checked_shlw(liquidity * sqrt_price_diff)<\/p>\n<p>When divided by the denominator, the quotient is also close to 0, and the result ends up\u00a0being:<\/p>\n<p>liquidity_token_required \u2248\u00a01<\/p>\n<p>This means that the contract mistakenly assumed only one token was required from the attacker in exchange for minting a massive liquidity position\u00a0worth<\/p>\n<p>Source: <a href=\"https:\/\/cetusprotocol.notion.site\/Cetus-Incident-Report-May-22-2025-Attack-Disclosure-1ff1dbf3ac8680d7a98de6158597d416\">Cetus<\/a><\/p>\n<p><a href=\"https:\/\/dedaub.com\/blog\/the-cetus-amm-200m-hack-how-a-flawed-overflow-check-led-to-catastrophic-loss\/\">According to Dedaub<\/a>, the numeric values involved in the attack \u201care precisely calculated\u200a\u2014\u200athe attacker utilized some existing functions in the contract to compute these, notably get_liquidity_from_a.\u201d<\/p>\n<p>The attacker exploited this by carefully crafting inputs (using internal helper functions) that pushed the intermediate values just over the unsafe threshold, triggering the silent overflow and minting disproportionate liquidity for minimal token\u00a0input.<\/p>\n<p><strong><em>It was an exploit that was both outstandingly simple and executed with surgical precision.<\/em><\/strong><\/p>\n<p>The attacker repeated the same process multiple times, crafting slightly different values to avoid suspicion and maximize profit before anyone noticed. They repaid their flash swap and then walked out the door with $223\u00a0million.<\/p>\n<p>Almost.<\/p>\n<h3>Overflow Check Vulnerability\u200a\u2014\u200aA Long Standing Issue for\u00a0Cetus?<\/h3>\n<p>Dedaub reviewed the multiple audits Cetus underwent and discovered that \u2018an eerily similar overflow vulnerability\u2019 had been flagged back in early 2023 during an audit of Cetus\u2019s codebase on\u00a0Aptos.<\/p>\n<p>For reasons unknown, when the protocol was later deployed on Sui, it appears little effort was made to assess whether that same vulnerability might still\u00a0apply.<\/p>\n<p>The team may have felt secure relying on what they assumed was a standard masking technique used by others, and didn\u2019t consider re-evaluating the underlying calculation logic\u200a\u2014\u200aespecially at such a critical\u00a0point.<\/p>\n<p>Apparently, OtterSec, MoveBit, and, more recently, Zellic audited the code but didn\u2019t catch the issue. Cetus\u2019s post-mortem also suggests that they overlooked it.<\/p>\n<p>As for Dedaub, the root cause may be as simple as the library handling numerical calculations being out of scope for the\u00a0audits.<\/p>\n<p>Unlike Aptos, the Sui blockchain natively supports 256-bit operations, which could have contributed to the oversight.<\/p>\n<h3>The Aftermath &amp; Decentralization Critics<\/h3>\n<p>The crypto community has been divided in the immediate aftermath of the attack. After the theft, the attacker swapped $60 million into ETH. But before they could secure the rest of their bounty, Sui validators\u200a\u2014\u200acoordinated by the Sui Foundation\u200a\u2014\u200avoted to refuse transactions signed by the attacker\u2019s addresses.<\/p>\n<p>Once the vote crossed the 33% stake threshold, the remaining $163 million from the exploit was effectively frozen.<\/p>\n<p>A few days ago, the Sui community approved the unfreezing of the tokens locked during the Cetus hack, in hopes of a swift repayment of user losses.<br \/>It\u2019s abundantly clear that the Cetus community applauded the asset freezing with all their\u00a0being.<\/p>\n<p>Meanwhile, it drew abundant criticism from much of the broader crypto\u00a0space.<\/p>\n<p>The move was painted as anti-decentralization. Freezing those assets was seen by some as a betrayal of one of DeFi\u2019s core values: censorship-resistance.<\/p>\n<p>On the decentralization front, at least, it\u2019s somewhat questionable\u200a\u2014\u200abecause it wasn\u2019t a centralized power of three deciding alone to freeze the assets, but a consortium of validators that reached consensus by surpassing the 33% stake threshold.<\/p>\n<p>To what degree those validators feel free to act independently, especially while being prompted to vote by the Sui Foundation, well, that\u2019s a question only they can\u00a0answer.<\/p>\n<p>As for the censorship criticism, the act speaks for itself: you can\u2019t get more censorious on a blockchain than by freezing\u00a0assets.<\/p>\n<p><strong><em>The debate, as always, rages on\u200a\u2014\u200ashould we, or should we not, bend the core values of blockchain when malicious actors exploit\u00a0them?<\/em><\/strong><\/p>\n<p>For purists, \u201ccode is law,\u201d and decentralization and censorship-resistance are absolute, no matter the circumstances.<\/p>\n<p>For others, it\u2019s acceptable to bend or even crack those principles if it means safeguarding people\u2019s\u00a0funds.<\/p>\n<p>Meanwhile, the attacker remains at large, and Cetus\u2019s promise of a 10% cut of the $60 million has yet to convince them to come out of the shadows and put on the white\u00a0hat.<\/p>\n<h3>About us<\/h3>\n<p><a href=\"https:\/\/nefture.com\/\"><em>Nefture<\/em><\/a><em> is a <\/em><strong><em>Web3 real-time security and risk prevention platform<\/em><\/strong><em> that detects on-chain vulnerabilities and protects digital assets, protocols and asset managers from significant losses or\u00a0threats.<\/em><em>Nefture core services includes <\/em><strong><em>Real-Time Transaction Security<\/em><\/strong><em> and a <\/em><strong><em>Threat Monitoring Platform<\/em><\/strong><em> that provides accurate exploits detections and fully customized alerts covering hundreds of risk types with a clear expertise in\u00a0DeFi.<\/em><em>Today, Nefture proudly collaborates with leading projects and asset managers, providing them with unparalleled security solutions.<\/em><a href=\"https:\/\/www.nefture.com\/demo\"><strong><em>Book a demo<\/em><\/strong><\/a><strong><em>\u00a0<\/em><\/strong><em>\ud83e\udd1d<\/em><\/p>\n<p><a href=\"https:\/\/medium.com\/coinmonks\/cetus-hack-post-mortem-of-a-223m-heist-acd851f2e5b9\">Cetus Hack\u200a\u2014\u200aPost-Mortem of a $223M Heist<\/a> was originally published in <a href=\"https:\/\/medium.com\/coinmonks\">Coinmonks<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>","protected":false},"excerpt":{"rendered":"<p>Cetus Hack\u200a\u2014\u200aPost-Mortem of a $223M\u00a0Heist $223 million was stolen in what might be one of the simplest hacks the crypto space has\u00a0seen. All the attacker needed to do was come knocking at the door with a high liquidity position, and they were handed the entire Cetus treasury. While Cetus labeled the attack a \u201csophisticated smart [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-71316","post","type-post","status-publish","format-standard","hentry","category-interesting"],"_links":{"self":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/71316"}],"collection":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=71316"}],"version-history":[{"count":0,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/71316\/revisions"}],"wp:attachment":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=71316"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=71316"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=71316"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}