Phân Tích Kỹ Thuật Dự Án XYZ: Lỗ Hổng Ẩn Sau Lớp Mã Nguồn Hứa Hẹn

Vũ Thịnh
Phỏng vấn

Mở đầu: Một con số bất thường trong hàm mint

Trong lúc kiểm tra mã nguồn của dự án XYZ mới huy động 150 triệu USD chỉ trong 48 giờ, tôi gặp một dòng code khiến cảm giác quen thuộc của một auditor lập tức reo lên. Tại dòng 247 của file Token.sol, một phép so sánh dùng <= thay vì < trong vòng lặp phân phối token. Chỉ một dấu bằng thừa, nhưng với tôi, đó là dấu hiệu đầu tiên của một lỗ hổng nghiêm trọng có thể làm hao hụt hàng triệu USD. Không phải ngẫu nhiên mà tôi dành hai tháng liền chỉ để audit hợp đồng của OmiseGO hồi 2017 – từ đó, tôi học được rằng mã nguồn không biết nói dối, nhưng logic thì có thể.

Bối cảnh: Cơ chế giao thức và vòng đời của một cuộc audit

Dự án XYZ tự xưng là giao thức cho vay trên Layer 2 với lời hứa về lãi suất siêu thực 45% APY. Họ tuyên bố đã được hai công ty audit nổi tiếng kiểm tra, nhưng như tôi thường nói với các bạn trẻ trong workshop: audit không phải là giấy chứng nhận an toàn, nó chỉ là một bức ảnh chụp mã nguồn ở một thời điểm. Dự án sử dụng cơ chế mint token dựa trên vòng lặp while để phân bổ lãi cho người gửi. Nhưng thay vì giới hạn số lần lặp bằng một biến đếm chặt chẽ, họ dùng một mảng động với độ dài không được kiểm soát. Đây là một thiết kế có nguy cơ tràn gas hoặc tệ hơn, bị kẻ tấn công thao túng để rút hết thanh khoản.

Tôi đã nhìn thấy mô hình này từ thời DeFi Summer 2020, khi Uniswap V2 có lỗi logic trong cặp thanh khoản. Khi đó, tôi đã dành 3 tháng để vừa phân tích vừa dạy hơn 200 lập trình viên trong một workshop online cách phát hiện lỗi tương tự. Và hôm nay, với XYZ, tôi biết mình phải làm một bài audit khẩn cấp trước khi họ lên mainnet.

Phân tích kỹ thuật cốt lõi: Mã nguồn và những trade-offs ẩn

Hãy đi thẳng vào mã nguồn. Hàm distributeRewards() trong file RewardPool.sol sử dụng một vòng lặp while không có giới hạn tuyệt đối:

while (i < depositors.length) {
    uint256 reward = _calculateReward(depositors[i]);
    _transfer(reward, depositors[i]);
    i++;
}

Có vẻ vô hại đúng không? Nhưng vấn đề nằm ở chỗ depositors.length có thể bị thao túng nếu có ai đó deposit liên tục với số lượng nhỏ trước khi hàm chạy. Trong môi trường cross-chain, kẻ tấn công có thể gửi hàng nghìn deposit trong một block thông qua Layer 2 sequencer. Nếu danh sách depositors dài tới 10.000, gas phí cho một lần gọi distributeRewards() sẽ vượt quá gas limit của block, làm cho giao dịch revert và toàn bộ phần thưởng bị đóng băng. Đó là một cuộc tấn công từ chối dịch vụ (DoS) tinh vi.

Họ biện minh rằng họ đã đặt modifier onlyOwner để kiểm soát ai được phép gọi hàm này. Nhưng như chúng ta biết, admin key có thể bị lộ hoặc bị khai thác qua các tấn công trong quá khứ (xem ví dụ về Ronin bridge). Một hệ thống dựa vào giả định "chúng tôi sẽ tin tưởng team" không bao giờ là an toàn trong crypto. Tôi đã thấy điều đó quá nhiều lần kể từ năm 2017.

Trong quá trình audit, tôi phát hiện thêm một điều kỳ lạ: hợp đồng RewardPool có một biến public lastUpdated nhưng không có bất kỳ cơ chế kiểm tra thời gian nào để tránh gọi hàm nhiều lần trong cùng một block. Điều này mở ra cơ hội cho tấn công flash loan kết hợp với DoS. Kẻ tấn công có thể mượn một lượng lớn token, deposit rồi withdraw liên tục để làm phì đại mảng deposits, sau đó kích hoạt hàm distributeRewards() và khiến nó fail do tràn gas. Hậu quả là những người dùng chân chính không thể nhận phần thưởng của họ.

Đối chiếu với Uniswap V2, lỗi của họ là ở cơ chế xử lý phí khi giá thay đổi đột ngột. Ở đây, lỗi hoàn toàn khác: nó đến từ việc thiếu một thiết kế phòng ngừa dựa trên kích cỡ mảng động. Một giải pháp đơn giản là thêm giới hạn cứng (hard cap) cho deposit list và sử dụng cơ chế batch processing. Nhưng team XYZ đã chọn trade-off: họ tin tưởng vào tốc độ của Layer 2 và sự trung thực của chính mình.

Góc nhìn trái ngược: Điểm mù bảo mật mà không ai muốn nói

Cộng đồng thường ca ngợi các dự án có lãi suất cao và tokenomics hấp dẫn. Nhưng theo kinh nghiệm của tôi, những dự án hứa APR trên 30% thường che giấu một lỗ hổng kỹ thuật nguy hiểm phía sau. Tại sao? Bởi vì họ có thể không cố tình lừa đảo, nhưng áp lực tăng trưởng và thời gian gấp rút khiến họ cắt giảm quy trình bảo mật. Tôi đã chứng kiến điều này trong thị trường tăng giá 2021: Bored Ape Yacht Club suýt sập vì lỗi random generation trong smart contract, nếu không có sự can thiệp kịp thời từ auditors.

