Phân tích lỗ hổng biên dịch viên Solidity và chiến lược ứng phó
Trình biên dịch là một trong những thành phần cơ bản của hệ thống máy tính hiện đại, chức năng của nó là chuyển đổi mã nguồn của ngôn ngữ lập trình bậc cao thành mã lệnh có thể thực thi bởi máy tính. Hầu hết các nhà phát triển và nhân viên an ninh thường chú ý đến sự an toàn của mã ứng dụng, nhưng thường bỏ qua tính an toàn của chính trình biên dịch. Trên thực tế, trình biên dịch như một chương trình máy tính cũng có thể tồn tại lỗ hổng bảo mật, trong những tình huống cụ thể có thể mang lại rủi ro an ninh nghiêm trọng.
Ví dụ, khi trình duyệt biên dịch và thực thi mã JavaScript, có thể do lỗ hổng của engine JavaScript, dẫn đến việc người dùng truy cập vào trang web độc hại bị kẻ tấn công lợi dụng lỗ hổng để thực hiện mã từ xa, cuối cùng kiểm soát trình duyệt của nạn nhân thậm chí cả hệ điều hành. Một nghiên cứu khác cũng chỉ ra rằng lỗi của trình biên dịch C++ có thể dẫn đến các hậu quả nghiêm trọng như thực thi mã từ xa.
Trình biên dịch Solidity cũng có lỗ hổng bảo mật. Theo cảnh báo bảo mật của nhóm phát triển Solidity, nhiều phiên bản của trình biên dịch Solidity đều có lỗ hổng bảo mật. Chức năng của trình biên dịch Solidity là chuyển đổi mã hợp đồng thông minh thành mã lệnh (EVM) cho máy ảo Ethereum, các lệnh này cuối cùng sẽ được tải lên Ethereum và được EVM thực thi.
Cần phân biệt giữa lỗ hổng của trình biên dịch Solidity và lỗ hổng của chính EVM. Lỗ hổng EVM là các vấn đề bảo mật phát sinh khi máy ảo thực hiện các chỉ thị, có thể ảnh hưởng đến toàn bộ mạng Ethereum. Trong khi đó, lỗ hổng của trình biên dịch Solidity là các vấn đề xảy ra khi chuyển đổi Solidity thành mã EVM, không ảnh hưởng trực tiếp đến mạng Ethereum, nhưng có thể dẫn đến mã EVM được tạo ra không khớp với mong đợi của nhà phát triển.
Một mối nguy hiểm của lỗ hổng trình biên dịch Solidity là nó có thể dẫn đến mã EVM được tạo ra không khớp với mong đợi của nhà phát triển hợp đồng thông minh. Vì hợp đồng thông minh thường liên quan đến tài sản tiền điện tử của người dùng, bất kỳ lỗi nào do trình biên dịch gây ra có thể dẫn đến mất mát tài sản của người dùng, gây ra hậu quả nghiêm trọng.
Các nhà phát triển và nhân viên kiểm toán thường chú trọng vào logic hợp đồng và các vấn đề an ninh phổ biến, trong khi các lỗ hổng của trình biên dịch rất khó phát hiện qua kiểm tra mã. Cần phải kết hợp phân tích giữa phiên bản trình biên dịch cụ thể và mẫu mã để xác định liệu hợp đồng thông minh có bị ảnh hưởng bởi lỗ hổng trình biên dịch hay không.
Dưới đây là một số ví dụ thực tế về lỗi biên dịch Solidity:
SOL-2016-9 HighOrderByteCleanStorage
Lỗi này tồn tại trong các phiên bản trước của trình biên dịch Solidity (>=0.1.6 <0.4.4). Xem xét đoạn mã dưới đây:
solid
hợp đồng C {
uint32 a = 0x12345678;
uint32 b = 0;
hàm f() công khai {
a = a + 1;
}
function run() public view returns (uint32) {
return b;
}
}
Về lý thuyết, biến b không bị thay đổi, hàm run() nên trả về 0. Tuy nhiên, trong mã do trình biên dịch phiên bản lỗi tạo ra, run() sẽ trả về 1. Điều này là do EVM sử dụng các phần tử ngăn xếp kích thước 32 byte, trong khi Solidity hỗ trợ các kiểu dữ liệu nhỏ hơn như uint32. Khi biên dịch, trình biên dịch cần phải xóa các bit cao (clean up), nhưng trong trường hợp tràn số khi cộng, nó không được xử lý đúng cách, dẫn đến việc bit cao của 1 được ghi vào biến b.
SOL-2022-4 Ảnh hưởng bên cạnh bộ nhớ InlineAssembly
Lỗ hổng này tồn tại trong các phiên bản biên dịch từ 0.8.13 đến 0.8.15. Xem xét mã sau:
solidity
hợp đồng C {
function f() public pure returns (uint) {
lắp ráp {
mstore(0, 0x42)
}
uint x;
assembly {
x := mload(0)
}
return x;
}
}
Trình biên dịch để tối ưu hóa sẽ loại bỏ các thao tác ghi bộ nhớ có vẻ không cần thiết, nhưng đã lỗi làm cho việc truy cập bộ nhớ giữa các khối assembly cũng bị tối ưu hóa. Điều này dẫn đến hàm f() trả về 0 thay vì 0x42 đúng.
Lỗ hổng này ảnh hưởng đến các phiên bản biên dịch từ 0.5.8 đến 0.8.16. Xem xét mã dưới đây:
tính vững chắc
hợp đồng C {
function f(string[1] calldata a) external pure returns (string memory) {
return abi.decode(abi.encode(a), (string[1]))[0];
}
}
Trong trường hợp bình thường, hàm này nên trả về chuỗi đầu vào. Nhưng trong phiên bản có lỗ hổng, nó sẽ trả về chuỗi rỗng. Điều này là do trình biên dịch đã xóa một số dữ liệu một cách sai lầm khi mã hóa abi cho mảng calldata, dẫn đến dữ liệu sau khi giải mã không nhất quán.
Đối với lỗ hổng của trình biên dịch Solidity, đội ngũ an ninh blockchain Cobo đề xuất các khuyến nghị sau:
Dành cho các nhà phát triển:
Sử dụng phiên bản biên dịch Solidity mới hơn, thường có ít vấn đề bảo mật đã biết.
Hoàn thiện các trường hợp kiểm tra đơn vị. Hầu hết các lỗi ở mức trình biên dịch sẽ dẫn đến kết quả thực thi mã không nhất quán với mong đợi, việc nâng cao tỷ lệ bao phủ kiểm tra có thể tránh được những vấn đề như vậy.
Tránh sử dụng lắp ráp nội tuyến, các thao tác giải mã và mã hóa abi phức tạp, không nên sử dụng mù quáng các tính năng mới và chức năng thử nghiệm. Phần lớn lỗ hổng biên dịch liên quan đến những thao tác phức tạp này.
Đối với nhân viên an ninh:
Trong quá trình kiểm toán, không được bỏ qua các rủi ro bảo mật có thể do trình biên dịch gây ra. Mục kiểm tra liên quan là SWC-102: Phiên bản trình biên dịch lỗi thời.
Trong quy trình phát triển, thúc giục đội ngũ phát triển nâng cấp phiên bản biên dịch, có thể đưa vào kiểm tra tự động phiên bản trong CI/CD.
Không cần quá lo lắng về lỗ hổng của trình biên dịch. Hầu hết các lỗ hổng chỉ được kích hoạt trong các mô hình mã cụ thể, cần đánh giá ảnh hưởng an ninh dựa trên từng dự án cụ thể.
Một số tài nguyên hữu ích:
Cảnh báo an toàn chính thức của Solidity:
Danh sách lỗi chính thức của Solidity:
Danh sách lỗi của các phiên bản biên dịch viên:
Biểu tượng cảnh báo ở góc trên bên phải trang hợp đồng Etherscan có thể thông báo về các lỗ hổng bảo mật của phiên bản trình biên dịch hiện tại.
Tóm lại, các nhà phát triển và nhân viên an ninh nên chú trọng đến các rủi ro an ninh có thể do lỗ hổng trong trình biên dịch Solidity gây ra, thực hiện các biện pháp phù hợp để giảm thiểu rủi ro, đảm bảo an toàn cho hợp đồng thông minh.
Xem bản gốc
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
Phân tích lỗ hổng trình biên dịch Solidity và các chiến lược thực tiễn để bảo vệ an toàn hợp đồng thông minh
Phân tích lỗ hổng biên dịch viên Solidity và chiến lược ứng phó
Trình biên dịch là một trong những thành phần cơ bản của hệ thống máy tính hiện đại, chức năng của nó là chuyển đổi mã nguồn của ngôn ngữ lập trình bậc cao thành mã lệnh có thể thực thi bởi máy tính. Hầu hết các nhà phát triển và nhân viên an ninh thường chú ý đến sự an toàn của mã ứng dụng, nhưng thường bỏ qua tính an toàn của chính trình biên dịch. Trên thực tế, trình biên dịch như một chương trình máy tính cũng có thể tồn tại lỗ hổng bảo mật, trong những tình huống cụ thể có thể mang lại rủi ro an ninh nghiêm trọng.
Ví dụ, khi trình duyệt biên dịch và thực thi mã JavaScript, có thể do lỗ hổng của engine JavaScript, dẫn đến việc người dùng truy cập vào trang web độc hại bị kẻ tấn công lợi dụng lỗ hổng để thực hiện mã từ xa, cuối cùng kiểm soát trình duyệt của nạn nhân thậm chí cả hệ điều hành. Một nghiên cứu khác cũng chỉ ra rằng lỗi của trình biên dịch C++ có thể dẫn đến các hậu quả nghiêm trọng như thực thi mã từ xa.
Trình biên dịch Solidity cũng có lỗ hổng bảo mật. Theo cảnh báo bảo mật của nhóm phát triển Solidity, nhiều phiên bản của trình biên dịch Solidity đều có lỗ hổng bảo mật. Chức năng của trình biên dịch Solidity là chuyển đổi mã hợp đồng thông minh thành mã lệnh (EVM) cho máy ảo Ethereum, các lệnh này cuối cùng sẽ được tải lên Ethereum và được EVM thực thi.
Cần phân biệt giữa lỗ hổng của trình biên dịch Solidity và lỗ hổng của chính EVM. Lỗ hổng EVM là các vấn đề bảo mật phát sinh khi máy ảo thực hiện các chỉ thị, có thể ảnh hưởng đến toàn bộ mạng Ethereum. Trong khi đó, lỗ hổng của trình biên dịch Solidity là các vấn đề xảy ra khi chuyển đổi Solidity thành mã EVM, không ảnh hưởng trực tiếp đến mạng Ethereum, nhưng có thể dẫn đến mã EVM được tạo ra không khớp với mong đợi của nhà phát triển.
Một mối nguy hiểm của lỗ hổng trình biên dịch Solidity là nó có thể dẫn đến mã EVM được tạo ra không khớp với mong đợi của nhà phát triển hợp đồng thông minh. Vì hợp đồng thông minh thường liên quan đến tài sản tiền điện tử của người dùng, bất kỳ lỗi nào do trình biên dịch gây ra có thể dẫn đến mất mát tài sản của người dùng, gây ra hậu quả nghiêm trọng.
Các nhà phát triển và nhân viên kiểm toán thường chú trọng vào logic hợp đồng và các vấn đề an ninh phổ biến, trong khi các lỗ hổng của trình biên dịch rất khó phát hiện qua kiểm tra mã. Cần phải kết hợp phân tích giữa phiên bản trình biên dịch cụ thể và mẫu mã để xác định liệu hợp đồng thông minh có bị ảnh hưởng bởi lỗ hổng trình biên dịch hay không.
Dưới đây là một số ví dụ thực tế về lỗi biên dịch Solidity:
Lỗi này tồn tại trong các phiên bản trước của trình biên dịch Solidity (>=0.1.6 <0.4.4). Xem xét đoạn mã dưới đây:
solid hợp đồng C { uint32 a = 0x12345678; uint32 b = 0; hàm f() công khai { a = a + 1; } function run() public view returns (uint32) { return b; } }
Về lý thuyết, biến b không bị thay đổi, hàm run() nên trả về 0. Tuy nhiên, trong mã do trình biên dịch phiên bản lỗi tạo ra, run() sẽ trả về 1. Điều này là do EVM sử dụng các phần tử ngăn xếp kích thước 32 byte, trong khi Solidity hỗ trợ các kiểu dữ liệu nhỏ hơn như uint32. Khi biên dịch, trình biên dịch cần phải xóa các bit cao (clean up), nhưng trong trường hợp tràn số khi cộng, nó không được xử lý đúng cách, dẫn đến việc bit cao của 1 được ghi vào biến b.
Lỗ hổng này tồn tại trong các phiên bản biên dịch từ 0.8.13 đến 0.8.15. Xem xét mã sau:
solidity hợp đồng C { function f() public pure returns (uint) { lắp ráp { mstore(0, 0x42) } uint x; assembly { x := mload(0) } return x; } }
Trình biên dịch để tối ưu hóa sẽ loại bỏ các thao tác ghi bộ nhớ có vẻ không cần thiết, nhưng đã lỗi làm cho việc truy cập bộ nhớ giữa các khối assembly cũng bị tối ưu hóa. Điều này dẫn đến hàm f() trả về 0 thay vì 0x42 đúng.
Lỗ hổng này ảnh hưởng đến các phiên bản biên dịch từ 0.5.8 đến 0.8.16. Xem xét mã dưới đây:
tính vững chắc hợp đồng C { function f(string[1] calldata a) external pure returns (string memory) { return abi.decode(abi.encode(a), (string[1]))[0]; } }
Trong trường hợp bình thường, hàm này nên trả về chuỗi đầu vào. Nhưng trong phiên bản có lỗ hổng, nó sẽ trả về chuỗi rỗng. Điều này là do trình biên dịch đã xóa một số dữ liệu một cách sai lầm khi mã hóa abi cho mảng calldata, dẫn đến dữ liệu sau khi giải mã không nhất quán.
Đối với lỗ hổng của trình biên dịch Solidity, đội ngũ an ninh blockchain Cobo đề xuất các khuyến nghị sau:
Dành cho các nhà phát triển:
Sử dụng phiên bản biên dịch Solidity mới hơn, thường có ít vấn đề bảo mật đã biết.
Hoàn thiện các trường hợp kiểm tra đơn vị. Hầu hết các lỗi ở mức trình biên dịch sẽ dẫn đến kết quả thực thi mã không nhất quán với mong đợi, việc nâng cao tỷ lệ bao phủ kiểm tra có thể tránh được những vấn đề như vậy.
Tránh sử dụng lắp ráp nội tuyến, các thao tác giải mã và mã hóa abi phức tạp, không nên sử dụng mù quáng các tính năng mới và chức năng thử nghiệm. Phần lớn lỗ hổng biên dịch liên quan đến những thao tác phức tạp này.
Đối với nhân viên an ninh:
Trong quá trình kiểm toán, không được bỏ qua các rủi ro bảo mật có thể do trình biên dịch gây ra. Mục kiểm tra liên quan là SWC-102: Phiên bản trình biên dịch lỗi thời.
Trong quy trình phát triển, thúc giục đội ngũ phát triển nâng cấp phiên bản biên dịch, có thể đưa vào kiểm tra tự động phiên bản trong CI/CD.
Không cần quá lo lắng về lỗ hổng của trình biên dịch. Hầu hết các lỗ hổng chỉ được kích hoạt trong các mô hình mã cụ thể, cần đánh giá ảnh hưởng an ninh dựa trên từng dự án cụ thể.
Một số tài nguyên hữu ích:
Tóm lại, các nhà phát triển và nhân viên an ninh nên chú trọng đến các rủi ro an ninh có thể do lỗ hổng trong trình biên dịch Solidity gây ra, thực hiện các biện pháp phù hợp để giảm thiểu rủi ro, đảm bảo an toàn cho hợp đồng thông minh.