Phân tích lỗ hổng biên dịch Solidity và chiến lược đối phó
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. Nó là một chương trình máy tính, có chức năng chuyển đổi mã nguồn của ngôn ngữ lập trình cấp cao mà con người dễ hiểu và viết thành mã lệnh có thể thực thi bởi CPU hoặc máy ảo bytecode của máy tính.
Hầu hết các nhà phát triển và nhân viên an ninh thường quan tâm đến tính bảo mật của mã ứng dụng, nhưng có thể họ sẽ bỏ qua tính bảo mật của chính trình biên dịch. Trên thực tế, trình biên dịch cũng là một chương trình máy tính và có thể tồn tại các lỗ hổng bảo mật. Các lỗ hổng bảo mật do trình biên dịch tạo ra trong một số trường hợp có thể mang lại rủi ro an ninh nghiêm trọng. Ví dụ, trong quá trình biên dịch và phân tích mã Javascript phía trước, trình duyệt có thể bị tấn công bởi kẻ xấu khi người dùng truy cập vào trang web độc hại do lỗ hổng của engine phân tích Javascript, dẫn đến việc thực thi 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. Nghiên cứu cho thấy, lỗi của trình biên dịch Clang C++ cũng có thể dẫn đến việc thực thi mã từ xa và các hậu quả nghiêm trọng khác.
Trình biên dịch Solidity cũng không phải là ngoại lệ, theo cảnh báo an toàn từ nhóm phát triển Solidity, nhiều phiên bản khác nhau của trình biên dịch Solidity đều có lỗ hổng bảo mật.
Lỗi biên dịch Solidity
Chức năng của trình biên dịch Solidity là chuyển đổi mã hợp đồng thông minh mà các nhà phát triển viết thành mã lệnh (EVM) cho máy ảo Ethereum, các mã lệnh EVM này được đóng gói và tải lên Ethereum thông qua giao dịch, cuối cùng được EVM phân tích và thực thi.
Cần phải 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 của EVM chỉ những lỗ hổng bảo mật phát sinh khi máy ảo thực hiện các lệnh. Do kẻ tấn công có thể tải lên bất kỳ mã nào lên Ethereum, mã này cuối cùng sẽ được chạy trong mỗi chương trình khách hàng P2P của Ethereum, nếu EVM có lỗ hổng bảo mật, điều này sẽ ảnh hưởng đến toàn bộ mạng Ethereum, có thể gây ra từ chối dịch vụ toàn mạng (DoS) thậm chí dẫn đến việc toàn bộ chuỗi bị kẻ tấn công kiểm soát hoàn toàn. Tuy nhiên, do thiết kế của EVM tương đối đơn giản và mã nguồn chính không được cập nhật thường xuyên, nên xác suất xảy ra vấn đề trên là tương đối thấp.
Lỗi trình biên dịch Solidity đề cập đến việc trình biên dịch có lỗ hổng khi chuyển đổi Solidity thành mã EVM. Khác với các tình huống như trình duyệt biên dịch và chạy Javascript trên máy tính của người dùng, quá trình biên dịch Solidity chỉ diễn ra trên máy tính của nhà phát triển hợp đồng thông minh, không chạy trên Ethereum. Do đó, lỗi trình biên dịch Solidity sẽ không ảnh hưởng trực tiếp đến mạng Ethereum.
Một mối nguy hiểm chính 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 nhất quán với những gì mà nhà phát triển hợp đồng thông minh mong đợi. Bởi vì các hợp đồng thông minh trên Ethereum 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 trong hợp đồng thông minh đều có thể dẫn đến việc 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 hợp đồng có thể chú trọng vào các vấn đề thực hiện logic mã hợp đồng, cũng như các vấn đề an toàn ở mức Solidity như tấn công tái nhập và tràn số nguyên. Tuy nhiên, đối với các lỗ hổng của trình biên dịch Solidity, chỉ thông qua việc kiểm toán logic mã nguồn hợp đồng thì rất khó để phát hiện. 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à các mẫu mã cụ thể để xác định xem hợp đồng thông minh có bị ảnh hưởng bởi lỗ hổng của trình biên dịch hay không.
Ví dụ về lỗ hổng trình biên dịch Solidity
Dưới đây là một số trường hợp thực tế về lỗ hổng của trình biên dịch Solidity, trình bày hình thức cụ thể, nguyên nhân và tác hại của lỗ hổng trình biên dịch Solidity.
SOL-2016-9 HighOrderByteCleanStorage
Lỗ hổng này tồn tại trong các phiên bản trình biên dịch Solidity cũ hơn (>=0.1.6 <0.4.4).
Xem xét mã sau:
solidity
hợp đồng C {
uint32 a = 0x12345678;
uint16 b = 0x1234;
chức năng f() công cộng {
a = a + 1;
}
function run() public view returns (uint16) {
return b;
}
}
Biến storage b không bị thay đổi, vì vậy hàm run() nên trả về giá trị mặc định 0. Nhưng thực tế trong mã được tạo ra bởi phiên bản biên dịch có lỗ hổng, run() sẽ trả về 1.
Trong trường hợp không hiểu lỗ hổng của trình biên dịch, các nhà phát triển thông thường rất khó để phát hiện các lỗi trong đoạn mã trên chỉ thông qua việc xem xét mã đơn giản. Đoạn mã trên chỉ là một ví dụ đơn giản, do đó sẽ không gây ra hậu quả nghiêm trọng đặc biệt. Nhưng nếu biến b được sử dụng cho việc xác thực quyền hạn, ghi chép tài sản, v.v., sự không nhất quán này có thể dẫn đến hậu quả rất nghiêm trọng.
Nguyên nhân gây ra hiện tượng bất thường nêu trên là do EVM sử dụng máy ảo kiểu ngăn xếp, mỗi phần tử trong ngăn xếp đều có kích thước 32 byte ( tức là kích thước của biến uint256 ). Mặt khác, mỗi slot trong lưu trữ cơ sở cũng có kích thước 32 byte. Trong ngôn ngữ Solidity hỗ trợ các loại dữ liệu nhỏ hơn 32 byte như uint32, trình biên dịch khi xử lý các biến kiểu này cần thực hiện các thao tác làm sạch thích hợp đối với các bit cao (clean up) để đảm bảo tính chính xác của dữ liệu. Trong tình huống trên, khi phép cộng gây ra tràn số nguyên, trình biên dịch không thực hiện đúng thao tác làm sạch đối với bit cao của kết quả, dẫn đến việc bit 1 ở vị trí cao bị ghi vào lưu trữ, cuối cùng ghi đè lên biến a và biến b, làm giá trị của biến b bị thay đổi thành 1.
SOL-2022-4 InlineAssemblyMemorySideEffects
Lỗ hổng này tồn tại trong các phiên bản của trình biên dịch từ >=0.8.13 <0.8.15. Trình biên dịch Solidity trong quá trình chuyển đổi ngôn ngữ Solidity thành mã EVM không chỉ đơn giản là dịch thuật. Nó còn thực hiện phân tích sâu về luồng điều khiển và dữ liệu, thực hiện các quy trình tối ưu hóa biên dịch khác nhau, nhằm giảm kích thước mã được tạo ra và tối ưu hóa tiêu thụ gas trong quá trình thực thi. Các thao tác tối ưu hóa kiểu này rất phổ biến trong các trình biên dịch của các ngôn ngữ cấp cao khác nhau, nhưng do số lượng tình huống cần xem xét rất phức tạp, cũng dễ xảy ra lỗi hoặc lỗ hổng bảo mật.
Xem xét mã sau:
solidity
hợp đồng C {
function f() public pure returns (uint256) {
lắp ráp {
mstore(0, 0x42)
}
uint256 x;
lắp ráp {
x := mload(0)
}
return x;
}
}
Lỗi trong mã trên xuất phát từ việc tối ưu hóa biên dịch. Trong một số trường hợp, nếu trong hàm có mã sửa đổi dữ liệu tại địa chỉ bộ nhớ 0, nhưng không có nơi nào khác sử dụng dữ liệu đó, thì thực tế có thể loại bỏ mã sửa đổi bộ nhớ 0 trực tiếp, từ đó tiết kiệm gas mà không ảnh hưởng đến logic chương trình sau.
Chiến lược tối ưu hóa này bản thân không có vấn đề gì, nhưng trong việc triển khai mã cụ thể của trình biên dịch Solidity, loại tối ưu hóa này chỉ được áp dụng trong một block assembly duy nhất. Đối với đoạn mã ví dụ trên, việc ghi và truy cập vào bộ nhớ 0 nằm trong hai block assembly khác nhau, trong khi trình biên dịch chỉ thực hiện phân tích tối ưu hóa cho từng block assembly riêng lẻ. Do trong block assembly đầu tiên không có bất kỳ thao tác đọc nào sau khi ghi vào bộ nhớ 0, nên lệnh ghi này được xác định là thừa và sẽ bị loại bỏ, dẫn đến lỗi. Trong phiên bản có lỗ hổng, hàm f( sẽ trả về giá trị 0, trong khi thực tế đoạn mã trên nên trả về giá trị chính xác 0x42.
Lỗ hổng này ảnh hưởng đến các phiên bản biên dịch viên >= 0.5.8 < 0.8.16. Xem xét đoạn mã sau:
solidity
hợp đồng C {
function f###bytes calldata data( external pure returns )bytes memory( {
bytes4) bộ nhớ a = [bytes4[1]dữ liệu(];
return abi.encode)a(;
}
}
Trong tình huống bình thường, đoạn mã trên sẽ trả về biến a là "aaaa". Nhưng trong phiên bản có lỗ hổng, nó sẽ trả về chuỗi rỗng "".
Nguyên nhân của lỗ hổng này là do Solidity thực hiện thao tác abi.encode trên mảng kiểu calldata, đã sai lầm trong việc dọn dẹp một số dữ liệu, dẫn đến việc sửa đổi dữ liệu khác liền kề, gây ra sự không nhất quán trong dữ liệu sau khi mã hóa và giải mã.
Cần lưu ý rằng, khi thực hiện cuộc gọi bên ngoài và phát sự kiện, Solidity sẽ ẩn danh mã hóa các tham số bằng abi.encode, do đó, xác suất xuất hiện của mã lỗi nêu trên sẽ cao hơn so với cảm nhận trực quan.
Lỗ hổng này đã được chuyển thể thành một bài toán blockchain nổi tiếng trong cuộc thi an ninh 0ctf 2022, cho thấy ảnh hưởng của lỗ hổng biên dịch đối với hợp đồng thông minh trong các tình huống phát triển thực tế.
![Phân tích lỗ hổng biên dịch Solidity và các biện pháp ứng phó])https://img-cdn.gateio.im/webp-social/moments-c97428f89ed62d5ad8551cdb2ba30867.webp(
Đề xuất an toàn
Đối với mối đe dọa và cách ứng phó với lỗ hổng biên dịch Solidity, có thể đưa ra những gợi ý sau:
Đối với các nhà phát triển:
Sử dụng phiên bản trình biên dịch Solidity mới hơn. Mặc dù phiên bản mới có thể giới thiệu các vấn đề bảo mật mới, nhưng các vấn đề bảo mật đã biết thường ít hơn so với phiên bản cũ.
Hoàn thiện các trường hợp kiểm tra đơn vị. Hầu hết các lỗi ở cấp 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. Những vấn đề này rất khó phát hiện qua việc xem xét mã, nhưng dễ dàng bị phát hiện trong giai đoạn kiểm tra. Do đó, việc nâng cao tỷ lệ bao phủ mã có thể tối đa hóa khả năng tránh các vấn đề này.
Cố gắng tránh sử dụng lắp ghép nội tuyến, các thao tác giải mã và mã hóa abi đối với mảng đa chiều và cấu trúc phức tạp, tránh sử dụng mù quáng các tính năng mới của ngôn ngữ và các tính năng thử nghiệm khi không có yêu cầu rõ ràng. Hầu hết các lỗ hổng có liên quan đến lắp ghép nội tuyến, các bộ mã hóa abi và các thao tác khác. Trình biên dịch dễ gặp lỗi hơn khi xử lý các tính năng ngôn ngữ phức tạp. Mặt khác, các nhà phát triển cũng dễ gặp sai lầm khi sử dụng các tính năng mới, dẫn đến vấn đề an ninh.
Đối với nhân viên an ninh:
Khi thực hiện kiểm toán an ninh mã Solidity, đừng bỏ qua những rủi ro an ninh có thể được giới thiệu bởi trình biên dịch Solidity. Mục kiểm tra tương ứng trong Phân loại Điểm yếu Hợp đồng Thông minh )SWC( 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 bảo mật nội bộ, khuyến khích đội ngũ phát triển nâng cấp phiên bản trình biên dịch Solidity và xem xét việc đưa vào quy trình CI/CD kiểm tra tự động cho phiên bản trình biên dịch.
Nhưng không cần quá lo lắng về lỗ hổng của trình biên dịch, hầu hết lỗ hổng trình biên dịch chỉ được kích hoạt trong các mẫu mã cụ thể. Hợp đồng được biên dịch bằng phiên bản trình biên dịch có lỗ hổng không nhất thiết phải có rủi ro an ninh, cần đánh giá tác động an ninh thực tế dựa trên tình hình cụ thể của dự án.
Một số tài nguyên hữu ích:
Cảnh báo an ninh do đội ngũ Solidity phát hành định kỳ:
Danh sách lỗi được cập nhật định kỳ từ kho chính thức của Solidity:
Danh sách lỗi biên dịch viên các phiên bản:
Code của Etherscan, biểu tượng dấu chấm than hình tam giác ở góc trên bên phải có thể cảnh báo về các lỗ hổng bảo mật trong phiên bản trình biên dịch hiện tại.
Tóm tắt
Bài viết này giới thiệu về khái niệm lỗ hổng biên dịch Solidity, phân tích các rủi ro an ninh có thể xảy ra trong môi trường phát triển Ethereum, và đưa ra những lời khuyên thực tế về an toàn cho các nhà phát triển và nhân viên an ninh. Mặc dù lỗ hổng biên dịch không phổ biến, nhưng tác động của nó có thể rất nghiêm trọng, xứng đáng được các nhà phát triển và nhân viên an ninh chú ý.
![Giải mã lỗ hổng trình biên dịch Solidity và các biện pháp ứng phó])https://img-cdn.gateio.im/webp-social/moments-84f5083d8748f2aab71fd92671d999a7.webp(
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.
22 thích
Phần thưởng
22
7
Chia sẻ
Bình luận
0/400
fork_in_the_road
· 07-19 10:48
Biên dịch viên cũng có thể có lỗ hổng? Hoảng gì hoảng!
Xem bản gốcTrả lời0
gas_fee_therapist
· 07-19 08:57
Lỗ hổng này viết rất rõ ràng nhé~
Xem bản gốcTrả lời0
GateUser-75ee51e7
· 07-18 18:19
Card bug luôn hấp dẫn hơn khai thác
Xem bản gốcTrả lời0
MetaverseHobo
· 07-16 19:40
Biên dịch viên cũng gặp sự cố? Lần này hoảng loạn rồi.
Xem bản gốcTrả lời0
DeepRabbitHole
· 07-16 19:39
Thật tệ, có nhiều lỗ hổng như vậy mà còn chơi vị thế bị khóa?
Xem bản gốcTrả lời0
GasWaster
· 07-16 19:37
Trình biên dịch cũng có lỗ hổng? yyds
Xem bản gốcTrả lời0
ForkTongue
· 07-16 19:23
Vấn đề rút tiền từ thẻ lỗ hổng mới là đáng tin cậy nhất.
Phân tích lỗ hổng biên dịch viên Solidity: Ảnh hưởng, trường hợp và chiến lược ứng phó
Phân tích lỗ hổng biên dịch Solidity và chiến lược đối phó
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. Nó là một chương trình máy tính, có chức năng chuyển đổi mã nguồn của ngôn ngữ lập trình cấp cao mà con người dễ hiểu và viết thành mã lệnh có thể thực thi bởi CPU hoặc máy ảo bytecode của máy tính.
Hầu hết các nhà phát triển và nhân viên an ninh thường quan tâm đến tính bảo mật của mã ứng dụng, nhưng có thể họ sẽ bỏ qua tính bảo mật của chính trình biên dịch. Trên thực tế, trình biên dịch cũng là một chương trình máy tính và có thể tồn tại các lỗ hổng bảo mật. Các lỗ hổng bảo mật do trình biên dịch tạo ra trong một số trường hợp có thể mang lại rủi ro an ninh nghiêm trọng. Ví dụ, trong quá trình biên dịch và phân tích mã Javascript phía trước, trình duyệt có thể bị tấn công bởi kẻ xấu khi người dùng truy cập vào trang web độc hại do lỗ hổng của engine phân tích Javascript, dẫn đến việc thực thi 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. Nghiên cứu cho thấy, lỗi của trình biên dịch Clang C++ cũng có thể dẫn đến việc thực thi mã từ xa và các hậu quả nghiêm trọng khác.
Trình biên dịch Solidity cũng không phải là ngoại lệ, theo cảnh báo an toàn từ nhóm phát triển Solidity, nhiều phiên bản khác nhau của trình biên dịch Solidity đều có lỗ hổng bảo mật.
Lỗi biên dịch Solidity
Chức năng của trình biên dịch Solidity là chuyển đổi mã hợp đồng thông minh mà các nhà phát triển viết thành mã lệnh (EVM) cho máy ảo Ethereum, các mã lệnh EVM này được đóng gói và tải lên Ethereum thông qua giao dịch, cuối cùng được EVM phân tích và thực thi.
Cần phải 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 của EVM chỉ những lỗ hổng bảo mật phát sinh khi máy ảo thực hiện các lệnh. Do kẻ tấn công có thể tải lên bất kỳ mã nào lên Ethereum, mã này cuối cùng sẽ được chạy trong mỗi chương trình khách hàng P2P của Ethereum, nếu EVM có lỗ hổng bảo mật, điều này sẽ ảnh hưởng đến toàn bộ mạng Ethereum, có thể gây ra từ chối dịch vụ toàn mạng (DoS) thậm chí dẫn đến việc toàn bộ chuỗi bị kẻ tấn công kiểm soát hoàn toàn. Tuy nhiên, do thiết kế của EVM tương đối đơn giản và mã nguồn chính không được cập nhật thường xuyên, nên xác suất xảy ra vấn đề trên là tương đối thấp.
Lỗi trình biên dịch Solidity đề cập đến việc trình biên dịch có lỗ hổng khi chuyển đổi Solidity thành mã EVM. Khác với các tình huống như trình duyệt biên dịch và chạy Javascript trên máy tính của người dùng, quá trình biên dịch Solidity chỉ diễn ra trên máy tính của nhà phát triển hợp đồng thông minh, không chạy trên Ethereum. Do đó, lỗi trình biên dịch Solidity sẽ không ảnh hưởng trực tiếp đến mạng Ethereum.
Một mối nguy hiểm chính 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 nhất quán với những gì mà nhà phát triển hợp đồng thông minh mong đợi. Bởi vì các hợp đồng thông minh trên Ethereum 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 trong hợp đồng thông minh đều có thể dẫn đến việc 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 hợp đồng có thể chú trọng vào các vấn đề thực hiện logic mã hợp đồng, cũng như các vấn đề an toàn ở mức Solidity như tấn công tái nhập và tràn số nguyên. Tuy nhiên, đối với các lỗ hổng của trình biên dịch Solidity, chỉ thông qua việc kiểm toán logic mã nguồn hợp đồng thì rất khó để phát hiện. 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à các mẫu mã cụ thể để xác định xem hợp đồng thông minh có bị ảnh hưởng bởi lỗ hổng của trình biên dịch hay không.
Ví dụ về lỗ hổng trình biên dịch Solidity
Dưới đây là một số trường hợp thực tế về lỗ hổng của trình biên dịch Solidity, trình bày hình thức cụ thể, nguyên nhân và tác hại của lỗ hổng trình biên dịch Solidity.
SOL-2016-9 HighOrderByteCleanStorage
Lỗ hổng này tồn tại trong các phiên bản trình biên dịch Solidity cũ hơn (>=0.1.6 <0.4.4).
Xem xét mã sau:
solidity hợp đồng C { uint32 a = 0x12345678; uint16 b = 0x1234;
chức năng f() công cộng { a = a + 1; }
function run() public view returns (uint16) { return b; } }
Biến storage b không bị thay đổi, vì vậy hàm run() nên trả về giá trị mặc định 0. Nhưng thực tế trong mã được tạo ra bởi phiên bản biên dịch có lỗ hổng, run() sẽ trả về 1.
Trong trường hợp không hiểu lỗ hổng của trình biên dịch, các nhà phát triển thông thường rất khó để phát hiện các lỗi trong đoạn mã trên chỉ thông qua việc xem xét mã đơn giản. Đoạn mã trên chỉ là một ví dụ đơn giản, do đó sẽ không gây ra hậu quả nghiêm trọng đặc biệt. Nhưng nếu biến b được sử dụng cho việc xác thực quyền hạn, ghi chép tài sản, v.v., sự không nhất quán này có thể dẫn đến hậu quả rất nghiêm trọng.
Nguyên nhân gây ra hiện tượng bất thường nêu trên là do EVM sử dụng máy ảo kiểu ngăn xếp, mỗi phần tử trong ngăn xếp đều có kích thước 32 byte ( tức là kích thước của biến uint256 ). Mặt khác, mỗi slot trong lưu trữ cơ sở cũng có kích thước 32 byte. Trong ngôn ngữ Solidity hỗ trợ các loại dữ liệu nhỏ hơn 32 byte như uint32, trình biên dịch khi xử lý các biến kiểu này cần thực hiện các thao tác làm sạch thích hợp đối với các bit cao (clean up) để đảm bảo tính chính xác của dữ liệu. Trong tình huống trên, khi phép cộng gây ra tràn số nguyên, trình biên dịch không thực hiện đúng thao tác làm sạch đối với bit cao của kết quả, dẫn đến việc bit 1 ở vị trí cao bị ghi vào lưu trữ, cuối cùng ghi đè lên biến a và biến b, làm giá trị của biến b bị thay đổi thành 1.
SOL-2022-4 InlineAssemblyMemorySideEffects
Lỗ hổng này tồn tại trong các phiên bản của trình biên dịch từ >=0.8.13 <0.8.15. Trình biên dịch Solidity trong quá trình chuyển đổi ngôn ngữ Solidity thành mã EVM không chỉ đơn giản là dịch thuật. Nó còn thực hiện phân tích sâu về luồng điều khiển và dữ liệu, thực hiện các quy trình tối ưu hóa biên dịch khác nhau, nhằm giảm kích thước mã được tạo ra và tối ưu hóa tiêu thụ gas trong quá trình thực thi. Các thao tác tối ưu hóa kiểu này rất phổ biến trong các trình biên dịch của các ngôn ngữ cấp cao khác nhau, nhưng do số lượng tình huống cần xem xét rất phức tạp, cũng dễ xảy ra lỗi hoặc lỗ hổng bảo mật.
Xem xét mã sau:
solidity hợp đồng C { function f() public pure returns (uint256) { lắp ráp { mstore(0, 0x42) } uint256 x; lắp ráp { x := mload(0) } return x; } }
Lỗi trong mã trên xuất phát từ việc tối ưu hóa biên dịch. Trong một số trường hợp, nếu trong hàm có mã sửa đổi dữ liệu tại địa chỉ bộ nhớ 0, nhưng không có nơi nào khác sử dụng dữ liệu đó, thì thực tế có thể loại bỏ mã sửa đổi bộ nhớ 0 trực tiếp, từ đó tiết kiệm gas mà không ảnh hưởng đến logic chương trình sau.
Chiến lược tối ưu hóa này bản thân không có vấn đề gì, nhưng trong việc triển khai mã cụ thể của trình biên dịch Solidity, loại tối ưu hóa này chỉ được áp dụng trong một block assembly duy nhất. Đối với đoạn mã ví dụ trên, việc ghi và truy cập vào bộ nhớ 0 nằm trong hai block assembly khác nhau, trong khi trình biên dịch chỉ thực hiện phân tích tối ưu hóa cho từng block assembly riêng lẻ. Do trong block assembly đầu tiên không có bất kỳ thao tác đọc nào sau khi ghi vào bộ nhớ 0, nên lệnh ghi này được xác định là thừa và sẽ bị loại bỏ, dẫn đến lỗi. Trong phiên bản có lỗ hổng, hàm f( sẽ trả về giá trị 0, trong khi thực tế đoạn mã trên nên trả về giá trị chính xác 0x42.
) SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup
Lỗ hổng này ảnh hưởng đến các phiên bản biên dịch viên >= 0.5.8 < 0.8.16. Xem xét đoạn mã sau:
solidity hợp đồng C { function f###bytes calldata data( external pure returns )bytes memory( { bytes4) bộ nhớ a = [bytes4[1]dữ liệu(]; return abi.encode)a(; } }
Trong tình huống bình thường, đoạn mã trên sẽ trả về biến a là "aaaa". Nhưng trong phiên bản có lỗ hổng, nó sẽ trả về chuỗi rỗng "".
Nguyên nhân của lỗ hổng này là do Solidity thực hiện thao tác abi.encode trên mảng kiểu calldata, đã sai lầm trong việc dọn dẹp một số dữ liệu, dẫn đến việc sửa đổi dữ liệu khác liền kề, gây ra sự không nhất quán trong dữ liệu sau khi mã hóa và giải mã.
Cần lưu ý rằng, khi thực hiện cuộc gọi bên ngoài và phát sự kiện, Solidity sẽ ẩn danh mã hóa các tham số bằng abi.encode, do đó, xác suất xuất hiện của mã lỗi nêu trên sẽ cao hơn so với cảm nhận trực quan.
Lỗ hổng này đã được chuyển thể thành một bài toán blockchain nổi tiếng trong cuộc thi an ninh 0ctf 2022, cho thấy ảnh hưởng của lỗ hổng biên dịch đối với hợp đồng thông minh trong các tình huống phát triển thực tế.
![Phân tích lỗ hổng biên dịch Solidity và các biện pháp ứng phó])https://img-cdn.gateio.im/webp-social/moments-c97428f89ed62d5ad8551cdb2ba30867.webp(
Đề xuất an toàn
Đối với mối đe dọa và cách ứng phó với lỗ hổng biên dịch Solidity, có thể đưa ra những gợi ý sau:
Đối với các nhà phát triển:
Sử dụng phiên bản trình biên dịch Solidity mới hơn. Mặc dù phiên bản mới có thể giới thiệu các vấn đề bảo mật mới, nhưng các vấn đề bảo mật đã biết thường ít hơn so với phiên bản cũ.
Hoàn thiện các trường hợp kiểm tra đơn vị. Hầu hết các lỗi ở cấp 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. Những vấn đề này rất khó phát hiện qua việc xem xét mã, nhưng dễ dàng bị phát hiện trong giai đoạn kiểm tra. Do đó, việc nâng cao tỷ lệ bao phủ mã có thể tối đa hóa khả năng tránh các vấn đề này.
Cố gắng tránh sử dụng lắp ghép nội tuyến, các thao tác giải mã và mã hóa abi đối với mảng đa chiều và cấu trúc phức tạp, tránh sử dụng mù quáng các tính năng mới của ngôn ngữ và các tính năng thử nghiệm khi không có yêu cầu rõ ràng. Hầu hết các lỗ hổng có liên quan đến lắp ghép nội tuyến, các bộ mã hóa abi và các thao tác khác. Trình biên dịch dễ gặp lỗi hơn khi xử lý các tính năng ngôn ngữ phức tạp. Mặt khác, các nhà phát triển cũng dễ gặp sai lầm khi sử dụng các tính năng mới, dẫn đến vấn đề an ninh.
Đối với nhân viên an ninh:
Khi thực hiện kiểm toán an ninh mã Solidity, đừng bỏ qua những rủi ro an ninh có thể được giới thiệu bởi trình biên dịch Solidity. Mục kiểm tra tương ứng trong Phân loại Điểm yếu Hợp đồng Thông minh )SWC( 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 bảo mật nội bộ, khuyến khích đội ngũ phát triển nâng cấp phiên bản trình biên dịch Solidity và xem xét việc đưa vào quy trình CI/CD kiểm tra tự động cho phiên bản trình biên dịch.
Nhưng không cần quá lo lắng về lỗ hổng của trình biên dịch, hầu hết lỗ hổng trình biên dịch chỉ được kích hoạt trong các mẫu mã cụ thể. Hợp đồng được biên dịch bằng phiên bản trình biên dịch có lỗ hổng không nhất thiết phải có rủi ro an ninh, cần đánh giá tác động an ninh thực tế dựa trên tình hình cụ thể của dự án.
Một số tài nguyên hữu ích:
Cảnh báo an ninh do đội ngũ Solidity phát hành định kỳ:
Danh sách lỗi được cập nhật định kỳ từ kho chính thức của Solidity:
Danh sách lỗi biên dịch viên các phiên bản:
Code của Etherscan, biểu tượng dấu chấm than hình tam giác ở góc trên bên phải có thể cảnh báo về các lỗ hổng bảo mật trong phiên bản trình biên dịch hiện tại.
Tóm tắt
Bài viết này giới thiệu về khái niệm lỗ hổng biên dịch Solidity, phân tích các rủi ro an ninh có thể xảy ra trong môi trường phát triển Ethereum, và đưa ra những lời khuyên thực tế về an toàn cho các nhà phát triển và nhân viên an ninh. Mặc dù lỗ hổng biên dịch không phổ biến, nhưng tác động của nó có thể rất nghiêm trọng, xứng đáng được các nhà phát triển và nhân viên an ninh chú ý.
![Giải mã lỗ hổng trình biên dịch Solidity và các biện pháp ứng phó])https://img-cdn.gateio.im/webp-social/moments-84f5083d8748f2aab71fd92671d999a7.webp(