Tôi nhận được một smart contract audit vào tuần trước. Một DEX nhỏ đang fork Uniswap V4, tự hào về tính 'modular' và 'hook' linh hoạt. Họ gửi cho tôi 200 dòng code hook, kèm theo lời hứa 'sẽ cách mạng hóa thanh khoản'. Tôi mở Remix, chạy thử nghiệm, và phát hiện ra điều khiến tôi phải dừng lại: một lỗ hổng reentrancy trong hook beforeSwap có thể rút sạch pool chỉ trong một giao dịch. Đây không phải là lỗi mới, nhưng nó minh họa cho một vấn đề cốt lõi: càng linh hoạt, càng dễ bị tấn công. Và thị trường tăng giá hiện tại đang che giấu hàng loạt vấn đề tương tự.
Uniswap V4 ra mắt với lời hứa về 'hooks' — những đoạn code tùy chỉnh cho phép developer can thiệp vào mọi bước của quá trình swap. Ý tưởng rất hay: thay vì phải fork toàn bộ AMM, bạn chỉ cần viết một hook nhỏ để thêm phí, kiểm soát giá, hoặc kết nối với oracle. Nhưng vấn đề nằm ở chỗ: hook là code chạy trong cùng một transaction, cùng một EVM context, và có quyền truy cập vào toàn bộ trạng thái của pool. Nếu hook được viết không cẩn thận, nó mở ra cánh cửa cho hàng loạt vector tấn công mà Uniswap V3 không gặp phải.
Cụ thể, trong trường hợp tôi audit, hook beforeSwap thực hiện một cuộc gọi đến một contract bên ngoài để kiểm tra giá oracle. Cuộc gọi này là một external call không an toàn. Kẻ tấn công có thể deploy một contract giả mạo oracle, khi hook gọi đến, contract độc hại đó sẽ gọi lại hàm swap của pool với một callback khác, tạo ra vòng lặp reentrancy. Bởi vì hook chưa cập nhật trạng thái reserve (Uniswap V4 dùng beforeSwap và afterSwap để quản lý), pool sẽ cho phép swap lần thứ hai dựa trên số dư cũ. Kết quả: kẻ tấn công có thể rút nhiều token hơn số dư thực tế, drain toàn bộ pool chỉ trong một giao dịch.
Tôi gửi báo cáo cho team dự án. Họ im lặng ba ngày, sau đó trả lời rằng họ đã 'fix' bằng cách thêm một mutex lock. Tôi kiểm tra lại code: họ thêm require(!locked) và locked = true ở đầu hook. Cơ bản, họ copy-paste từ OpenZeppelin ReentrancyGuard mà không hiểu rằng trong Uniswap V4, hook có thể bị gọi nhiều lần trong cùng một transaction từ nhiều nguồn khác nhau (ví dụ: từ một hook khác). Lock đơn giản không đủ. Bạn cần kiểm tra rằng hook chỉ được gọi đúng một lần trên mỗi transaction, hoặc sử dụng checks-effects-interactions pattern nghiêm ngặt. Họ không làm vậy.
Đây là điểm mù bảo mật mà tôi thấy lặp đi lặp lại trong các dự án DeFi mùa bull run này. Các đội ngũ phát triển, dưới áp lực phải ship sản phẩm nhanh để bắt kịp đà tăng, thường bỏ qua các edge case. Họ nghĩ rằng audit là 'mua bảo hiểm', không phải là một phần của quy trình phát triển. Họ thuê auditor, nhưng auditor không thể tìm ra hết lỗi nếu thiết kế cơ bản đã sai. Trong trường hợp này, lỗi thiết kế là cho phép hook thực hiện external call trước khi hoàn tất swap. Uniswap V4 cho phép điều này, nhưng tài liệu của nó cảnh báo rất rõ: 'Không bao giờ gọi external contract trong hook unless bạn hiểu rõ rủi ro'. Hầu hết developer không đọc tài liệu.
Tôi nhớ lại năm 2017, khi audit ShibeCoin, lỗi batchTransfer cũng xuất phát từ việc không kiểm tra msg.value — một validation thiếu. Năm 2020, Uniswap V2 router có lỗi amountOutMin không được validate đúng. Năm 2022, Arbitrum Nitro bridge thiếu kiểm tra seqNum tăng dần. Tất cả đều là lỗi validation cơ bản, nhưng mỗi lần chúng xuất hiện dưới một hình thức mới. Với Uniswap V4, lỗi validation không nằm ở pool chính, mà nằm ở hook — nơi developer tự do nhất. Sự tự do đó trở thành cạm bẫy.
Góc nhìn phản trực giác: Thị trường tăng giá hiện tại đang tạo ra ảo tưởng rằng 'nếu dự án được nhiều người dùng, nó phải an toàn'. Sự thật ngược lại: càng nhiều người dùng, càng nhiều kẻ tấn công nhắm vào. Các dự án lớn như Uniswap có đội ngũ bảo mật mạnh, nhưng các dự án fork nhỏ thì không. Họ copy code từ Uniswap, thêm hook tùy chỉnh, và không audit kỹ. Khi TVL tăng, họ trở thành mục tiêu. Tôi dự đoán trong 6 tháng tới, sẽ có ít nhất 3 vụ tấn công liên quan đến Uniswap V4 hook, mỗi vụ gây thiệt hại trên 10 triệu USD. Các auditor sẽ bận rộn, nhưng thiệt hại đã xảy ra.
Câu hỏi cho bạn đọc: Khi bạn nhìn vào một dự án DeFi mới, bạn có kiểm tra hook của nó không? Hay bạn chỉ nhìn vào logo, website, và số liệu TVL? Nếu câu trả lời là 'không', thì bạn đang đặt cược vào một cánh cửa không khóa.