Cảnh báo rủi ro: Đề phòng huy động vốn bất hợp pháp dưới danh nghĩa 'tiền điện tử' và 'blockchain'. — Năm cơ quan bao gồm Ủy ban Giám sát Ngân hàng và Bảo hiểm
Tìm kiếm
Đăng nhập
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt
BTC
ETH
HTX
SOL
BNB
Xem thị trường
Lỗ hổng rủi ro cao trong trình biên dịch Solidity: Vô tình xóa gán biến trạng thái
Eocene
特邀专栏作者
2023-05-06 12:49
Bài viết này có khoảng 2022 từ, đọc toàn bộ bài viết mất khoảng 3 phút
Bài viết này giải thích chi tiết trình biên dịch solidity (0.8.13<=solidity<0.8.17) từ mã nguồn, trong quá trình biên dịch do cơ chế tối ưu Yul bị lỗi nên xóa nhầm thao tác gán biến trạng thái tương ứ

Bài viết này giải thích chi tiết trình biên dịch solidity (0.8.13<=solidity<0.8.17) từ mã nguồn, trong quá trình biên dịch do cơ chế tối ưu Yul bị lỗi nên xóa nhầm thao tác gán biến trạng thái tương ứng. các biện pháp phòng ngừa.

tiêu đề cấp đầu tiên

1. Chi tiết lỗ hổng

tài liệu chính thứctài liệu chính thức

Trong bước tối ưu hóa UnusedStoreEliminator của quá trình biên dịch, trình biên dịch sẽ loại bỏ thao tác ghi Lưu trữ "dư thừa", nhưng do lỗi nhận dạng của "dư thừa", khi một khối hàm Yul gọi một hàm cụ thể do người dùng xác định (hàm Có một nhánh bên trong không ảnh hưởng đến luồng thực thi của khối gọi) và có một thao tác ghi vào cùng một biến trạng thái trước và sau hàm được gọi trong khối chức năng Yul, điều này sẽ khiến hàm do người dùng xác định trong khối bị được gọi bởi cơ chế tối ưu hóa Yul trước đóHoạt động ghi lưu trữ bị xóa vĩnh viễn khỏi cấp độ biên dịch

Hãy xem xét đoạn mã sau:

contract Eocene {

        uint public x;

        function attack() public {

                x = 1;

                x = 2;

        }

}

x=1 rõ ràng là dư thừa cho toàn bộ quá trình thực thi hàm attack() khi UnusedStoreEliminator tối ưu hóa. Đương nhiên mã Yul đã tối ưu sẽ xóa x= 1; để giảm lượng gas tiêu thụ của hợp đồng.

Tiếp theo, hãy xem xét việc chèn một lệnh gọi đến một chức năng tùy chỉnh ở giữa:

contract Eocene {

uint public x;

function attack(uint i) public {

        x = 1;

        y(i);

        x = 2;

}

function y(uint i) internal{

        if (i > 0){

                return;

        }

        assembly { return( 0, 0) }

}

}

Rõ ràng, do lệnh gọi của hàm y(), chúng ta cần đánh giá xem hàm y() có ảnh hưởng đến việc thực thi hàm attack() hay không, nếu hàm y() có thể khiến toàn bộ luồng thực thi hàm bị chấm dứt ( lưu ý rằng nó không phải là một rollback, hàm Yul code return()), thì x= 1 rõ ràng là không thể xóa được, vì vậy đối với hợp đồng trên, sự tồn tại của hội {return(0, 0)} trong hàm y() có thể dẫn đến kết thúc toàn bộ cuộc gọi tin nhắn, x= 1 đương nhiên không thể xóa được.

Nhưng trong trình biên dịch Solidity, do sự cố logic mã, x= 1 đã bị xóa do nhầm lẫn trong quá trình biên dịch, điều này đã thay đổi vĩnh viễn logic mã.

Kết quả kiểm tra biên dịch thực tế như sau:

Sốc! Mã Yul với x=1 không được tối ưu hóa sẽ bị mất! Nếu bạn muốn biết những gì đã xảy ra tiếp theo, xin vui lòng đọc dưới đây.

Trong UnusedStoreEliminator của mã trình biên dịch solidiry, theo dõi biến SSA và theo dõi luồng điều khiển được sử dụng để xác định xem thao tác ghi vào Bộ lưu trữ có dư thừa hay không. Khi nhập chức năng tùy chỉnh, nếu UnusedStoreEliminator gặp:

  1. hoạt động ghi bộ nhớ hoặc lưu trữ: lưu trữ hoạt động ghi bộ nhớ và lưu trữ vào biến m_store và đặt trạng thái ban đầu của hoạt động thành Chưa quyết định;

  2. Gọi hàm: Lấy vị trí hoạt động đọc và ghi của bộ nhớ hoặc bộ lưu trữ của hàm và so sánh nó với tất cả các hoạt động ở trạng thái Chưa quyết định được lưu trữ trong biến m_store:

    1. Nếu đó là thao tác ghi đè lên hoạt động lưu trữ trong m_store, hãy thay đổi trạng thái hoạt động tương ứng trong m_store thành Không sử dụng

    2. Nếu đó là thao tác đọc lưu trữ trong m_store, hãy thay đổi trạng thái hoạt động tương ứng trong m_store tương ứng thành Đã sử dụng

    3. Nếu chức năng không có bất kỳ nhánh nào có thể tiếp tục thực hiện các cuộc gọi tin nhắn, hãy thay đổi tất cả các thao tác ghi bộ nhớ trong m_store thành Không sử dụng

    1. Trong điều kiện khiếu nại, nếu chức năng có thể chấm dứt luồng thực thi, hãy thay đổi thao tác ghi bộ nhớ có trạng thái Chưa quyết định trong m_store thành Đã sử dụng, nếu không, hãy đánh dấu là Chưa sử dụng

  3. Kết thúc chức năng: xóa tất cả các thao tác ghi được đánh dấu là Không sử dụng

