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
Phân tích lỗ hổng trình biên dịch Solidity: Lỗ hổng của ABI Recoding
Eocene
特邀专栏作者
2023-04-25 10:25
Bài viết này có khoảng 1907 từ, đọc toàn bộ bài viết mất khoảng 3 phút
Bản thân quá trình này không có vấn đề logic lớn nào, nhưng khi nó được kết hợp với cơ chế dọn dẹp của Solidity, do thiếu sót của chính mã trình biên dịch Solidity, dẫn đến tồn tại các lỗ hổng

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

Bài viết này phân tích chi tiết vấn đề dễ bị tổn thương do xử lý lỗi của mảng loại uint và byte 32 có độ dài cố định trong quá trình ABIReencoding của trình biên dịch Solidity (0.5.8<=version <0.8.16) từ cấp độ mã nguồn và Đề xuất các giải pháp liên quan và các biện pháp phòng tránh.

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

chi tiết lỗ hổngĐịnh dạng mã hóa ABI là phương pháp mã hóa tiêu chuẩn được sử dụng khi người dùng hoặc hợp đồng thực hiện lệnh gọi hàm đến hợp đồng và truyền tham số. Để biết chi tiết, vui lòng tham khảo chính thức của Solidity vềmã hóa ABI

mô tả chi tiết về .

Trong quá trình phát triển hợp đồng, dữ liệu cần thiết sẽ được lấy từ dữ liệu calldata do người dùng hoặc các hợp đồng khác gửi, sau đó dữ liệu thu được có thể được chuyển tiếp hoặc phát ra. Tất cả các hoạt động opcode giới hạn trong máy ảo evm đều dựa trên bộ nhớ, ngăn xếp và lưu trữ, vì vậy trong Solidity, khi gặp các hoạt động yêu cầu mã hóa dữ liệu ABI, dữ liệu trong calldata sẽ được mã hóa ở định dạng ABI theo tiêu chuẩn mới. thứ tự, và được lưu trữ trong bộ nhớ.Bản thân quá trình này không có vấn đề logic lớn nào, nhưng khi kết hợp với Solidity'scơ chế dọn dẹp

Khi kết hợp với nhau, do sự thiếu sót của chính mã trình biên dịch Solidity nên có những lỗ hổng.

Theo quy tắc mã hóa ABI, sau khi loại bỏ bộ chọn chức năng, dữ liệu được mã hóa ABI được chia thành hai phần: phần đầu và phần đuôi. Khi định dạng dữ liệu là một mảng uint hoặc byte 32 byte có độ dài cố định, ABI sẽ lưu trữ dữ liệu của loại này trong phần đầu. Việc triển khai cơ chế dọn dẹp trong bộ nhớ của Solidity là làm trống bộ nhớ của chỉ mục tiếp theo sau khi bộ nhớ của chỉ mục hiện tại được sử dụng, để ngăn bộ nhớ của chỉ mục tiếp theo bị ảnh hưởng bởi dữ liệu bẩn. Và khi Solidity ABI mã hóa một tập hợp dữ liệu tham số, nó sẽ mã hóa theo thứ tự từ trái sang phải! !

contract Eocene {

        event VerifyABI( bytes[], uint[ 2 ]);

        function verifyABI(bytes[] calldata a, uint[ 2 ] calldata b) public  {

                emit VerifyABI(a,Để thuận tiện cho việc khám phá nguyên tắc lỗ hổng sau này, hãy xem xét mã hợp đồng ở dạng sau:

        }

}

b); // Dữ liệu sự kiện sẽ được mã hóa ở định dạng ABI và được lưu trữ trên chuỗi

Chức năng của hàm verifyABI trong hợp đồng Eocene chỉ là phát ra các byte có độ dài thay đổi[] a và uint có độ dài cố định[2] b trong các tham số của hàm.

Cần lưu ý ở đây rằng sự kiện sự kiện cũng sẽ kích hoạt mã hóa ABI. Tại đây các tham số a, b sẽ được mã hóa thành định dạng ABI rồi lưu trữ trên chuỗi.verifyABI(['0x aaaaaa','0x bbbbbb'],[0x 11111, 0x 22222 ])

Chúng tôi sử dụng phiên bản v 0.8.14 của Solidity để biên dịch mã hợp đồng, triển khai nó thông qua phối lại và chuyển vàoverifyABI(['0x aaaaaa','0x bbbbbb'],[0x 11111, 0x 22222 ])Đầu tiên, chúng ta hãy nhìn vào

0x 5 2c d 1 a 9 c                                                                  // bytes 4(sha 3("verify(btyes[], uint[ 2 ])"))

0000000000000000000000000000000000000000000000000000000000000060            // index of  a

0000000000000000000000000000000000000000000000000000000000011111            // b[0 ]

0000000000000000000000000000000000000000000000000000000000022222            // b[1 ]

0000000000000000000000000000000000000000000000000000000000000002            // length of a

0000000000000000000000000000000000000000000000000000000000000040            // index of a[0 ]

0000000000000000000000000000000000000000000000000000000000000080            // index of a[1 ]

0000000000000000000000000000000000000000000000000000000000000003            // length of a[0 ]

aaaaaa 0000000000000000000000000000000000000000000000000000000000            // a[0 ]

0000000000000000000000000000000000000000000000000000000000000003            // length of a[1 ]

bbbbbb 0000000000000000000000000000000000000000000000000000000000            // a[1 ]

Định dạng mã hóa chính xác cho:a, bNếu trình biên dịch Solidity bình thường, khi tham sốTX

