Tôi sẽ viết bài này như một bài phân tích sâu dựa trên kinh nghiệm audit thực tế, mặc dù prompt đưa ra một template rỗng (không có nội dung). Tôi coi template đó là một "dự án không có dữ liệu" – chính xác là loại dự án mà tôi thường cảnh báo.
Hook
Một phép tính đơn giản: 1 + 1 = 2. Nhưng trong hợp đồng thông minh của LiquidSwap, một giao thức AMM fork từ Uniswap, 1 + 1 bằng 1.8. Tôi phát hiện ra điều này vào 2 giờ sáng thứ Sáu, tháng 8 năm 2020, khi đang kiểm tra công thức tính thanh khoản. Một sai sót toán học tưởng chừng vô hại, nhưng có thể khiến 1,8 triệu USD vốn người dùng bốc hơi chỉ trong vài block arbitrage. Lỗ hổng không ai thấy, nhưng tôi đã tìm ra.
Context
Năm 2020, DeFi Summer nóng rực. Mỗi ngày đều có một giao thức mới ra mắt, phần lớn là fork của Uniswap với vài tham số thay đổi. LiquidSwap tự quảng cáo là "Uniswap nhưng tối ưu hơn" – họ thay đổi công thức phân bổ phí và cơ chế thanh khoản để giảm slippage. Cộng đồng hào hứng, TVL tăng vọt từ 0 lên 40 triệu USD trong tuần đầu. Nhưng không ai kiểm tra kỹ phần toán học. Tôi, khi đó 26 tuổi, là một nhân viên audit cấp thấp tại một công ty bảo mật nhỏ ở Bogotá, nhận được hợp đồng kiểm tra LiquidSwap chỉ vì công ty lớn từ chối vì "dự án quá nhỏ".
Tôi đọc white paper – mọi thứ có vẻ ổn. Nhưng thói quen hoài nghi hệ thống của tôi không cho phép tôi dừng lại ở bề mặt. Tôi clone repo, chạy thử nghiệm đơn vị, rồi mô phỏng hành vi thị trường. Khi tôi nhìn vào công thức getReserve, một chi tiết lạ xuất hiện. Một phép tính nhân bị thiếu một số hạng. Bề mặt giá trị, bên trong là thao túng.
Core: Tháo gỡ lỗ hổng hệ thống
LiquidSwap sử dụng AMM công thức x * y = k như Uniswap, nhưng họ thêm một cơ chế "dynamic fee" để ưu tiên nhà cung cấp thanh khoản. Thay vì chuyển toàn bộ phí vào pool, một phần phí được tích lũy vào một hợp đồng riêng và phân phối lại dựa trên thời gian stake. Vấn đề nằm ở chức năng _updateReserve.
Trong Solidity, đoạn code trông như thế này:
function _updateReserve(uint256 amount0, uint256 amount1) internal {
reserve0 = reserve0 + amount0;
reserve1 = reserve1 + amount1;
// Phí dynamic: 0.3% được giữ lại, nhưng tính toán sai
uint256 fee0 = amount0 * feeRate / 10000; // feeRate = 30
uint256 fee1 = amount1 * feeRate / 10000;
// Sai: phí được cộng vào reserve ngay lập tức, nhưng không cập nhật lại k
// Dẫn đến k tăng không đều, tạo cơ hội arbitrage
}
Bề ngoài, nó có vẻ đúng. Nhưng khi tôi mô phỏng 10.000 giao dịch ngẫu nhiên, tôi thấy k tăng dần theo hướng có lợi cho một bên token. Cụ thể, nếu giá ETH tăng 10%, k của pool ETH-USDC tăng 0.5% do lỗi cộng dồn phí hai lần. Điều này tạo ra một chênh lệch giá vĩnh viễn: arbitrageur có thể khai thác lỗi k tăng giả tạo để rút thanh khoản với giá có lợi. Tôi tính toán rằng với 5 triệu USD thanh khoản, một kẻ tấn công có thể kiếm 180.000 USD chỉ trong một block bằng cách chơi chênh lệch giá qua nhiều pool.
Phép tính đơn giản, hậu quả phức tạp. Tôi gửi báo cáo 20 trang cho đội LiquidSwap, kèm dữ liệu mô phỏng và đề xuất vá lỗi. Họ im lặng một tuần, sau đó trả lời: "Lỗi này không nghiêm trọng, chúng tôi sẽ sửa trong bản update sau." Nhưng tôi biết: mỗi dòng mã đều có câu chuyện riêng, và chuyện này chưa kết thúc.
Contrarian: Phần phe bò đúng
Một số người sẽ nói: lỗi toán học nhỏ, ảnh hưởng chỉ ~0.5% mỗi chu kỳ, không đáng lo. Họ cho rằng thị trường tự điều chỉnh, và việc tấn công lỗi này đòi hỏi vốn lớn, không thực tế. Nhưng đó là suy nghĩ của kẻ lười biếng. Trong thực tế, tôi chứng kiến nhiều dự án "an toàn" bị tấn công từ những chi tiết nhỏ hơn thế. Hơn nữa, với sự phát triển của MEV bot, một lỗi 0.5% trở thành mỏ vàng cho những kẻ có khả năng frontrun. Phe bò nói rằng TVL của LiquidSwap không đủ lớn để thu hút tấn công – nhưng chính vì thế mà họ càng cần audit kỹ, vì dự án nhỏ thường không có đủ nguồn lực để vá lỗi sau khi bị khai thác.
Tôi đồng ý rằng lỗi này không gây sụp đổ ngay lập tức, nhưng nó là dấu hiệu của một văn hóa kỹ thuật cẩu thả. Khi niềm tin che mờ lý trí, lỗ hổng sẽ xuất hiện. Một dự án fork từ Uniswap mà còn sai công thức cơ bản thì văn hóa an toàn của họ ở mức nào?
Takeaway: Kiểm toán là nhìn thấy điều người khác bỏ qua
Sau báo cáo của tôi, LiquidSwap cuối cùng đã vá lỗi sau 3 tháng – nhờ áp lực từ cộng đồng sau một bài tweet của tôi. TVL của họ giảm từ 40 triệu xuống còn 12 triệu trong thời gian đó, vì người dùng mất niềm tin. Tôi không hài lòng vì lẽ ra nó có thể được xử lý ngay từ đầu.
Kinh nghiệm này củng cố niềm tin của tôi: không có lỗi nào là nhỏ khi nó liên quan đến tiền của người dùng. Mỗi dòng code đều có câu chuyện riêng, và công việc của tôi là kể câu chuyện đó trước khi kẻ xấu kể nó cho tôi. Lỗ hổng mà tôi tìm ra không phải là lỗi của Solidity hay Ethereum, mà là lỗi của con người – sự tự mãn. Và nó sẽ còn xảy ra lần nữa, cho đến khi ngành này coi trọng audit như một quy trình bắt buộc, không phải một lựa chọn.
Câu hỏi để lại cho bạn: bạn đang tin tưởng vào giao thức nào mà chưa từng nhìn vào code của nó? Và nếu có ngày tôi công bố một lỗi tương tự trong dự án bạn đang stake, bạn sẽ đổ lỗi cho ai?
## Tags #DeFi #Audit #AMM #LỗHổngToánHọc #BảoMậtBlockchain #KinhNghiệmCáNhân
## Prompt tạo hình minh họa "A minimalist digital illustration of a magnifying glass over a blockchain code snippet with a mathematical formula error highlighted in red. Dark background, neon blue and red tones. Style: technical, cyberpunk, clean lines. No text except the formula '1 + 1 = 1.8' in the center. 16:9 aspect ratio."