Mã khởi tạo để ghi vào bộ nhớ hoặc bộ lưu trữ như sau:

Có thể thấy rằng các hoạt động ghi vào bộ nhớ và lưu trữ gặp phải được lưu trữ trong m_store

Mã logic xử lý khi gặp lệnh gọi hàm như sau:

Trong số đó, operationFromFunctionCall() và applyOperation() triển khai logic xử lý 2.1 và 2.2 của kháng nghị. Câu lệnh If được đánh giá dựa trên hàm canContinue và canTerminate bên dưới thực hiện logic 2.3.

Cần lưu ý rằng chính khiếm khuyết của phán đoán Nếu dưới đây đã dẫn đến sự tồn tại của kẽ hở! ! !

operationFromFunctionCall() để lấy tất cả các hoạt động đọc và ghi bộ nhớ hoặc lưu trữ của hàm này. Cần lưu ý ở đây rằng có nhiều hàm tích hợp sẵn trong Yul, chẳng hạn như sstore(), return(). Ở đây bạn có thể thấy rằng có các logic xử lý khác nhau cho các hàm tích hợp sẵn và các hàm do người dùng định nghĩa.

Hàm applyOperation() so sánh tất cả các thao tác đọc và ghi thu được từ operationFromFuncitonCall() để xác định xem dữ liệu được lưu trữ trong m_store được đọc hay ghi trong lệnh gọi hàm này và sửa đổi trạng thái hoạt động tương ứng trong m_store.

Xem xét việc xử lý logic tối ưu hóa UnusedStoreEliminator đã đề cập ở trên trên hàm attack() của hợp đồng Eocene:

Lưu trữ x= 1 vào biến m_store và đặt trạng thái là Chưa quyết định

1. Gặp lệnh gọi hàm y(), lấy toàn bộ thao tác đọc ghi của lệnh gọi hàm y()

2. Duyệt qua biến m_store và thấy rằng tất cả các thao tác đọc và ghi do lệnh gọi y() gây ra không liên quan gì đến x= 1 và trạng thái của x= 1 vẫn là Undecided

1. Lấy logic luồng điều khiển của hàm y(), vì hàm y() có nhánh có thể trở lại bình thường nên canContinue là True và không nhập phán đoán If. x= 1 trạng thái vẫn chưa quyết định! ! !

3. Gặp phải thao tác lưu trữ x=2:

1. Duyệt qua biến m_store và thấy rằng x= 1 ở trạng thái Chưa quyết định, thao tác x= 2 sẽ ghi đè x= 1 và đặt trạng thái của x= 1 thành Chưa sử dụng.

2. Lưu trữ thao tác x= 2 vào m_store và trạng thái ban đầu là chưa quyết định.

4. Chức năng kết thúc:

1. Thay đổi trạng thái hoạt động của trạng thái chưa quyết định trong tất cả m_stores thành Đã sử dụng

2. Xóa mọi thao tác ở trạng thái Unused trong m_store

Rõ ràng, khi hàm được gọi, nếu hàm được gọi có thể chấm dứt việc thực thi thông báo, thì tất cả các thao tác ghi ở trạng thái Chưa quyết định trước khi hàm được gọi nên chuyển thành Đã sử dụng, thay vì giữ nguyên ở trạng thái Chưa quyết định, dẫn đến thao tác ghi trước đó chức năng được gọi Hành động đã bị xóa do nhầm lẫn.

hiện hữu

hiện hữuSolidity, một ví dụ về mã hợp đồng sẽ không bị ảnh hưởng theo logic cơ bản giống nhau. Tuy nhiên, mã không bị ảnh hưởng bởi lỗ hổng này không phải vì có các khả năng khác trong logic xử lý của UnusedStoreEliminator, mà trong bước tối ưu hóa Yul trước UnusedStoreEliminator, có một quy trình tối ưu hóa FullInliner sẽ nhúng hàm được gọi với một hàm nhỏ hoặc chỉ một gọi vào chức năng gọi Trong chức năng gọi, các chức năng do người dùng xác định trong các điều kiện kích hoạt lỗ hổng được tránh.

contract Normal {

    uint public x;

    function f(bool a) public {

        x = 1;

        g(a);

        x = 2;

    }

    function g(bool a) internal {

        if (!a)

        assembly { return( 0, 0) }

    }

}

Kết quả tổng hợp như sau:

tiêu đề cấp đầu tiên

2. Giải pháp

Giải pháp cơ bản nhất là biên dịch mà không sử dụng trình biên dịch solidity trong phạm vi bị ảnh hưởng. Nếu cần sử dụng phiên bản dễ bị tấn công của trình biên dịch, bạn có thể xem xét loại bỏ bước tối ưu hóa UnusedStoreEliminator trong quá trình biên dịch.

Nếu bạn muốn giảm thiểu lỗ hổng từ cấp độ mã hợp đồng, xem xét mức độ phức tạp của nhiều bước tối ưu hóa và độ phức tạp của luồng lệnh gọi chức năng thực tế, vui lòng tìm nhân viên bảo mật chuyên nghiệp để tiến hành kiểm tra mã nhằm giúp khám phá các lỗ hổng trong hợp đồng do điều này gây ra lỗ hổng Bảo mật Câu hỏi.

Sự an toàn
hợp đồng thông minh
nhà phát triển
công nghệ
Chào mừng tham gia cộng đồng chính thức của Odaily
Nhóm đăng ký
https://t.me/Odaily_News
Tài khoản chính thức
https://twitter.com/OdailyChina