Vào ngày 12 tháng 2 năm 2025, giao thức tài chính zkLend đã bị khai thác, dẫn đến mất khoảng 9,5 triệu USD.
zkLend là một giao thức cho vay phi tập trung hoạt động trên chuỗi Starknet. Nó cho phép người dùng cho vay và vay tài sản, tương tự như các giao thức như Aave hoặc Compound.
Cuộc tấn công nhắm vào hợp đồng zkLend Market (cụ thể là thị trường wstETH), đây là hợp đồng chính cho phép người dùng cho vay và vay tài sản.
Thông tin chính
Địa chỉ của kẻ tấn công: https://voyager.online/contract/0x04d7191dc8eac499bac710dd368706e3ce76c9945da52535de770d06ce7d3b26
Hợp đồng dễ bị tấn công (zkLend Market): https://voyager.online/contract/0x04c0a5193d58f74fbace4b74dcf65481e734ed1714121bdc571da345540efa05
Giao dịch tấn công mẫu: https://voyager.online/tx/0x596bb905f74b545ca5a2af39c5724d952e43ef9887af3f6fd603eebfcc9c2a
Phân tích lỗ hổng
Nhìn vào địa chỉ của kẻ tấn công, chúng ta có thể thấy rằng đã có nhiều giao dịch diễn ra trong thời gian xảy ra cuộc tấn công.
Để hiểu cuộc tấn công, trước tiên chúng ta cần xem xét giao dịch gọi runwithfirstdep, vì nó dường như là quan trọng nhất.
Trong giao dịch runwithfirstdep, chúng ta có thể thấy nhiều lệnh deposit và withdraw liên tiếp đến hợp đồng zkLend Market. Hãy xem xét chi tiết của một lệnh deposit trước.
Rõ ràng là số lượng trong các lệnh deposit và withdraw không giống nhau, cho thấy đây không phải là một giao dịch gửi và rút đơn giản. Ở đây, chúng ta thấy rằng kẻ tấn công chỉ gửi 4.069 wstETH nhưng rút 6.103 wstETH. Do đó, chúng ta cần xem xét mã của các hàm deposit và withdraw để hiểu cuộc tấn công.
Đi qua mã của hàm deposit, chúng ta có thể thấy rằng nó nhận amount của token đầu vào và đúc một lượng tương ứng của zToken. Điều tương tự cũng áp dụng cho hàm withdraw. Tuy nhiên, tại sao kẻ tấn công có thể đốt nhiều zToken hơn họ nên làm? Điều này dẫn chúng ta đến việc xem xét cách thực hiện của hàm burn trong hợp đồng zToken.
Từ cách thực hiện của hàm burn, chúng ta có thể thấy rằng số lượng thực tế bị đốt là scaledDownAmount. Ngoài ra, bằng cách xem xét hàm safe_math::sub(), chúng ta có thể xác định rằng công thức tính scaledDownAmount là amount * SCALE / accumulator.
Vì vậy, cách dễ nhất để kiểm tra giá trị này là kiểm tra các giá trị này từ giao dịch tấn công. Và chúng ta có các giá trị sau:
amount:
6103946859077466029(lệnh rút)accumulator:
4069297906051644020000000000000000000000000000SCALE:
1000000000000000000000000000
Từ những giá trị này, chúng ta có thể tính scaledDownAmount như sau:
scaledDownAmount = amount * SCALE / accumulator
= 6103946859077466029 * 1000000000000000000000000000 / 4069297906051644020000000000000000000000000000
= 1
Vì vậy, bây giờ rõ ràng rằng giá trị này đã được kẻ tấn công lựa chọn cẩn thận để vượt qua kiểm tra của scaled_down_amount.is_non_zero(). Giao dịch deposit trước giao dịch runwithfirstdep được sử dụng để thao túng kết quả của hàm get_lending_accumulator() để làm cho giá trị này lớn hơn nhiều và hiệu quả làm cho scaled_down_amount trở thành 1.
Phân tích sau cuộc tấn công
Sau cuộc tấn công, số tiền bị đánh cắp đã được chuyển từ Starknet sang chuỗi Ethereum. Sau đó, kẻ tấn công đã cố gắng rửa tiền bằng cách gửi chúng vào các bể Railgun, nhưng đã bị ngăn chặn bởi các chính sách của giao thức. Việc cố gắng rửa tiền thất bại này nhấn mạnh tầm quan trọng của việc cân bằng giữa quyền riêng tư và minh bạch trong không gian DeFi.
Bài học kinh nghiệm
Sự cố này nhấn mạnh tầm quan trọng của việc kiểm tra cẩn thận các giá trị đầu vào trong các hợp đồng thông minh, đặc biệt là khi xử lý các hoạt động tài chính phức tạp và nhạy cảm. Bằng cách học hỏi từ các sự cố như thế này, cộng đồng DeFi có thể xây dựng các hệ thống bền vững hơn và bảo vệ tốt hơn các khoản tiền của người dùng.











