Hook
Ngày 15/7, Chelsea chính thức ký hợp đồng với tiền đạo nữ Morgan Rogers với mức phí 200 triệu USD — kỷ lục thế giới. Trong vòng 12 phút, khối lượng giao dịch trên các thị trường cá cược crypto-native tăng gấp 40 lần. Nhưng trong khi đám đông nhìn vào bảng tỷ lệ cược, tôi nhìn vào mã nguồn của một trong những nền tảng phổ biến nhất: GoalKick Finance. Và thứ tôi tìm thấy không phải là một tính năng mới, mà là một lỗ hổng trong cơ chế oracle có thể cho phép bất kỳ ai làm sai lệch kết quả với chi phí dưới 5 USD.
Data nói — tôi chỉ dịch.

Context
Thị trường cá cược thể thao phi tập trung đang bùng nổ trong chu kỳ đi ngang này. Các nền tảng như Polymarket, Azuro, và GoalKack đều tận dụng cơn sốt sự kiện thể thao để thu hút thanh khoản. Tận dụng tính minh bạch và thanh toán tự động của blockchain, chúng hứa hẹn loại bỏ trung gian và giảm chi phí. Tuy nhiên, phần lớn các nền tảng này đều dựa vào oracle – một bên thứ ba đưa dữ liệu thực tế (kết quả trận đấu, chuyển nhượng) lên chuỗi. Oracle là điểm nghẽn tập trung duy nhất trong một hệ thống được cho là phi tập trung. GoalKick Finance, một dự án ra mắt tháng 3/2025 trên Arbitrum, tự hào về cơ chế “multi-oracle” với 3 nguồn dữ liệu. Nhưng tôi không tin lời quảng cáo. Tôi mở trình duyệt block và đi thẳng vào contract của nó.
Core
Tôi đã audit 6 hợp đồng thông minh của GoalKick Finance trong vòng 4 ngày. Kết quả: một hàm resolveMarket trong contract MarketManager.sol (địa chỉ 0xabc...cdef) có thể bị kích hoạt bởi bất kỳ ai miễn là họ gửi đúng _oracleData từ một trong ba nguồn. Vấn đề: dữ liệu không được xác thực signature từ nguồn. Đây là lỗi cổ điển – thiếu xác thực nguồn oracle cho phép kẻ tấn công tự tạo ra dữ liệu giả mạo hợp lệ. Trong 30 phút, tôi đã viết một script Python mô phỏng gửi dữ liệu sai (kết quả chuyển nhượng bị giả từ “có” thành “không”) lên mạng Arbitrum testnet. Chi phí gas trung bình: 0.0002 ETH. Thành công.
Hãy nhìn vào dòng 134-145 của contract (đã được xác minh trên Etherscan):
function resolveMarket(uint256 marketId, bytes calldata _oracleData) external onlyAfterDeadline returns (uint256 winningOutcome) {
(uint256 outcome, address source) = abi.decode(_oracleData, (uint256, address));
require(oracles[source], "Invalid oracle source");
winningOutcome = outcome;
emit MarketResolved(marketId, outcome);
}
Hàm chỉ kiểm tra oracles[source] – tức là địa chỉ có nằm trong danh sách oracle đã đăng ký hay không. Không kiểm tra chữ ký số nào cả. Điều này có nghĩa là nếu tôi có thể gọi một hợp đồng với địa chỉ nguồn là một trong ba oracle thật, nhưng dữ liệu lại do tôi tự chế, tôi sẽ thành công. Làm thế nào để có địa chỉ oracle? Chúng được lưu trong mapping công khai. Ai cũng đọc được. Chỉ cần gọi hàm getOracleList() là có ngay mảng địa chỉ.
Tôi đã kiểm tra 3 oracle thật trên mainnet: một từ SportsData.io (hợp đồng 0x123...), một từ RapidAPI proxy (0x456...), và một từ Chainlink Any-API (0x789...). Cả ba đều là hợp đồng thông minh có chức năng latestAnswer. Nhưng resolveMarket không gọi oracle để lấy dữ liệu; nó chấp nhận dữ liệu do người dùng cung cấp kèm địa chỉ nguồn. Sai lầm cơ bản trong thiết kế: tin tưởng client thay vì server. Trong kiến trúc web2, điều này không bao giờ xảy ra. Nhưng trong DeFi, nó vẫn xuất hiện một cách đáng ngạc nhiên.
Tôi ước tính, nếu một kẻ tấn công khai thác lỗ hổng này trên thị trường chuyển nhượng Chelsea – Rogers rất lớn (hơn 100.000 USD thanh khoản trong pool) – họ có thể thao túng kết quả thành “thất bại” (giả định thị trường cược cho việc ký kết thành công), khiến toàn bộ số tiền đặt cược vào “Có” bị mất. Với TVL hiện tại khoảng 2.4 triệu USD, lợi nhuận tiềm năng cho một cuộc tấn công là rất lớn.

Contrarian
Nhưng trước khi bạn vội kết luận rằng đây là một dự án lừa đảo hoàn toàn, tôi phải nói: phần lớn các nền tảng cá cược on-chain khác đều có cơ chế xác thực chặt chẽ hơn. GoalKick chỉ là một ngoại lệ tồi tệ. Polymarket, chẳng hạn, sử dụng hệ thống “báo cáo viên” với cơ chế thử thách dựa trên token UMA. Azuro có nhiều lớp xác thực. Vấn đề là GoalKick đã cố gắng đơn giản hóa quá mức, hy sinh bảo mật để đạt tốc độ triển khai. Và phe bò có thể lập luận: “chỉ có một lỗ hổng nhỏ, chúng tôi sẽ vá ngay.” Đúng, họ có thể. Nhưng thực tế là lỗ hổng tồn tại từ khi ra mắt, và team đã không phát hiện ra nó trong 4 tháng. Ai đảm bảo rằng không có lỗ hổng nào khác?
May mắn thay, chưa có ai khai thác. Dữ liệu on-chain cho thấy số giao dịch resolveMarket chỉ đến từ 3 địa chỉ hợp lệ (các oracle thật) trong suốt lịch sử. Nhưng điều đó không có nghĩa là an toàn. Nó có nghĩa là chưa có kẻ xấu nào đọc mã nguồn giống tôi.
Data nói — tôi chỉ dịch.
Takeaway
Cú chuyển nhượng 200 triệu USD của Chelsea là cơn gió mới cho thị trường cá cược crypto. Nhưng nếu cơ sở hạ tầng bên dưới dễ vỡ như GoalKack, thì cơn gió ấy có thể cuốn đi tiền của người dùng nhanh hơn bất kỳ trận thua nào. Tôi đã gửi báo cáo cho team GoalKack qua Discord. Họ trả lời: “Cảm ơn, chúng tôi sẽ deploy bản vá trong 72 giờ.” Tôi sẽ theo dõi. Câu hỏi dành cho bạn, người đọc: Bạn có sẵn sàng đặt cược vào một cái máy mà ai cũng có thể vặn được kim? Tôi thì không — chừng nào tôi còn chưa kiểm tra tất cả các ốc vít.