Bài viết
Chia sẻ kiến thức của bạn.
Mổ xẻ khai thác giao thức Cetus trên Sui
Vào ngày 22 tháng 5 năm 2025, một vụ khai thác lớn nhắm vào Giao thức Cetus trên blockchain Sui, dẫn đến thiệt hại ước tính là 223 triệu đô la. Sự cố này đã thu hút sự chú ý ngay lập tức trên toàn hệ sinh thái, đặc biệt là từ các nhà quan sát kỹ thuật do các cơ chế bất thường liên quan. Sau đây là phân tích toàn diện về cuộc tấn công từ hoán đổi flash và thao tác tick đến lỗ hổng im lặng trong logic phát hiện tràn.
https://suiscan.xyz/mainnet/tx/DVMG3B2kocLEnVMDuQzTYRgjwuuFSfciawPvXXheB3x
Thiết lập
Kẻ tấn công bắt đầu khai thác bằng cách sử dụng flash_swap để mượn một lượng lớn token SUI để đổi lấy HaSui. Không giống như các flashloan truyền thống, flash_swap trong Cetus cho phép người dùng nhận token1 (SUI trong trường hợp này) trước và sau đó hoàn trả token0 (HaSui) trong cùng một giao dịch. Cơ chế này là trung tâm trong thiết lập của kẻ tấn công.
Trong trường hợp cụ thể này, kẻ tấn công đã mua thành công 5,76 triệu SUI và có nghĩa vụ hoàn trả 10,02 triệu HaSui. Nhưng trong quá trình hoán đổi này, giá của HaSui trong nhóm thanh khoản đã bị thao túng mạnh mẽ. Giá của pool đã giảm từ mức giá biểu thị tỷ lệ 1.056 xuống mức giá thấp đến mức giá chỉ đạt 0.0000009977 - một sự mất giá khổng lồ xuống 0,0000009% so với giá ban đầu của nó.
Phạm vi giá tương ứng với phạm vi đánh dấu là:
Tiêm thanh khoản chiến lược
Sau sự sụp đổ giá này, kẻ tấn công đã sử dụng open_position để tạo ra một vị trí thanh khoản phạm vi hẹp, chọn một phạm vi tick rất cụ thể (TickLower: 300000, TickUpper: 300200). Trong môi trường bị thao túng này, họ đã cung cấp một lượng thanh khoản khổng lồ: hơn 10 ^ 34 đơn vị, tất cả đều nằm trong phạm vi giá siêu nén được tạo ra bởi sự sụp đổ.
Điều này đã được thực hiện bởi một hàm dường như vô hại: get_amount_by_. Trong điều kiện bình thường, hàm này tính toán lượng token A và token B cần thiết để phù hợp với một mức thanh khoản nhất định. Nó thực hiện điều này bằng cách sử dụng các hàm trợ giúp như get_delta_a, dựa vào get_sqrt_price_at_tick.
Tuy nhiên, do dấu tích hiện tại được thao tác là -138185 và phạm vi đánh dấu được chọn nằm ngoài phạm vi đó (bắt đầu từ 300000), đường dẫn logic bên trong get_amount_by_đảm bảo rằng các phép tính sẽ đi qua một phần mã dễ bị tổn thương trong đó hoạt động dịch chuyển trái (checked_shlw) có liên quan.
Tràn và cắt ngắn: Lỗ hổng cốt lõi
Đây là nơi khai thác thực sự trở nên kỹ thuật. Chức năng checked_shlw được cho là xử lý dịch chuyển trái một số 256 bit bằng 64 bit. Trong môi trường giống như Solidity, một hoạt động như vậy có rủi ro vì nó có thể tràn. Việc triển khai cụ thể này đã cố gắng phát hiện tràn bằng cách kiểm tra xem đầu vào có vượt quá ngưỡng xác định trước hay không: 0xffffffffffffff << 192.
Ngưỡng thực tế để phát hiện sự dịch chuyển an toàn bằng 64 bit phải là 2^ (256 - 64) - 1. Tuy nhiên, (0xffffffffffffffff << 192) lớn hơn 2^192. Điều này có nghĩa là một đầu vào được lựa chọn cẩn thận có thể bỏ qua kiểm tra tràn bằng cách nằm dưới ngưỡng được sử dụng trong mã, nhưng vẫn đủ lớn để gây tràn trong quá trình thực thi.
Kẻ tấn công đã cung cấp một đầu vào như vậy, trong đó sự nhân lên của thanh khoản và chênh lệch giá tràn qua một cách âm thầm, trả lại một giá trị nhỏ hơn nhiều so với dự định - trên thực tế, gần bằng không.
Kết quả là, giao thức tính toán rằng kẻ tấn công chỉ cần trả một lượng token nhỏ — về cơ bản là một đơn vị — để thêm thanh khoản khổng lồ vào pool. Tính toán sai lầm này đã cho phép kẻ tấn công bơm thanh khoản khổng lồ mà không mất chi phí thực sự.
Sau đó, kẻ tấn công đã loại bỏ thanh khoản bổ sung thông qua remove_và hoàn thành cuộc tấn công bằng cách thanh toán các token chưa thanh toán của flash_swap thông qua repay_flash_swap. Kẻ tấn công kiếm được lợi nhuận là 5.765.124 SUI và 10.024.321 HaSui. Cuối cùng, nhóm Cetus đã khắc phục lỗ hổng thông qua hai PR:
https://github.com/CetusProtocol/integer-mate/pull/6/files
Cetus nhanh chóng phát hành một bản vá trên kho lưu trữ liên kết nguyên của họ, với yêu cầu kéo đầu tiên cố gắng tinh chỉnh phát hiện tràn. Tuy nhiên, sự khắc phục ban đầu đó là không đầy đủ. Nó sử dụng một mặt nạ 1 << 192, vẫn cho phép các giá trị chữ thường xuyên qua.
Yêu cầu kéo thứ hai tiếp theo là so sánh chặt chẽ hơn, kiểm tra xem đầu vào có lớn hơn hoặc bằng 2 ^ 192 hay không, đảm bảo giới hạn thích hợp để chuyển sang trái an toàn. Chỉ với sự điều chỉnh này, lỗ hổng mới được giảm thiểu một cách hiệu quả.
- Sui
Nguyên nhân gốc rễ không phải là lỗi logic trong cơ học DeFi mà là do không phát hiện chính xác hành vi tràn trong một phép toán số học cấp thấp