

Vào ngày 27 tháng 3 năm 2022, Revest Finance, một dự án đặt cược DeFi trên Ethereum, đã bị hack và mất khoảng 2 triệu USD. Nhóm BlockSecTeam đã ngay lập tức can thiệp vào phân tích và chia sẻ kết quả phân tích của chúng tôi với cộng đồng trên tweeter. Trên thực tế, khi chúng tôi chia sẻ kết quả phân tích của mình với cộng đồng thông qua tweeter, chúng tôi đã tìm thấy một lỗ hổng zero-day nghiêm trọng trong hợp đồng TokenVault của Revest Finance. Sử dụng lỗ hổng này, kẻ tấn công có thể đánh cắp tài sản trong giao thức theo cách đơn giản hơn. Vì vậy, chúng tôi đã ngay lập tức liên hệ với bên dự án Revest Finance. Sau khi xác nhận rằng lỗ hổng đã được khắc phục, chúng tôi quyết định chia sẻ blog này với cộng đồng.
0. What's the Revest Finance FNFT
tiêu đề phụ
Revest Finance là một giải pháp để đặt cược trong lĩnh vực DeFi. Bất kỳ khoản đặt cược DeFi nào mà người dùng tham gia thông qua Revest Finance đều có thể trực tiếp tạo ra một NFT, cụ thể là FNFT (Mã thông báo không thể thay thế tài chính), đại diện cho trạng thái hiện tại và hiện tại của vị trí đặt cược. giá trị tương lai. Người dùng có thể tương tác với các dự án thông qua 3 giao diện do Revest Finance cung cấp. Cầm cố tài sản kỹ thuật số của riêng bạn, đúc ra FNFT tương ứng.
•mintTimeLock : Tài sản kỹ thuật số được người dùng cam kết chỉ có thể được mở khóa sau một khoảng thời gian.
•mintValueLock : Tài sản kỹ thuật số được người dùng cam kết chỉ có thể được mở khóa khi chúng đánh giá cao hoặc giảm giá trị theo giá trị đặt trước.
•mintAddressLock : Tài sản kỹ thuật số được người dùng cam kết chỉ có thể được mở khóa bằng tài khoản đặt trước.
Revest Finance hoàn thành việc khóa và mở khóa tài sản kỹ thuật số do người dùng ký gửi thông qua ba hợp đồng thông minh sau đây.
•FNFTHandler: Kế thừa từ mã thông báo ERC-1155 (triển khai openzepplin). Mỗi khi thao tác khóa được thực thi, fnftId sẽ được tăng lên (fnftId tương tự như tokenId trong ERC721). Khi FNFT được tạo, người dùng cần chỉ định tổng nguồn cung của nó. Khi người dùng muốn rút tài sản cơ bản đằng sau FNFT, họ cần đốt một tỷ lệ FNTF tương ứng.
•LockManage : Ghi lại các điều kiện để FNFT được mở khóa (mở khóa).
•TokenVault: Nhận và gửi tài sản cơ bản do người dùng gửi và ghi lại siêu dữ liệu của từng FNFT. Ví dụ: loại tài sản được cầm cố đằng sau FNFT với fnftId = 1.
Do cuộc tấn công này, điểm vào của cuộc tấn công của tin tặc là hàm mintAddressLock, vì vậy, hãy lấy hàm này làm ví dụ để mô tả vòng đời của FNFT.
•unlocker : User X ->Người dùng A gọi hàm mintAddressLock của Revest
Chỉ Người dùng X mới có thể mở khóa nội dung này •người nhận: [Người dùng A , Người dùng B , Người dùng C] •số lượng: [50 , 25 , 25] -> số lượng đúc là 100 (tổng (số lượng)) , Người dùng A , Người dùng B , Người dùng C mỗi người sở hữu 50, 25 và 25 xu. •tài sản : WETH -> FNFT từ bạc hà lấy WETH làm tài sản thế chấp. • DepositAmount : 1e18 -> Số lượng tài sản thế chấp đằng sau mỗi FNFT là 1 WETH ( WETH thập phân là 18 )
Giả sử rằng không có FNFT nào khác trong hệ thống hiện tại, Người dùng A tương tác với hệ thống thông qua mintAddressLock và fnftId được trả về bởi FNFTHandler = 1
•fnftId : 1•unlocker : User X
LockManger thêm các bản ghi tương ứng cho nó
•fnftId : 1•asset : WETH•depoistAmount : 1e18
Token Vault thêm các bản ghi tương ứng cho nó
Sau đó, Token Valut cần chuyển 100 * 1e18 WETH từ Người dùng A.
Cuối cùng, hệ thống cung cấp cho Người dùng A, Người dùng B và Người dùng C đúc lần lượt 50, 25 và 25 mẩu 01-FNFT.
Điều này được thực hiện bằng cách đúc FNFT thông qua hàm mintAddressLock.
Sau khi Người dùng X mở khóa 01-FNFT, Người dùng B có thể rút tài sản cơ bản thông qua rútFNFT. Như được hiển thị trong Hình 2, Người dùng B muốn rút 25 tài sản kỹ thuật số được cầm cố bởi 01-FNFT trong tay của mình.
Trước tiên, giao thức sẽ kiểm tra xem 01-FNTF đã được mở khóa chưa. Nếu nó đã được mở khóa, giao thức sẽ ghi 25 01-FNFT của Người dùng B và chuyển 25*1e18 WETH cho anh ta. Tại thời điểm này, tổng Nguồn cung của 01-FNFT là 75.
Hợp đồng Revest cũng cung cấp một giao diện khác có tên là DepositAdditionalToFNFT để cho phép người dùng thêm nhiều tài sản cơ bản hơn vào một FNFT hiện có. Dưới đây chúng tôi mô tả cách sử dụng "bình thường" của nó bằng 2 hình ảnh.
Có ba trường hợp ở đây
1.quantity == 01-FNFT.totalSupply() như trong Hình 3
•quantity = 75 ->Lấy tình huống trong Hình 2 làm bối cảnh, Người dùng A muốn thêm nhiều tài sản thế chấp cho 01-FNFT.
•amount = 0.5*1e18 ->Cam kết bổ sung cho 75 01-FNFT.
Mỗi 01-FNFT sẽ thêm 0,5*1e18 WETH.
Vì vậy, Người dùng A cần chuyển 37,5*1e18 WETH (75 * 0,5*1e18) vào Token Vault. Token Vault sửa đổi kế toán hệ thống và thay đổi Số tiền gửi thành 1,5*1e18. Giờ đây, mỗi 01-FNFT mang tài sản 1,5 * 1e18 WETH.
Tại thời điểm này, Người dùng C gọi rút tiềnFNFT để tiêu hủy 25 01-FNFT mà anh ấy nắm giữ và anh ấy có thể lấy đi 25*(1,5*1e18) = 37,5*1e18 WETH.
Do đó, tổng Nguồn cung của 01-FNFT là 50 vào lúc này.
2.quantity < 01-FNFT.totalSupply() như trong Hình 4
•quantity = 10 ->Lấy bối cảnh trong Hình 3 làm bối cảnh, Người dùng A tiếp tục thêm nhiều tài sản thế chấp cho 01-FNFT.
Cam kết bổ sung cho 10 01-FNFT. •số tiền = 0,5*1e18 -> Thêm 0,5*1e18 WETH vào mỗi trong số 10 01-FNFT< 01-FNFT.totalSupply()
do số lượng•fnftId : 2•asset : WETH•depositAmount : 2.0*1e18 (1.5*1e18 + 0.5*1e18)
Do đó, Người dùng A thanh toán 5*1e18 WETH cho thỏa thuận. Hệ thống sẽ ghi 10 01-FNFT, đúc 10 02-FNFT và ghi các tài sản được mang theo bởi 10 01-FNFT và Tài sản mới được chuyển của Người dùng A, được đưa vào 02-FNFT . Vì vậy, có
tại thời điểm này
• 01-FNFT.totalSupply : 40 01-FNFT.depositAmount : 1,5*1e18 (về mặt logic, nó phải như vậy, xem bên dưới: Lỗ hổng Zero-day mới) • 02-FNFT.totalSupply : 10 02-FNFT.depositAmount : 2,0* 1e18
Trong trường hợp này, giao dịch sẽ hoàn nguyên.
1. What't the Re-entrancy vulnerability
tiêu đề phụ
Sau khi hiểu quy trình làm việc cơ bản của hàm mintAddressLock và hàm DepositAdditionalToFNFT, chúng ta hãy xem các phương thức vào lại được kẻ tấn công sử dụng. Giả sử fnftId mới nhất = 1 (không ảnh hưởng đến việc hiểu)
Như thể hiện trong Hình 5
bước đầu tiên:
•depositAmount = 0
•quantities = [2]
Kẻ tấn công gọi hàm mintAddressLock
Mint đã phát hành 2 mẩu 01-FNFT, vì kẻ tấn công đã đặt số tiền gửi là 0, vì vậy hắn không chuyển bất kỳ tài sản kỹ thuật số nào. Tương đương với tài sản cơ bản được mang theo sau 01-FNFT là 0.
•depositAmount = 0
Bước 2: Kẻ tấn công gọi lại hàm mintAddressLock
•số lượng = [360000] Chuẩn bị tiền đúc 36w miếng 02-Số tiền đặt cọc FNFT là 0.
Trong bước đúc cuối cùng, kẻ tấn công đã sử dụng cơ chế gọi lại ERC-1155 để nhập lại chức năng DepositAdditionalToFNFT. (Xem chức năng _doSafeTransferAcceptanceCheck được cung cấp bên dưới để biết chi tiết)
•quantity = 1
•amount = 1*1e18
•fnftId = 1
Trong DepositAdditionalToFNFT, kẻ tấn công chuyển vào< fntfId.totalSupply(),vì số lượng
Cuối cùng, kẻ tấn công gọi chức năng rút tiềnNFNFT, đốt cháy 360.001 02-FNFT và lấy đi 360.001*1e18 RENA.
sửa chữa được đề xuất
2. the New Zero-day Vulnerability
tiêu đề phụ
Trong khi nhóm blockSecTeam đang phân tích mã của Revest Finance, hàm handleMultipleDeposits đã thu hút sự chú ý của chúng tôi.
Khi người dùng gọi hàm DepositAdditionToNFT để thêm tài sản thế chấp, hàm này sẽ thay đổi số tiền ký gửi của FNFT. Từ đoạn mã này, chúng ta có thể thấy rằng khi newFNFTId != 0, hàm này không chỉ thay đổi DepositAmount của FNFT tương ứng với fnftId, mà còn thay đổi DepositAmount tương ứng với newFNFTId.
Theo lẽ thường, khi newFNFTId !=0, hệ thống chỉ nên ghi lại DepositAmount tương ứng với newNFTId. Không nên thay đổi số tiền gửi tương ứng với fnftId.
Chúng tôi cho rằng đây là một lỗi logic rất nghiêm trọng. Bằng cách khai thác lỗ hổng này, kẻ tấn công có thể dễ dàng rút tài sản kỹ thuật số khỏi hệ thống. Ba hình ảnh sau đây mô tả nguyên tắc của cuộc tấn công mô phỏng.
Giả sử fnftId mới nhất = 1
Đầu tiên, kẻ tấn công gọi hàm mintAddressLock và tạo ra 360.000 01-FNFT. Kẻ tấn công đặt số tiền thành 0 để anh ta không phải chuyển bất kỳ tài sản nào vào giao thức Revest Finance.
Sau khi đúc kết thúc, kẻ tấn công có 360000 01-FNFT với số tiền gửi = 0.
•fnftId = 1
•amount = 1 * 1e18
•quantity = 1
Sau đó, kẻ tấn công gọi hàm DepositAdditionalToFNFT với các tham số sau
Giao thức chuyển số lượng kẻ tấn công * số lượng mã thông báo, nghĩa là 1 * 1e18RENA
Giao thức sẽ ghi một 01-FNFT của kẻ tấn công và đúc một 02-FNFT cho kẻ tấn công (giả sử fnftId mới nhất = 2)
Theo logic trong hàm handleMultipleDeposits, nội dung có fnftId = 2 sẽ có số tiền gửi được đặt thành 1.0*1e18.
Nhưng đối với nội dung có fnftId = 1, số tiền gửi của chúng cũng sẽ được đặt thành 1,0*1e18, giá trị này phải là 0!
Rõ ràng, sử dụng phương pháp này để tấn công đơn giản và trực tiếp hơn so với các cuộc tấn công vào lại thực sự.
chữ
Để đối phó với lỗ hổng này, nhóm blockSecTeam đã cung cấp một phương pháp vá lỗi tương ứng.
tiêu đề phụ
Do hai hợp đồng dễ bị tấn công là TokenVault và FNFTHandler lưu trữ nhiều trạng thái chính nên không thể triển khai lại chúng trong thời gian ngắn. Để nhanh chóng tiếp tục sử dụng, Revest Finance đã chính thức triển khai lại hợp đồng Revest (https://etherscan.io/address/ 0x36c2732f1b2ed69cf17133ab01f2876b614a2f27# mã). Phiên bản này tắt hầu hết các chức năng phức tạp để tránh các cuộc tấn công tiếp theo. Bên dự án sẽ di chuyển trạng thái và triển khai lại hợp đồng cố định trong tương lai.
tiêu đề phụ
Cải thiện tính bảo mật của các dự án DeFi không phải là một nhiệm vụ dễ dàng. Ngoài việc kiểm tra mã, chúng tôi tin rằng cộng đồng nên áp dụng các phương pháp chủ động hơn, chẳng hạn như giám sát dự án và cảnh báo sớm, thậm chí ngăn chặn tấn công để giúp cộng đồng DeFi an toàn hơn. (https://mp.weixin.qq.com/s/o41Da2PJtu7LEcam9eyCeQ).
người giới thiệu
*[1]: https://blocksecteam.medium.com/revest-finance-vulnerabilities-more-than-re-entrancy-1609957b742f
