Tôi vừa audit một bản nâng cấp cho giao thức EigenLayer và phát hiện một dòng code làm tôi giật mình: function initialize(address initialOwner) public initializer { _transferOwnership(initialOwner); }.
— Root: Cảm xúc của tôi khi ấy không phải là sự ngạc nhiên về kỹ thuật, mà là nỗi lo lắng cho cộng đồng. Bởi dòng code tưởng chừng vô hại này thực chất là một ‘cửa sau’ mà ít người để ý: cho phép kẻ tấn công, nếu chiếm được quyền triển khai, có thể chiếm quyền kiểm soát giao thức ngay từ giây phút đầu tiên.
Context: EigenLayer là giao thức restaking tiên phong trên Ethereum, cho phép người dùng tái sử dụng ETH đã stake để bảo mật nhiều mạng lưới khác nhau. Tuy nhiên, cơ chế bootstrapping của nó phụ thuộc vào một proxy upgradeable và một contract logic mới. Khi triển khai, hợp đồng proxy được khởi tạo với initializer, về lý thuyết chỉ nên chạy một lần. Vấn đề? Một số team chọn lưu trữ ‘owner’ dưới dạng storage variable mà không kiểm tra địa chỉ đó có thật sự thuộc về team hay không.
Core: Tôi đã mở Remix và mô phỏng kịch bản. Giả sử hợp đồng logic có _transferOwnership(initialOwner) trong initialize. Kẻ tấn công có thể deploy một hợp đồng giả mạo copy hoàn toàn mã nguồn, nhưng thay initialOwner bằng địa chỉ của mình. Sau đó, họ deploy proxy trỏ vào logic giả đó. Vì hàm initialize chỉ chạy một lần, proxy sẽ nhận owner là kẻ tấn công. Họ có thể nâng cấp lên logic độc hại, rút toàn bộ tài sản. Tôi gọi đây là ‘reinitialization attack’ — mặc dù initializer modifier ngăn gọi lại, nhưng nó không bảo vệ việc ai có thể gọi lần đầu tiên.
— Root: Cảm xúc này càng tăng khi tôi nhận ra đây không phải lỗi hiếm. Từ Compound fork đến các bản sao Uniswap, hàng trăm dự án copy-paste mà không audit đầy đủ. Một đồng nghiệp từng nói với tôi: ‘Code is law, nhưng luật nào bảo vệ chúng ta khỏi chính người viết luật?’ Đây là điểm mù mà các nhà đầu tư thường bỏ qua: họ tin vào smart contract, nhưng quên rằng hợp đồng có thể bị kiểm soát bởi admin multi-sig, và admin đó có thể bị thay thế nếu ai đó chiếm được private key.
Contrarian: Nhiều người nghĩ rằng bảo mật DeFi nằm ở toán học và mã hóa. Tôi cho rằng, bảo mật thực tế lại nằm ở governance và quyền nâng cấp. EigenLayer không sai khi thiết kế như vậy — họ cần linh hoạt để bootstrapping. Nhưng cộng đồng cần hiểu: ‘Can’t be evil’ là một hứa hẹn, không phải bằng chứng. Điểm khác biệt giữa một giao thức an toàn và một kẻ lừa đảo là quyền truy cập vào admin key được bảo vệ thế nào, không phải vì code có hay không có lỗ hổng.
Takeaway: Lần tới khi bạn stake trên một giao thức mới, hãy tự hỏi: ai đang nắm quyền nâng cấp? Code là law, nhưng kẻ viết code mới là vua. Câu hỏi còn lại: liệu cộng đồng có đủ tỉnh táo để nhìn vào admin address trước khi gửi tiền của họ không?