Khi Oracle Sụp Đổ: Bài Học Từ Vụ Hack 50 Triệu USD Trên Thị Trường Giảm
Hook
Tối thứ Ba, tôi nhận được một ping trên Discord từ một developer trẻ ở Hà Nội. Anh ấy gửi cho tôi một dòng log: “Giao thức X mất 50 triệu USD, oracle bị thao túng qua flash loan.” Tôi mở Etherscan, nhìn vào transaction hash. 50 triệu USD – không phải con số lớn nhất tôi từng thấy, nhưng trong bối cảnh thị trường đang giảm, mỗi đồng thanh khoản đều quý giá. Điều làm tôi chú ý không phải số tiền, mà là cách attacker khai thác: chính xác như mẫu tấn công mà tôi đã ghi chép trong AuditFlow cách đây ba năm. Một lỗi thiết kế oracle cổ điển, nhưng vẫn tồn tại trong một giao thức đã được audit bởi hai công ty lớn.
Context
Giao thức X là một AMM tập trung vào stablecoin, chạy trên Arbitrum. Nó sử dụng một oracle giá từ một sàn DEX bên ngoài, cập nhật mỗi 30 phút. Điểm đặc biệt là nó cho phép người dùng vay tài sản dựa trên giá trị collateral được lấy từ oracle đó. Khi thị trường giảm, thanh khoản trở nên mỏng hơn, và oracle giá từ DEX bên ngoài dễ bị ảnh hưởng bởi các lệnh lớn. Attacker đã dùng một flash loan 200 triệu USD để tạm thời đẩy giá của một cặp token trên DEX kia lên gấp 5 lần. Oracle ghi nhận mức giá đó, và hợp đồng của giao thức X cho phép attacker rút toàn bộ quỹ dự trữ stablecoin trước khi giá hồi phục.
Tôi đã từng kiểm tra mã nguồn của một oracle tương tự trong quá trình audit cho một dự án năm 2021. Khi đó tôi phát hiện lỗ hổng trong logic lấy giá trung bình. Nhưng lần này, oracle không có cơ chế TWAP. Nó chỉ lấy giá spot từ một pool duy nhất. Đó là một thiết kế rủi ro cao, nhưng vẫn được nhiều đội dev trẻ sử dụng vì đơn giản.
Core
Tôi dành cả đêm phân tích mã nguồn của giao thức X. Hợp đồng chính có khoảng 1500 dòng Solidity, tôi tìm thấy ba điểm yếu quan trọng:
- Oracle chỉ sử dụng một nguồn: Hàm
getPrice()gọi thẳng đến một pool Uniswap V2. Không có fallback, không có kiểm tra độ trễ. Khi attacker bơm thanh khoản giả qua flash loan, pool price thay đổi tức thì. - Thiếu kiểm tra “slippage” logic: Hợp đồng cho vay không so sánh giá hiện tại với giá của block trước. Nó chấp nhận bất kỳ giá nào từ oracle miễn là trong khoảng thời gian 30 phút (cập nhật theo block timestamp). Attacker có thể thao túng giá ngay sau khi oracle vừa update.
- Tính toán collateral dựa trên giá spot: Công thức
collateral = amount * price / 1e18không có bất kỳ bộ lọc nào. Attacker gửi một ít token rác, oracle báo giá cao, và hợp đồng tính ra collateral khổng lồ, cho phép mượn gần như toàn bộ pool stablecoin.
Trong AuditFlow, tôi từng tổng hợp 15 mẫu tấn công, và “oracle manipulation via flash loan” là mẫu số 3. Tôi nhớ mình đã viết một báo cáo 30 trang về vụ Polymath hack, nơi lỗ hổng tương tự xuất hiện. Sự khác biệt duy nhất là ở Polymath, token mint không giới hạn; ở đây, nó cho phép rút stablecoin. Cùng một gốc rễ: tin tưởng vào một nguồn dữ liệu duy nhất mà không có cơ chế xác thực chéo.
Tôi so sánh với các giải pháp khác mà tôi đã triển khai cho BlackRock ETF custody. Ở đó, chúng tôi sử dụng multiple oracle feeds (Chainlink, MakerDAO Medianizer, và một backup tự tính từ nhiều DEX). Mỗi feed có trọng số khác nhau, và có một bộ lọc “median-of-medians”. Chi phí gas cao hơn, nhưng độ an toàn tăng lên đáng kể. Giao thức X có lẽ đã chọn giải pháp rẻ hơn để tiết kiệm gas, nhưng cái giá phải trả là 50 triệu USD.
Tôi simulate lại attack step-by-step trên local Forks. Kết quả khớp hoàn hảo với transaction thật. Attacker mất tổng cộng 4 block để thực hiện toàn bộ chuỗi hành động: flash loan → swap → gọi oracle → borrow → rút stablecoin → trả flash loan. Với 200 triệu USD flash loan, họ kiếm được 50 triệu USD chỉ trong 30 giây. Đó là tỷ lệ lợi nhuận 25% – cao hơn bất kỳ chiến lược nào trong thị trường giảm.
Contrarian
Nhiều người trong cộng đồng nghĩ rằng sử dụng TWAP (time-weighted average price) là đủ an toàn. Tôi từng thấy nhiều team dev tự hào khoe “chúng tôi dùng Uniswap TWAP oracle”. Nhưng TWAP không phải vạn năng. Trong thị trường giảm, thanh khoản thấp, TWAP 2 phút vẫn có thể bị thao túng nếu attacker dùng flash loan nhiều lần trong cùng một block. Thực tế, tôi đã chứng minh điều này trong một bài viết trên AuditVerse: với mức thanh khoản giảm 80% so với đỉnh, một flash loan 50 triệu USD có thể đẩy TWAP 2 phút của một pool nhỏ lên 50%.
Điểm mù thực sự ở đây không phải là loại oracle, mà là thiết kế miễn nhiễm với thanh khoản thấp. Giao thức X đã không nghĩ rằng thị trường có thể giảm sâu đến mức thanh khoản trên DEX chỉ còn 1/10. Họ xây dựng dựa trên giả định thị trường bull. Khi tôi audit hợp đồng cho một quỹ ETF năm 2024, tôi đã nhấn mạnh: phải stress-test oracle ở mọi kịch bản thanh khoản. Cụ thể, tôi đề xuất một hệ số “liquidity threshold” – nếu thanh khoản của pool oracle giảm dưới một mức, giao thức sẽ chuyển sang chế độ emergency, đóng băng cho vay cho đến khi thanh khoản phục hồi. Giao thức X không có cơ chế nào như vậy.
Một góc nhìn khác: không phải lỗi hoàn toàn do oracle. Chính logic vay dựa trên giá spot là vấn đề. Đã đến lúc các giao thức DeFi cần suy nghĩ lại về “collateral factor”. Tại sao không sử dụng giá trị trung bình nhiều ngày thay vì giá tức thời? Điều này buộc attacker phải duy trì thao túng trong thời gian dài, rủi ro bị front-run bởi arbitrageur giảm. Tôi đã thấy một số dự án làm điều này, nhưng chưa trở thành tiêu chuẩn.
Takeaway
Trong thị trường giảm, mồi ngon cho hacker không chỉ là TVL lớn, mà là những lỗ hổng nằm im từ thời bull. Giao thức X mất 50 triệu USD, nhưng bài học đắt giá hơn: đừng bao giờ tin tưởng vào một nguồn dữ liệu duy nhất. Nếu tôi có thể gửi một thông điệp đến mọi developer DeFi đang đọc bài này, đó là: hãy tự hỏi, trong kịch bản thanh khoản giảm 90%, oracle của bạn có còn an toàn không? Nếu câu trả lời là “không”, thì bạn đang ngồi trên quả bom hẹn giờ. Tôi sẽ tiếp tục theo dõi các giao thức tương tự, và viết tiếp loạt bài “Mỗi ngày một lỗi bảo mật” – bởi vì trong thế giới này, không có kỳ nghỉ cho hacker.