Trong 11 ngày qua, giao thức Horizon – một nền tảng cho vay cross-chain từng được xếp hạng A+ bởi CertiK – đã hứng chịu một loạt các cuộc tấn công tinh vi, mỗi đêm một phương thức khác nhau. Dữ liệu on-chain từ Dune Analytics cho thấy tổng thiệt hại đã vượt 40 triệu USD, với hơn 15.000 ETH và 8 triệu USDC bị rút khỏi các pool thanh khoản. Điều đáng nói: Horizon vừa hoàn thành đợt audit thứ ba chỉ một tháng trước, với kết luận “không phát hiện lỗ hổng critical”. Vậy điều gì đã xảy ra?
Horizon là một giao thức cho vay đa chuỗi, cho phép người dùng gửi tài sản trên Ethereum, Arbitrum và Polygon để kiếm lãi suất. Cơ chế bảo mật của nó dựa trên multi-sig 5/9 với các signer là những thành viên uy tín trong cộng đồng, cùng với oracle phi tập trung Chainlink để lấy giá. Tuy nhiên, cuộc tấn công kéo dài 11 đêm đã làm lộ ra điểm yếu trong thiết kế tổng thể: không có cơ chế dừng khẩn cấp (emergency pause) tự động, và quy trình nâng cấp hợp đồng quá chậm. Khi tôi đọc dòng log đầu tiên, tôi biết đây không phải là một vụ hack thông thường – nó có tổ chức, có kịch bản, và kẻ tấn công đã nghiên cứu mã nguồn từ nhiều tháng trước.
Phân tích kỹ thuật từng đợt tấn công Đêm thứ nhất: Reentrancy cổ điển. Attacker gọi hàm withdraw() với một hợp đồng độc hại, lợi dụng callback receive() để gọi lại withdraw() trước khi số dư được cập nhật. Horizon đã có bảo vệ Checks-Effects-Interactions, nhưng lỗi nằm ở chỗ biến totalLiquidity được cập nhật sau khi chuyển token, tạo cửa sổ cho reentrancy. Cụ thể, dòng code totalLiquidity -= amount; nằm sau IERC20(token).safeTransfer(msg.sender, amount);. Đây là lỗi sắp xếp cổ điển mà audit trước đã bỏ sót vì cho rằng safeTransfer an toàn. Sai lầm: safeTransfer chỉ bảo vệ khỏi token giả, không ngăn reentrancy.
Đêm thứ hai: Flash loan + price manipulation. Attacker vay 50 triệu USDC từ Aave, swap trên Uniswap V3 để đẩy giá của token HZN lên 3 lần, sau đó dùng HZN làm tài sản thế chấp để vay toàn bộ pool USDC trên Horizon. Điểm mấu chốt: Horizon sử dụng giá từ Chainlink, nhưng Chainlink cập nhật mỗi 30 phút. Trong khoảng thời gian đó, giá on-chain của Uniswap đã thay đổi, nhưng oracle vẫn giữ giá cũ, cho phép attacker vay với tài sản thế chấp bị định giá quá cao. Đây là lỗ hổng kinh điển của các giao thức cho vay không có TWAP (Time-Weighted Average Price).
Đêm thứ ba: Dependency injection. Attacker triển khai một oracle giả thông qua proxy upgrade, thay thế Chainlink bằng hợp đồng của hắn. Horizon cho phép admin cập nhật oracle thông qua multi-sig, nhưng quy trình phê duyệt có lỗi: chỉ cần 3/9 chữ ký thay vì 5/9 cho các thay đổi “không quan trọng”. Attacker đã chiếm được 3 private keys thông qua phishing email gửi đến signer. Mỗi dòng Solidity đều có một câu chuyện, và câu chuyện này kết thúc bằng một lỗ hổng critical trong logic cấp quyền.
Đêm thứ tư: Logic lỗi trong tính lãi suất. Attacker phát hiện rằng hàm accrueInterest() có thể bị gọi nhiều lần trong cùng một block, dẫn đến lãi kép không giới hạn. Bằng cách gọi deposit() và withdraw() liên tục trong một giao dịch, hắn đã nhân số dư của mình lên gấp 10. Lỗi này tồn tại vì đội phát triển không đặt modifier nonReentrant cho các hàm tính lãi, cho rằng chúng chỉ được gọi nội bộ. Sai lầm: accrueInterest() là hàm public, bất kỳ ai cũng có thể gọi.
Đêm thứ năm và sáu: Kết hợp nhiều kỹ thuật. Attacker dùng cross-chain messaging wormhole để gửi tin nhắn giả từ Arbitrum sang Ethereum, lợi dụng việc xác thực Merkle proof không kiểm tra kích thước mảng. Điều này cho phép hắn mint token HZN giả trên Ethereum. Đây là lỗi phổ biến trong các bridge: không validate proof.length trước khi xử lý.
Đêm thứ bảy đến mười một: Sau khi các lỗ hổng chính bị vá, attacker chuyển sang tấn công các hợp đồng phụ trợ như staking rewards, vesting, và governance. Mỗi đêm, đội ngũ Horizon vá một lỗi, nhưng lại tạo ra lỗi mới do vá vội vàng. Ví dụ: khi thêm emergency pause, họ quên đặt quyền cho multisig, dẫn đến attacker có thể kích hoạt pause để khóa thanh khoản của người dùng khác.
Góc nhìn phản trực giác: Ai cũng cho rằng bị tấn công 11 đêm liên tiếp là thảm họa. Nhưng thực tế, điều này cho thấy cộng đồng bảo mật và đội ngũ Horizon đã phản ứng rất nhanh: mỗi lỗ hổng được vá trong vòng 6-8 giờ nhờ sự hỗ trợ của các white hat từ Immunefi. Tuy nhiên, chính sự vá lỗi vội vàng lại tạo ra bề mặt tấn công mới. Điểm mù lớn nhất: ban phát triển không có một kế hoạch tổng thể, chỉ ứng phó từng đợt như “chữa cháy”. Họ thiếu một framework bảo mật toàn diện: không có formal verification, không có fuzzing tự động, không có kịch bản phục hồi disaster recovery. Nếu bạn nghĩ rằng multi-sig là đủ, hãy nhìn vào Horizon: họ có 5/9 multi-sig, nhưng attacker đã lợi dụng chính quy trình phê duyệt để chiếm quyền kiểm soát oracle.
Takeaway: Cuộc vây hãm 11 đêm của Horizon là hồi chuông cảnh tỉnh cho toàn ngành DeFi. Nó cho thấy audit đơn thuần không đủ; cần có hệ thống phòng thủ nhiều lớp bao gồm: formal verification, bug bounty liên tục, emergency pause tự động, và quan trọng nhất – một kế hoạch ứng phó sự cố được thử nghiệm trước. Câu hỏi đặt ra: Liệu Horizon có thể sống sót sau 11 đêm này, hay sẽ trở thành bài học cho thế hệ giao thức tiếp theo? Dựa trên kinh nghiệm kiểm toán của tôi, tôi tin rằng nếu họ không thay đổi triết lý bảo mật từ “chữa cháy” sang “phòng ngừa”, thì đêm thứ 12 sẽ đến sớm hơn họ nghĩ.