20 ETH mất vì một chiếc hợp đồng cười.
Đó không phải là một dòng tweet giật tít. Đó là sự thật tôi nhìn thấy trong audit code của một dự án vừa huy động 12 triệu USD từ các quỹ đầu tư mạo hiểm hàng đầu. Dự án tự xưng là 'giao thức cho vay thế hệ tiếp theo' với lời hứa lãi suất ổn định và thanh khoản sâu. Nhưng chỉ cần đọc qua hàm withdraw() trong hợp đồng chính, tôi đã thấy một backdoor rõ ràng đến mức buồn cười.
Backdoor không bao giờ là lỗi, là chủ ý.
Hãy để tôi đưa bạn vào cuộc giải phẫu.
Context: Cơn sốt cho vay phi tập trung
Chúng ta đang ở giữa một bull run. Mọi người đổ xô vào DeFi, tìm kiếm lợi nhuận từ lending pools. Các dự án mọc lên như nấm sau mưa, mỗi dự án đều có whitepaper dài 50 trang, đội ngũ 'danh tiếng' và tokenomics được thiết kế tinh vi. Nhưng tôi, một người đã sống qua DeFi Summer 2020 và mất 20 ETH vì một lỗi yield farming, biết rằng giữa cơn sốt, người ta dễ bỏ qua những chi tiết kỹ thuật chết người.
Dự án tôi đang nói đến (tạm gọi là 'LiquidGold') huy động vốn từ các VC lớn, có audit từ một công ty 'uy tín' và thậm chí còn có cả chương trình bug bounty. Nhưng audit chỉ là một bức ảnh chụp tại một thời điểm. Nó không thể hiện được toàn bộ bức tranh, đặc biệt khi code được update sau audit.

Core: Mổ xẻ backdoor
Tôi bắt đầu bằng việc kiểm tra hàm withdraw(). Hợp đồng cho vay thông thường cho phép người dùng rút tiền gốc và lãi. Nhưng ở LiquidGold, hàm withdraw() có một modifier đặc biệt: onlyAdmin. Có vẻ vô hại? Admin có thể rút tiền? Nhưng khi tôi đọc kỹ, modifier này không chỉ dành cho admin – nó được gắn vào một hàm public khác: emergencyWithdraw().
Hàm emergencyWithdraw() cho phép bất kỳ ai gọi nó, miễn là có một chữ ký đặc biệt từ admin. Nghe có vẻ an toàn? Nhưng vấn đề là: chữ ký đó được kiểm tra bằng cách so sánh với một địa chỉ admin được lưu trong storage. Địa chỉ admin này có thể thay đổi thông qua một hàm changeAdmin() – và hàm này chỉ yêu cầu một chữ ký từ 'admin hiện tại'.
Bạn thấy lỗ hổng chưa? Nếu một kẻ tấn công có được chữ ký của admin hiện tại (qua phishing, hoặc admin là một multisig bị compromise), hắn có thể thay đổi admin thành địa chỉ của mình, sau đó gọi emergencyWithdraw() để rút toàn bộ pool.
Nhưng điều đó vẫn chưa phải là backdoor thực sự. Backdoor nằm ở chỗ: không có giới hạn nào về số lần gọi changeAdmin(). Và quan trọng hơn, chữ ký được xác thực bằng cách so sánh với một địa chỉ admin duy nhất – không có cơ chế revoke hay timeout. Một khi bạn có chữ ký của admin, bạn có thể thay đổi admin liên tục, và rút tiền từ từ mà không bị phát hiện.

Đây là một lỗi thiết kế sơ đẳng. Bất kỳ ai có kinh nghiệm audit đều phải nhận ra. Vậy tại sao nó lọt qua audit? Câu trả lời: audit chỉ kiểm tra phiên bản snapshot của code, và nhóm phát triển có thể đã thêm backdoor sau khi audit hoàn tất. Hoặc tệ hơn, audit đã bỏ qua nó vì… lý do nào đó.
Tôi từng thấy điều này trước đây. Năm 2017, tôi phát hiện một backdoor trong hợp đồng ICO của BlockVault. Hàm ẩn cho phép đội ngũ rút toàn bộ ETH sau 30 ngày. Khi tôi báo cáo, quỹ hủy đầu tư 500 ETH. Dự án sụp đổ sau đó hai tháng. Lịch sử có xu hướng lặp lại, chỉ khác là công nghệ phức tạp hơn và số tiền lớn hơn.
Contrarian: Phần phe bò đúng
Nhưng hãy công bằng. Không phải lúc nào backdoor cũng là ý đồ xấu. Đôi khi, nhóm phát triển để lại 'cửa sau' để bảo trì, nâng cấp hoặc khắc phục lỗi khẩn cấp. Trong trường hợp của LiquidGold, đội ngũ có thể lý luận rằng họ cần một cơ chế khẩn cấp để rút tiền trong trường hợp có lỗi nghiêm trọng. Và đúng vậy, nhiều giao thức lớn như MakerDAO cũng có các cơ chế tương tự.
Nhưng sự khác biệt nằm ở minh bạch và kiểm soát. MakerDAO có các cơ chế quản trị phân quyền, multisig và thời gian chờ. LiquidGold lại không có bất kỳ cơ chế nào như vậy. Một admin duy nhất có thể thay đổi địa chỉ admin và rút tiền ngay lập tức. Đây không phải là một 'tính năng' – đó là một quả bom hẹn giờ.
Tôi hiểu rằng trong thị trường tăng, mọi người muốn tin tưởng. Họ muốn FOMO vào dự án 'tiềm năng'. Nhưng với tư cách là một người đã mất 20 ETH chỉ vì một lỗi yield farming, tôi có trách nhiệm phải lên tiếng.
Takeaway: Trách nhiệm không chỉ thuộc về nhà phát triển
Bạn – nhà đầu tư, người dùng, KOL – có trách nhiệm đọc code. Không phải ai cũng có thể audit toàn bộ, nhưng ít nhất hãy kiểm tra xem dự án có các cơ chế bảo vệ cơ bản không: multisig, timelock, các giới hạn rút tiền. Nếu một dự án không có những thứ đó, hãy nghi ngờ.
Còn với các nhà phát triển: đừng biến sự bất cẩn thành lý do. Một backdoor trong code không phải là 'lỗi' – đó là sự lựa chọn. Và khi bạn chọn không minh bạch, bạn đang đánh cược với tiền của người khác.
Tôi sẽ tiếp tục theo dõi LiquidGold. Nhưng lời khuyên của tôi: đừng đưa tiền vào một hợp đồng có thể cười vào mặt bạn.
20 ETH mất vì một chiếc hợp đồng cười. Đừng để điều đó xảy ra với bạn.