Phân tích lỗ hổng của trình biên dịch Solidity và chiến lược đối 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. Nó là một chương trình máy tính đặc biệt, có trách nhiệm 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 mà CPU hoặc máy ảo bytecode có thể thực thi.
Mặc dù hầu hết các nhà phát triển và chuyên gia an ninh thường tập trung vào tính an toàn của mã ứng dụng, nhưng tính an toàn của chính trình biên dịch cũng không thể bị bỏ qua. Là một loại chương trình máy tính, trình biên dịch cũng có thể tồn tại các lỗ hổng an ninh, những lỗ hổng này có thể mang lại rủi ro an ninh nghiêm trọng trong một số trường hợp. Ví dụ, khi trình duyệt biên dịch và phân tích mã Javascript phía trước, có thể do lỗ hổng của động cơ phân tích Javascript, khiến người dùng bị tấn công khi truy cập vào các trang web độc hại, từ đó kẻ tấn công có thể khai thác 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 hoặc thậm chí toàn bộ hệ điều hành.
Trình biên dịch Solidity cũng không phải là ngoại lệ, nó có tồn tại lỗ hổng bảo mật trong nhiều phiên bản khác nhau.
Lỗ hổng trình biên dịch Solidity
Chức năng chính 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 có thể thực thi trên máy ảo Ethereum (EVM). Các mã lệnh EVM này được đóng gói và tải lên mạng Ethereum thông qua giao dịch, cuối cùng được EVM phân tích và thực thi.
Cần lưu ý rằng lỗ hổng trình biên dịch Solidity khác với lỗ hổng của EVM. Lỗ hổng EVM đề cập đến các vấn đề bảo mật khi máy ảo thực hiện các lệnh. Bởi vì kẻ tấn công có thể tải lên bất kỳ mã nào lên mạng Ethereum, mã này cuối cùng sẽ chạy trên mỗi chương trình khách hàng P2P Ethereum, nếu EVM có lỗ hổng bảo mật, có thể ảnh hưởng đến toàn bộ mạng Ethereum, gây ra từ chối dịch vụ (DoS) thậm chí dẫn đến việc toàn bộ blockchain bị kẻ tấn công kiểm soát. Tuy nhiên, do thiết kế EVM tương đối đơn giản và mã lõi không thường xuyên được cập nhật, nên khả năng xảy ra các vấn đề như vậy là khá thấp.
Lỗi biên dịch Solidity đề cập đến các vấn đề xảy ra khi trình biên dịch chuyển đổi mã Solidity thành mã EVM. Khác với việc 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 và không được thực hiện trên mạng 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 trong những mối nguy hiểm chính của lỗ hổng trong 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. Do 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 trong hợp đồng thông minh do trình biên dịch gây ra đều có thể dẫn đến mất mát tài sản của người dùng, từ đó gây ra hậu quả nghiêm trọng.
Các nhà phát triển và kiểm toán viên hợp đồng có thể chú trọng vào các vấn đề triển khai logic mã hợp đồng, cũng như các vấn đề bảo mật liên quan đến Solidity như tấn công tái nhập, tràn số nguyên, v.v. Tuy nhiên, chỉ thông qua việc kiểm toán logic mã nguồn hợp đồng, rất khó để phát hiện ra các lỗ hổng của trình biên dịch Solidity. Cần phải phân tích kết hợp 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ố ví dụ về lỗ hổng trình biên dịch Solidity thực sự, cho thấy các hình thức, nguyên nhân và tác hại cụ thể.
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 sớm hơn (>=0.1.6 <0.4.4).
Xem xét mã sau:
solidity
hợp đồng C {
uint32 a = 0x1234;
uint32 b = 0;
function f() public {
a += 1;
}
function run() public view returns (uint) {
return b;
}
}
Biến storage b không bị sửa đổi, vì vậy hàm run() nên trả về giá trị mặc định 0. Nhưng trong mã được tạo bởi trình biên dịch phiên bản lỗi, run() thực tế sẽ trả về 1.
Các nhà phát triển thông thường rất khó để phát hiện các vấn đề trong mã trên thông qua việc kiểm tra mã đơn giản. Mặc dù ví dụ này tương đối đơn giản và có thể không gây ra hậu quả nghiêm trọng, nhưng nếu biến b được sử dụng cho các mục đích quan trọng như xác thực quyền truy cập, ghi chép tài sản, thì tình huống không nhất quán với mong đợi này có thể dẫn đến nguy cơ an ninh nghiêm trọng.
Nguồn gốc của vấn đề này nằm ở chỗ EVM sử dụng máy ảo kiểu ngăn xếp, mỗi phần tử trong ngăn xếp có kích thước 32 byte (tức là kích thước của biến uint256). Mỗi slot trong bộ nhớ lưu trữ cũng có kích thước 32 byte. Trong khi đó, ngôn ngữ Solidity hỗ trợ các kiểu dữ liệu nhỏ hơn 32 byte như uint32, nên khi biên dịch viên 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 (clean up) trên các bit cao để đảm bảo tính chính xác của dữ liệu. Trong trường hợp đã nêu, khi phép cộng xảy ra tràn số nguyên, biên dịch viên không thực hiện làm sạch đúng cách với các bit cao của kết quả, dẫn đến việc bit 1 ở vị trí cao bị ghi vào bộ nhớ lưu trữ, cuối cùng ghi đè lên biến a và làm cho giá trị của biến b bị thay đổi thành 1.
SOL-2022-4 InlineAssemblyMemorySideEffects
Lỗi này tồn tại trong các phiên bản biên dịch viên từ >=0.8.13 đến <0.8.15. Xem xét mã sau:
solid
hợp đồng C {
function f() public pure returns (uint) {
lắp ráp {
mstore(0, 0x42)
}
uint x;
lắp ráp {
x := mload(0)
}
return x;
}
}
Trình biên dịch Solidity không chỉ đơn giản là dịch ngôn ngữ Solidity sang mã EVM. Nó còn tiến hành 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 mức tiêu thụ gas trong quá trình thực thi. Các thao tác tối ưu hóa này rất phổ biến trong các trình biên dịch ngôn ngữ cao cấp, nhưng do sự phức tạp của các tình huống cần xem xét, cũng dễ xuất hiện lỗi hoặc lỗ hổng bảo mật.
Lỗ hổng trong đoạn mã trên xuất phát từ loại thao tác tối ưu hóa này. Trình biên dịch cho rằng, nếu trong một hàm có mã sửa đổi dữ liệu tại địa chỉ bộ nhớ 0, nhưng không có bất kỳ nơi nào sử dụng dữ liệu đó sau đó, thì có thể trực tiếp loại bỏ mã sửa đổi bộ nhớ 0 để 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 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 cho một khối assembly đơn lẻ. Đối với mã PoC trên, việc ghi và truy cập vào bộ nhớ 0 tồn tại trong hai khối assembly khác nhau, trong khi trình biên dịch chỉ phân tích và tối ưu hóa khối assembly riêng lẻ. Bởi vì không có bất kỳ thao tác đọc nào sau khi ghi vào bộ nhớ 0 trong khối assembly đầu tiên, nên lệnh ghi này được xác định là dư thừa và sẽ bị xóa, 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 giá trị đúng mà mã trên nên trả về là 0x42.
Lỗ hổng này ảnh hưởng đến các phiên bản biên dịch viên từ >= 0.5.8 đến < 0.8.16. Xem xét mã sau:
solidity
hợp đồng C {
function f###string( calldata a[1] external pure returns )string memory( {
return abi.decode)abi.encode(a(, )string([1]));
}
}
Trong điều kiện bình thường, biến a được trả về bởi đoạn mã trên nên 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 loại calldata, nhưng đã xóa nhầm một số dữ liệu, dẫn đến việc thay đổ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 gọi external và emit event, Solidity sẽ ngầm định mã hóa các tham số bằng abi.encode, do đó xác suất xuất hiện mã lỗ hổng trên sẽ cao hơn so với cảm nhận trực quan.
![Phân tích lỗ hổng biên dịch Solidity và biện pháp ứng phó][0]https://img-cdn.gateio.im/webp-social/moments-c97428f89ed62d5ad8551cdb2ba30867.webp(
Đề xuất an toàn
Sau khi phân tích mô hình mối đe dọa từ các lỗ hổng của trình biên dịch Solidity và tổng hợp các lỗ hổng lịch sử, chúng tôi đưa ra các khuyến nghị sau cho các nhà phát triển và nhân viên an ninh.
) Đố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ũng có thể mang lại 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 thông qua kiểm tra mã, nhưng dễ dàng bị phát hiện trong giai đoạn kiểm tra. Do đó, bằng cách nâng cao tỷ lệ phủ mã, có thể tránh tối đa những vấn đề như vậy.
Cố gắng tránh sử dụng lắp ghép nội tuyến, mã hóa và giải mã abi cho mảng đa chiều và cấu trúc phức tạp, tránh việc theo đuổi kỹ thuật mới mà không có nhu cầu rõ ràng. Theo phân tích các lỗ hổng lịch sử, phần lớn các lỗ hổng liên quan đến lắp ghép nội tuyến, bộ mã hóa abi và các thao tác tương tự. Biên dịch viên 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ễ mắc sai lầm trong việc sử dụng khi áp dụng các tính năng mới, dẫn đến các vấn đề về an toàn.
Đố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ể do trình biên dịch Solidity gây ra. Mục kiểm tra tương ứng trong Smart Contract Weakness Classification ###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 SDL 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à có thể 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.
Tuy nhiên, không cần quá hoảng sợ về lỗ hổng của trình biên dịch, hầu hết các lỗ hổng của trình biên dịch chỉ được kích hoạt trong các mẫu mã cụ thể, không nhất thiết hợp đồng được biên dịch bằng phiên bản trình biên dịch có lỗ hổng sẽ luôn tồn tại rủi ro an ninh, ảnh hưởng thực tế đến an ninh cần được đánh giá cụ thể dựa trên tình huống của dự án.
Tài nguyên hữu ích
Bài viết 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 của các phiên bản biên dịch. Có thể dựa vào đây để tự động kiểm tra phiên bản biên dịch trong quá trình CI/CD, thông báo các lỗ hổng bảo mật tồn tại trong phiên bản hiện tại.
Trên trang Contract -> Code của Etherscan, biểu tượng hình tam giác cảm thán ở góc trên bên phải có thể cho biết các lỗ hổng bảo mật hiện có của trình biên dịch phiên bản hiện tại.
![Phân tích lỗ hổng trình biên dịch Solidity và biện pháp ứng phó])https://img-cdn.gateio.im/webp-social/moments-84f5083d8748f2aab71fd92671d999a7.webp(
Tóm tắt
Bài viết này bắt đầu từ khái niệm cơ bản về trình biên dịch, giới thiệu các lỗ hổng của trình biên dịch Solidity, và phân tích những rủi ro về bảo mật mà nó có thể gây ra trong môi trường phát triển Ethereum thực tế. Cuối cùng, bài viết cung cấp một số lời khuyên thực tiễn về bảo mật cho các nhà phát triển và nhân viên an ninh. Bằng cách hiểu những lỗ hổng này và thực hiện các biện pháp phòng ngừa tương ứng, chúng ta có thể bảo vệ tốt hơn an toàn của hợp đồng thông minh, giảm thiểu rủi ro mất mát tài sản tiềm tàng.
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.
12 thích
Phần thưởng
12
6
Chia sẻ
Bình luận
0/400
0xLuckbox
· 10giờ trước
Lỗi logic gì đó thật sự làm đau đầu. Đi thôi, đi thôi.
Xem bản gốcTrả lời0
LidoStakeAddict
· 07-30 09:56
Tràn rồi, mã cần phải sửa.
Xem bản gốcTrả lời0
StablecoinArbitrageur
· 07-30 09:26
*điều chỉnh kính* hmm... nói theo thống kê, rủi ro biên dịch đang bị định giá quá thấp trong các phép tính tvl defi
Xem bản gốcTrả lời0
BoredStaker
· 07-30 09:24
Khi nào có thể nói chuyện một cách bình thường!
Xem bản gốcTrả lời0
ArbitrageBot
· 07-30 09:24
Lại phải làm cái lỗ compiler nữa sao?
Xem bản gốcTrả lời0
APY追逐者
· 07-30 09:15
Đến lúc tốn gas khi săn lùng mới nhớ đến lỗ hổng biên dịch.
Phân tích lỗ hổng trình biên dịch Solidity và thực hành phòng ngừa an toàn
Phân tích lỗ hổng của trình biên dịch Solidity và chiến lược đối 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. Nó là một chương trình máy tính đặc biệt, có trách nhiệm 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 mà CPU hoặc máy ảo bytecode có thể thực thi.
Mặc dù hầu hết các nhà phát triển và chuyên gia an ninh thường tập trung vào tính an toàn của mã ứng dụng, nhưng tính an toàn của chính trình biên dịch cũng không thể bị bỏ qua. Là một loại chương trình máy tính, trình biên dịch cũng có thể tồn tại các lỗ hổng an ninh, những lỗ hổng này có thể mang lại rủi ro an ninh nghiêm trọng trong một số trường hợp. Ví dụ, khi trình duyệt biên dịch và phân tích mã Javascript phía trước, có thể do lỗ hổng của động cơ phân tích Javascript, khiến người dùng bị tấn công khi truy cập vào các trang web độc hại, từ đó kẻ tấn công có thể khai thác 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 hoặc thậm chí toàn bộ hệ điều hành.
Trình biên dịch Solidity cũng không phải là ngoại lệ, nó có tồn tại lỗ hổng bảo mật trong nhiều phiên bản khác nhau.
Lỗ hổng trình biên dịch Solidity
Chức năng chính 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 có thể thực thi trên máy ảo Ethereum (EVM). Các mã lệnh EVM này được đóng gói và tải lên mạng Ethereum thông qua giao dịch, cuối cùng được EVM phân tích và thực thi.
Cần lưu ý rằng lỗ hổng trình biên dịch Solidity khác với lỗ hổng của EVM. Lỗ hổng EVM đề cập đến các vấn đề bảo mật khi máy ảo thực hiện các lệnh. Bởi vì kẻ tấn công có thể tải lên bất kỳ mã nào lên mạng Ethereum, mã này cuối cùng sẽ chạy trên mỗi chương trình khách hàng P2P Ethereum, nếu EVM có lỗ hổng bảo mật, có thể ảnh hưởng đến toàn bộ mạng Ethereum, gây ra từ chối dịch vụ (DoS) thậm chí dẫn đến việc toàn bộ blockchain bị kẻ tấn công kiểm soát. Tuy nhiên, do thiết kế EVM tương đối đơn giản và mã lõi không thường xuyên được cập nhật, nên khả năng xảy ra các vấn đề như vậy là khá thấp.
Lỗi biên dịch Solidity đề cập đến các vấn đề xảy ra khi trình biên dịch chuyển đổi mã Solidity thành mã EVM. Khác với việc 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 và không được thực hiện trên mạng 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 trong những mối nguy hiểm chính của lỗ hổng trong 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. Do 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 trong hợp đồng thông minh do trình biên dịch gây ra đều có thể dẫn đến mất mát tài sản của người dùng, từ đó gây ra hậu quả nghiêm trọng.
Các nhà phát triển và kiểm toán viên hợp đồng có thể chú trọng vào các vấn đề triển khai logic mã hợp đồng, cũng như các vấn đề bảo mật liên quan đến Solidity như tấn công tái nhập, tràn số nguyên, v.v. Tuy nhiên, chỉ thông qua việc kiểm toán logic mã nguồn hợp đồng, rất khó để phát hiện ra các lỗ hổng của trình biên dịch Solidity. Cần phải phân tích kết hợp 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ố ví dụ về lỗ hổng trình biên dịch Solidity thực sự, cho thấy các hình thức, nguyên nhân và tác hại cụ thể.
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 sớm hơn (>=0.1.6 <0.4.4).
Xem xét mã sau:
solidity hợp đồng C { uint32 a = 0x1234; uint32 b = 0; function f() public { a += 1; } function run() public view returns (uint) { return b; } }
Biến storage b không bị sửa đổi, vì vậy hàm run() nên trả về giá trị mặc định 0. Nhưng trong mã được tạo bởi trình biên dịch phiên bản lỗi, run() thực tế sẽ trả về 1.
Các nhà phát triển thông thường rất khó để phát hiện các vấn đề trong mã trên thông qua việc kiểm tra mã đơn giản. Mặc dù ví dụ này tương đối đơn giản và có thể không gây ra hậu quả nghiêm trọng, nhưng nếu biến b được sử dụng cho các mục đích quan trọng như xác thực quyền truy cập, ghi chép tài sản, thì tình huống không nhất quán với mong đợi này có thể dẫn đến nguy cơ an ninh nghiêm trọng.
Nguồn gốc của vấn đề này nằm ở chỗ EVM sử dụng máy ảo kiểu ngăn xếp, mỗi phần tử trong ngăn xếp có kích thước 32 byte (tức là kích thước của biến uint256). Mỗi slot trong bộ nhớ lưu trữ cũng có kích thước 32 byte. Trong khi đó, ngôn ngữ Solidity hỗ trợ các kiểu dữ liệu nhỏ hơn 32 byte như uint32, nên khi biên dịch viên 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 (clean up) trên các bit cao để đảm bảo tính chính xác của dữ liệu. Trong trường hợp đã nêu, khi phép cộng xảy ra tràn số nguyên, biên dịch viên không thực hiện làm sạch đúng cách với các bit cao của kết quả, dẫn đến việc bit 1 ở vị trí cao bị ghi vào bộ nhớ lưu trữ, cuối cùng ghi đè lên biến a và làm cho giá trị của biến b bị thay đổi thành 1.
SOL-2022-4 InlineAssemblyMemorySideEffects
Lỗi này tồn tại trong các phiên bản biên dịch viên từ >=0.8.13 đến <0.8.15. Xem xét mã sau:
solid hợp đồng C { function f() public pure returns (uint) { lắp ráp { mstore(0, 0x42) } uint x; lắp ráp { x := mload(0) } return x; } }
Trình biên dịch Solidity không chỉ đơn giản là dịch ngôn ngữ Solidity sang mã EVM. Nó còn tiến hành 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 mức tiêu thụ gas trong quá trình thực thi. Các thao tác tối ưu hóa này rất phổ biến trong các trình biên dịch ngôn ngữ cao cấp, nhưng do sự phức tạp của các tình huống cần xem xét, cũng dễ xuất hiện lỗi hoặc lỗ hổng bảo mật.
Lỗ hổng trong đoạn mã trên xuất phát từ loại thao tác tối ưu hóa này. Trình biên dịch cho rằng, nếu trong một hàm có mã sửa đổi dữ liệu tại địa chỉ bộ nhớ 0, nhưng không có bất kỳ nơi nào sử dụng dữ liệu đó sau đó, thì có thể trực tiếp loại bỏ mã sửa đổi bộ nhớ 0 để 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 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 cho một khối assembly đơn lẻ. Đối với mã PoC trên, việc ghi và truy cập vào bộ nhớ 0 tồn tại trong hai khối assembly khác nhau, trong khi trình biên dịch chỉ phân tích và tối ưu hóa khối assembly riêng lẻ. Bởi vì không có bất kỳ thao tác đọc nào sau khi ghi vào bộ nhớ 0 trong khối assembly đầu tiên, nên lệnh ghi này được xác định là dư thừa và sẽ bị xóa, 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 giá trị đúng mà mã trên nên trả về là 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 từ >= 0.5.8 đến < 0.8.16. Xem xét mã sau:
solidity hợp đồng C { function f###string( calldata a[1] external pure returns )string memory( { return abi.decode)abi.encode(a(, )string([1])); } }
Trong điều kiện bình thường, biến a được trả về bởi đoạn mã trên nên 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 loại calldata, nhưng đã xóa nhầm một số dữ liệu, dẫn đến việc thay đổ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 gọi external và emit event, Solidity sẽ ngầm định mã hóa các tham số bằng abi.encode, do đó xác suất xuất hiện mã lỗ hổng trên sẽ cao hơn so với cảm nhận trực quan.
![Phân tích lỗ hổng biên dịch Solidity và biện pháp ứng phó][0]https://img-cdn.gateio.im/webp-social/moments-c97428f89ed62d5ad8551cdb2ba30867.webp(
Đề xuất an toàn
Sau khi phân tích mô hình mối đe dọa từ các lỗ hổng của trình biên dịch Solidity và tổng hợp các lỗ hổng lịch sử, chúng tôi đưa ra các khuyến nghị sau cho các nhà phát triển và nhân viên an ninh.
) Đố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ũng có thể mang lại 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 thông qua kiểm tra mã, nhưng dễ dàng bị phát hiện trong giai đoạn kiểm tra. Do đó, bằng cách nâng cao tỷ lệ phủ mã, có thể tránh tối đa những vấn đề như vậy.
Cố gắng tránh sử dụng lắp ghép nội tuyến, mã hóa và giải mã abi cho mảng đa chiều và cấu trúc phức tạp, tránh việc theo đuổi kỹ thuật mới mà không có nhu cầu rõ ràng. Theo phân tích các lỗ hổng lịch sử, phần lớn các lỗ hổng liên quan đến lắp ghép nội tuyến, bộ mã hóa abi và các thao tác tương tự. Biên dịch viên 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ễ mắc sai lầm trong việc sử dụng khi áp dụng các tính năng mới, dẫn đến các vấn đề về an toàn.
Đố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ể do trình biên dịch Solidity gây ra. Mục kiểm tra tương ứng trong Smart Contract Weakness Classification ###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 SDL 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à có thể 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.
Tuy nhiên, không cần quá hoảng sợ về lỗ hổng của trình biên dịch, hầu hết các lỗ hổng của trình biên dịch chỉ được kích hoạt trong các mẫu mã cụ thể, không nhất thiết hợp đồng được biên dịch bằng phiên bản trình biên dịch có lỗ hổng sẽ luôn tồn tại rủi ro an ninh, ảnh hưởng thực tế đến an ninh cần được đánh giá cụ thể dựa trên tình huống của dự án.
Tài nguyên hữu ích
![Phân tích lỗ hổng trình biên dịch Solidity và biện pháp ứng phó])https://img-cdn.gateio.im/webp-social/moments-84f5083d8748f2aab71fd92671d999a7.webp(
Tóm tắt
Bài viết này bắt đầu từ khái niệm cơ bản về trình biên dịch, giới thiệu các lỗ hổng của trình biên dịch Solidity, và phân tích những rủi ro về bảo mật mà nó có thể gây ra trong môi trường phát triển Ethereum thực tế. Cuối cùng, bài viết cung cấp một số lời khuyên thực tiễn về bảo mật cho các nhà phát triển và nhân viên an ninh. Bằng cách hiểu những lỗ hổng này và thực hiện các biện pháp phòng ngừa tương ứng, chúng ta có thể bảo vệ tốt hơn an toàn của hợp đồng thông minh, giảm thiểu rủi ro mất mát tài sản tiềm tàng.