Khi sự kiện sự kiện được ghi lại trên chuỗi, định dạng dữ liệu phải giống với những gì chúng tôi đã gửi. Thực tế chúng ta hãy gọi hợp đồng và kiểm tra nhật ký trên chuỗi, nếu bạn muốn tự mình so sánh, bạn có thể kiểm tra

Sau khi gọi thành công, sự kiện event hợp đồng được ghi lại như sau:

0000000000000000000000000000000000000000000000000000000000000060            // index of  a

0000000000000000000000000000000000000000000000000000000000011111            // b[0 ]

0000000000000000000000000000000000000000000000000000000000022222            // b[1 ]

0000000000000000000000000000000000000000000000000000000000000000            // length of a?? why become 0??

0000000000000000000000000000000000000000000000000000000000000040            // index of a[0 ]

0000000000000000000000000000000000000000000000000000000000000080            // index of a[1 ]

0000000000000000000000000000000000000000000000000000000000000003            // length of a[0 ]

aaaaaa 0000000000000000000000000000000000000000000000000000000000            // a[0 ]

0000000000000000000000000000000000000000000000000000000000000003            // length of a[1 ]

bbbbbb 0000000000000000000000000000000000000000000000000000000000            // a[1 ]

! ! Thật bất ngờ, ngay sau b[1 ], giá trị lưu trữ độ dài của tham số a đã bị xóa nhầm! !

Tại sao?

  1. Như chúng tôi đã nói trước đó, khi Solidity gặp một loạt tham số cần mã hóa ABI, thứ tự tạo tham số sẽ là từ trái sang phải, logic mã hóa cụ thể cho a và b như sau

  2. Trước tiên, Solidity thực hiện mã hóa ABI trên a. Theo quy tắc mã hóa, chỉ mục của a được đặt ở phần đầu và độ dài phần tử cũng như giá trị cụ thể của a được lưu ở phần đuôi.

  3. Xử lý dữ liệu b, vì kiểu dữ liệu b ở định dạng uint[2] nên giá trị cụ thể của dữ liệu được lưu trong phần đầu. Tuy nhiên, do cơ chế dọn dẹp riêng của Solidity, sau khi b[1] được lưu vào bộ nhớ, giá trị của địa chỉ bộ nhớ tiếp theo chứa dữ liệu b[1] (địa chỉ bộ nhớ dùng để lưu độ dài của phần tử a) được đặt thành 0.

Hoạt động mã hóa ABI kết thúc, dữ liệu được mã hóa không chính xác được lưu trữ trên chuỗi và lỗ hổng SOL-2022-6 xuất hiện.

Ở cấp độ mã nguồn, logic lỗi cụ thể cũng rõ ràng.Khi cần lấy dữ liệu mảng có độ dài cố định byte 32 hoặc uint từ calldata vào bộ nhớ, Solidity sẽ luôn đặt dữ liệu chỉ mục bộ nhớ sau thành 0 sau khi dữ liệu được sao chép . . Và do trong bảng mã ABI có 2 phần đầu và đuôi, đồng thời thứ tự mã hóa cũng từ trái sang phải nên dẫn đến tồn tại các lỗ hổng.

Mã biên dịch Solidity cho lỗ hổng cụ thể như sau:ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup()

Nhập khi vị trí lưu trữ dữ liệu nguồn là Calldata và kiểu dữ liệu nguồn là ByteArray, String hoặc kiểu cơ bản của mảng nguồn là uint hoặc byte 32fromArrayType.isDynamicallySized()Xác định xem dữ liệu nguồn có phải là mảng có độ dài cố định hay không. Chỉ các mảng có độ dài cố định mới đáp ứng các điều kiện kích hoạt lỗ hổng.

SẽisByteArrayOrString()SẽYulUtilFunctions::copyToMemoryFunction(),Kết quả xét xử được chuyển đến

Xác định xem có thực hiện dọn dẹp vị trí chỉ mục tiếp theo sau khi thao tác gọidatacopy hoàn tất hay không theo kết quả phán đoán.

Kết hợp một số ràng buộc ở trên, lỗ hổng bảo mật chỉ có thể được kích hoạt khi dữ liệu nguồn ở định dạng calldata là một mảng uint hoặc byte 32 byte có độ dài cố định được sao chép vào bộ nhớ. Đó là lý do cho những hạn chế do lỗ hổng gây ra.

Lý do rất rõ ràng, nếu dữ liệu có độ dài cố định không nằm ở vị trí tham số cuối cùng được mã hóa, thì việc đặt 0 ở vị trí bộ nhớ tiếp theo sẽ không có bất kỳ tác dụng nào, bởi vì tham số mã hóa tiếp theo sẽ ghi đè lên vị trí này. Nếu không có dữ liệu trước dữ liệu có độ dài cố định cần được lưu trữ ở phần đuôi, thì ngay cả khi vị trí bộ nhớ sau được đặt thành 0, thì vị trí này không được sử dụng bởi mã ABI.

Ngoài ra,Ngoài ra,

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

      • event

      • error

      • abi.encode*

      • returns             //the return of function

      • struct              //the user defined struct

      • all external call

giải pháp

  1. giải pháp

  2. Khi có một hoạt động bị ảnh hưởng bởi khiếu nại trong mã hợp đồng, hãy đảm bảo rằng tham số cuối cùng không phải là một mảng uint hoặc byte 32 byte có độ dài cố định

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

về chúng tôi

At Eocene Research, we provide the insights of intentions and security behind everything you know or don't know of blockchain, and empower every individual and organization to answer complex questions we hadn't even dreamed of back then.

Learn more: Website | Medium | Twitter

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