“Phantom” trong Hợp đồng thông minh của bạn

Bài viết này được dịch máy
Xem bản gốc

Hợp đồng thông minh được mã hóa Solidity là một lớp đóng gói các phương thức bao gồm chức năng và logic được xác định. Việc gọi một chức năng không tồn tại trong hợp đồng thông minh thường dẫn đến giao dịch bị hoàn nguyên. Tuy nhiên, Solidity có một tính năng được gọi là " dự phòng ", có nghĩa là bất kỳ lệnh gọi hàm nào đến hợp đồng không khớp với tên hàm phù hợp sẽ bị dự phòng bắt giữ . Mặc dù điều này mang lại mục đích hữu ích nhưng nó cũng mang đến mối nguy hiểm. Hãy xem tại sao việc cẩn thận khi sử dụng dự phòng lại quan trọng trong phần tiếp theo.

Lịch sử “dự phòng”

Trước khi phát hành phiên bản 0.6.0 , Solidity có cú pháp dự phòng sau:

 function() public payable{}

Phương thức trên xử lý mọi "lệnh gọi" gửi mã thông báo gốc (ETH, BNB, ETC) đến hợp đồng. Nó cũng đóng vai trò là người nhận các lệnh gọi hàm không được chỉ định trong hợp đồng. Có lẽ nhóm Ethereum/ Solidity đã xác định được sự cố, dẫn đến việc đưa ra những Thay đổi đột phá trong phiên bản 0.6.0 . Điều này đòi hỏi phải thay đổi cú pháp cũng như tách hai hàm với các chức năng sau:

  • receive() external payable : được kích hoạt khi dữ liệu cuộc gọi trống. Mục đích của nó là xử lý việc nhận mã thông báo gốc được gửi đến hợp đồng thông minh.

  • fallback() external payable : được kích hoạt bất cứ khi nào không tìm thấy chức năng phù hợp. Nó đóng vai trò thay thế cho dự phòng cũ và có thể không được thanh toán nếu không có ý định nhận mã thông báo gốc.

Rủi ro “dự phòng” & Nghiên cứu điển hình

Chức năng dự phòng là một tính năng mạnh mẽ thường được sử dụng trong Mẫu proxy. Tuy nhiên, sự hiện diện của dự phòng trong hợp đồng có thể gây ra rủi ro đáng kể trong một số trường hợp nhất định, có khả năng dẫn đến tổn thất tài chính trực tiếp cho người dùng.

Để minh họa, hãy xem xét tình huống trong đó Hợp đồng A chứa một dự phòng và Hợp đồng B không liên quan từ một bên khác tương tác với Hợp đồng A. Khi Hợp đồng B gọi một chức năng mà Hợp đồng A không xác định, dự phòng sẽ được kích hoạt và giao dịch không hoàn nguyên . Điều này tạo ra một kịch bản trong đó người gọi bằng cách nào đó đã "bỏ qua" logic nếu Hợp đồng B gọi A với chức năng không tồn tại.

Mặc dù có vẻ như phải thỏa mãn nhiều điều kiện để điều này xảy ra, nhưng các ví dụ thực tế về các cuộc tấn công như vậy đã gây ra hậu quả nghiêm trọng. Khám phá chi tiết cụ thể của hai nghiên cứu điển hình dưới đây để tìm hiểu lý do tại sao dự phòng có thể nguy hiểm và ý nghĩa của nó đối với vấn đề bảo mật.

Multiswap (anyswap) - lỗ hổng kiểm soát truy cập

Hợp đồng Bộ định tuyến Multiswap (trước V6), có các chức năng được gọi là " giấy phép " đóng vai trò sử dụng quyền được ủy quyền để kết nối hoặc trao đổi mã thông báo thay mặt cho người khác. Ví dụ:

 function anySwapOutUnderlyingWithPermit(address from,address token,address to,uint amount,uint deadline,uint8 v,bytes32 r,bytes32 s,uint toChainID) external {address _underlying = AnyswapV1ERC20(token).underlying();IERC20(_underlying).permit(from, address(this), amount, deadline, v, r, s);TransferHelper.safeTransferFrom(_underlying, from, token, amount);AnyswapV1ERC20(token).depositVault(amount, from);_anySwapOut(from, token, to, amount, toChainID);}

Hàm anySwapOutUnderlyingWithPermit gọi hàm permit của Token cơ bản để ủy quyền chuyển giao cho hợp đồng. Sau đó, hợp đồng gọi transferFrom để chuyển tiền và thủ tục bắc cầu sẽ chuyển tiền sang một blockchain khác.

Mặc dù đoạn mã trên có vẻ bình thường nhưng nó sẽ có tính chất bất thường nếu hợp đồng của Token cơ bản chứa dự phòng. Một ví dụ cổ điển về Token luôn có dự phòng là mã thông báo gốc được bao bọc (WETH, WBNB, WAVAX, ETC). Đây là một kịch bản tấn công tiềm năng:

  • Nạn nhân Alice đã phê duyệt 10 WETH cho Bộ định tuyến.

  • Kẻ tấn công Bob thực thi hàm anySwapOutUnderlyingWithPermit với số lượng 10 WETH và các giá trị v , r , s tùy ý.

  • Hợp đồng thực hiện chức năng permit với mục đích ủy quyền. Tuy nhiên, tại thời điểm này, Token là WETH, chứa mã dự phòng. Không có logic nào để kiểm tra việc ủy ​​quyền nào đang diễn ra và cuộc gọi không được hoàn nguyên. Kết quả là, hợp đồng tiến hành chuyển 10 WETH từ Alice sang Bob .