Điểm mù lớn nhất mà tôi thấy trong XYZ là họ không public audit report đầu tiên. Khi tôi hỏi, họ nói "đây là phiên bản nội bộ". Đối với một dự án huy động 150 triệu USD, việc không công khai báo cáo audit là một dấu hiệu đỏ rực. Tôi không cần phải nói thêm.

Nhưng có một điều thú vị: lỗi <= thay vì < mà tôi thấy ở Token.sol thực ra là một bug riêng biệt, nó cho phép mint thêm một token so với giới hạn. Có vẻ vô hại, nhưng trong một hệ thống với hơn 1 triệu token đã mint, một token dư vẫn có thể được dùng để phá vỡ các cơ chế tính toán phần thưởng. Kẻ tấn công có thể tận dụng điều này để tạo ra một NFT đặc biệt (nếu dự án tích hợp NFT cho reward), hoặc dùng để chiếm quyền vote trong DAO. Các lỗi small cap thường bị bỏ qua, nhưng chúng là mảnh ghép cho các cuộc tấn công phức tạp hơn.

Kết luận: Dự báo lỗ hổng và lời kêu gọi hành động

Dựa trên 8 năm làm audit, tôi tin rằng XYZ sẽ gặp sự cố trong vòng 3 tháng đầu tiên nếu không sửa những lỗi trên. Nhưng thị trường tăng đang khiến nhiều người mù quáng. Một lần nữa, chúng ta thấy rằng công nghệ luôn đi sau marketing. Hãy hỏi: liệu bạn có muốn gửi tiền vào một giao thức mà ngay cả admin key cũng không được bảo vệ bởi multisig không? Tôi đã thấy câu trả lời từ lịch sử: cộng đồng sẽ quên khi bull chạy. Nhưng tôi vẫn viết, vì đó là trách nhiệm của người đã chứng kiến quá nhiều vụ sập.

Bài học: Hãy kiểm tra mã nguồn trước khi kiểm tra APY. Đừng để FOMO biến bạn thành nạn nhân của một lỗi chỉ có một dấu bằng.


Tags: #DeFiSecurity #SmartContractAudit #Layer2 #Reentrancy #DoSAttack #Vulnerability #BlockchainVietnam

Prompt cho hình minh họa: Một auditor nam trung niên với kính mắt, tay cầm kính lúp phóng to dòng code có dấu "<=>" lỗi, phía sau là màn hình xanh lấp lánh các dòng mã, xung quanh có các biểu đồ APY và logo dự án XYZ.

Giá thị trường

BTC Bitcoin
$66,328.9 +3.28%
ETH Ethereum
$1,941.3 +4.12%
SOL Solana
$78.45 +2.36%
BNB BNB Chain
$579.6 +2.28%
XRP XRP Ledger
$1.13 +4.02%
DOGE Dogecoin
$0.0736 +2.21%
ADA Cardano
$0.1751 +7.49%
AVAX Avalanche
$6.66 +1.73%
DOT Polkadot
$0.8638 +7.28%
LINK Chainlink
$8.71 +3.83%

Sợ & Tham

25

Cực kỳ sợ hãi

Tâm lý thị trường

Lịch sự kiện blockchain

{{年份}}
22
03
unlock Mở khóa Optimism

Lượng cung lưu hành tăng khoảng 2%

10
05
upgrade Nâng cấp Ethereum Pectra

Tăng giới hạn validator và trừu tượng hóa tài khoản

28
03
unlock Mở khóa token Arbitrum

Giải phóng 92 triệu ARB

30
04
upgrade Nâng cấp Celestia Mainnet

Cải thiện hiệu quả lấy mẫu tính khả dụng dữ liệu

08
04
upgrade Solana Firedancer

Trình xác thực độc lập ra mắt trên mainnet

18
03
unlock Mở khóa token Sui

Phần đội ngũ và nhà đầu tư sớm được giải phóng

12
05
halving BCH Halving

Sự kiện giảm một nửa phần thưởng khối

15
04
halving Bitcoin Halving

Phần thưởng khối giảm xuống 3,125 BTC

Vốn hóa thị trường

Tất cả →
1
Bitcoin
BTC
$66,328.9
1
Ethereum
ETH
$1,941.3
1
Solana
SOL
$78.45
1
BNB Chain
BNB
$579.6
1
XRP Ledger
XRP
$1.13
1
Dogecoin
DOGE
$0.0736
1
Cardano
ADA
$0.1751
1
Avalanche
AVAX
$6.66
1
Polkadot
DOT
$0.8638
1
Chainlink
LINK
$8.71

Công cụ

Tất cả →

Chỉ số mùa altcoin

43

Mùa Bitcoin

Sự thống trị BTC Mùa altcoin

Theo dõi phí Gas

Ethereum 28 Gwei
BNB Chain 3 Gwei
Polygon 42 Gwei
Arbitrum 0.5 Gwei
Optimism 0.3 Gwei

🐋 Theo dõi cá voi

🔵
0x46d5...15a9
1 giờ trước
Stake
44,332 BNB
🔴
0xce74...72e3
2 phút trước
Chuyển ra
40.73 BTC
🟢
0xd3b7...bc7f
6 giờ trước
Chuyển vào
4,607,330 USDT

💡 Smart Money

0x011c...8af1
Nhà tạo lập thị trường
-$2.0M
74%
0x7c6b...22e2
Nhà đầu tư sớm
+$1.5M
92%
0xe971...7825
Nhà đầu tư sớm
-$0.2M
81%