Cuối cùng, Bob đã thành công trong việc đánh cắp 10 WETH từ Alice vì hợp đồng Bộ định tuyến thiếu những hạn chế mạnh mẽ khi gọi một hàm trong một hợp đồng khác.

Cuộc tấn công đã gây ra tổn thất tài chính lớn, với tổng thiệt hại là 1,44 triệu USD. Đây là một khoản tiền đáng kể và là bài học quan trọng đối với nhiều nhà phát triển dự án, lập trình viên và kiểm toán viên bảo mật.

Tùy tiện làm mất Token của hợp đồng khác thông qua flashloan

Lỗ hổng này được phát hiện bởi các kiểm toán viên trong Code4rena . Tôi sẽ tái tạo lại hợp đồng dễ bị tổn thương theo cách đơn giản hóa cùng với một Script để chứng minh nó có thể dẫn đến tổn thất tài chính trong các hợp đồng khác như thế nào.

 interface IFlashBorrower {function onFlashLoan(uint256, bytes) external;}contract VulnerableContract {IERC20 tokenX = 0x001234;function flashloan(IFlashBorrower receiver, uint256 amount, bytes calldata data) public payable {uint256 fee = amnount * 50 / 1_000;tokenX.mint(address(receiver), amount);receiver.onFlashLoan(shareAmount, data);tokenX.burn(address(receiver), amount + fee);emit Flashloaned(receiver, eusdAmount, burnShare);}}

Người gọi hàm flashloan có toàn quyền kiểm soát receiver là ai. Không có logic xác thực nào trong hàm này và nó tiến hành mintburn tokenX trong khi tính phí cho người nhận. Nếu người nhận là một hợp đồng có chức năng dự phòng không hoàn nguyên, tokenX trong hợp đồng sẽ bị đốt cháy, dẫn đến tổn thất tương đương với khoản phí. Kịch bản tấn công:

  • Nạn nhân là một hợp đồng A được triển khai thông qua GnosisSafeProxy , chứa hàm dự phòng không hoàn nguyên khi gọi một hàm không tồn tại. Người dùng lưu trữ 100 tokenX trong hợp đồng.

  • Kẻ tấn công gọi hàm flashloan với bộ thu được đặt thành hợp đồng A .

  • Ở giai đoạn này, logic chạy mà không gặp bất kỳ ràng buộc nào có thể khiến nó hoàn nguyên. Sau khi giao dịch thành công, hợp đồng A sẽ bị đốt 5 token , đây là khoản phí trong Khoản vay nhanh của kẻ tấn công. Đây là hành vi vi phạm lớn về bảo vệ người dùng khi tham gia vào sản phẩm.

Phần kết luận

Với hai trường hợp nghiên cứu ở trên, việc có chức năng dự phòng bên trong hợp đồng cũng tương tự như một bóng ma ẩn có thể gây tác động đến người dùng hợp đồng. Nguồn gây tổn thất trực tiếp cho người dùng không phải là bản thân hợp đồng mà là hợp đồng của bên thứ ba sử dụng hợp đồng đó. Tuy nhiên, với tư cách là nhà phát triển, bạn phải luôn chịu trách nhiệm về sự an toàn của người dùng trong mọi tình huống.

Đối với các hợp đồng chứa các hàm dự phòng, điều quan trọng là phải luôn kiểm tra logic và hoàn nguyên ngay lập tức nếu có bất kỳ hành vi không hợp lệ nào trong dữ liệu.

Khi sử dụng hợp đồng bên thứ ba, bạn không bao giờ có thể dự đoán toàn bộ logic bên trong của nó. Luôn xác thực các giá trị trả về và không bao giờ cho phép người dùng kiểm soát dữ liệu quan trọng được truyền tới nó.

Nhóm Verichains cập nhật liên tục các lỗ hổng mới nhất được xác định trong các dự án mà họ đã kiểm tra và những dự án hiện đang được kiểm toán, cũng như thông tin từ cộng đồng bảo mật blockchain.

Cảm ơn đã đọc Verichains! Đăng ký miễn phí để nhận bài viết mới và hỗ trợ công việc của tôi.

Giới thiệu về Verichain

Verichains là công ty bảo mật blockchain hàng đầu chuyên kiểm tra mã, phân tích mật mã, bảo mật chu vi và điều tra sự cố. Được thành lập vào năm 2017 bởi các nhà nghiên cứu bảo mật đẳng cấp thế giới, công ty tận dụng chuyên môn sâu rộng về bảo mật, mật mã và công nghệ chuỗi khối cốt lõi, đồng thời đã giúp điều tra và khắc phục các vấn đề bảo mật trong các vụ hack tiền điện tử lớn nhất, bao gồm Cầu BNB và Cầu Ronin . Nếu có bất kỳ thắc mắc hoặc thắc mắc nào, vui lòng liên hệ với chúng tôi tại info@verichains.io

Nguồn
Tuyên bố từ chối trách nhiệm: Nội dung trên chỉ là ý kiến của tác giả, không đại diện cho bất kỳ lập trường nào của Followin, không nhằm mục đích và sẽ không được hiểu hay hiểu là lời khuyên đầu tư từ Followin.
Thích
Thêm vào Yêu thích
Bình luận