Phân tích lỗ hổng của trình biên dịch Solidity và chiến lược an toàn cho nhà phát triển

Phân tích lỗ hổng trình biên dịch Solidity và các 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. Nó là một chương trình máy tính, chức năng chính là chuyển đổi mã nguồn ngôn ngữ lập trình bậc 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à chuyên gia an ninh thường chú ý đến tính an toàn của mã ứng dụng, nhưng có thể bỏ qua các vấn đề 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 tồn tại lỗ hổng an ninh, và những lỗ hổng này có thể mang lại rủi ro an ninh nghiêm trọng trong những trường hợp nhất định. Ví dụ, trong quá trình trình duyệt biên dịch và phân tích thực thi mã JavaScript phía trước, có thể do lỗ hổng của động cơ phân tích 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 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.

Trình biên dịch Solidity cũng không phải là ngoại lệ, theo cảnh báo an ninh của nhóm phát triển Solidity, có nhiều lỗ hổng bảo mật tồn tại trong nhiều phiên bản khác nhau của trình biên dịch Solidity.

Lỗi biên dịch viên 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ã hướng dẫn EVM( của máy ảo Ethereum ), mã hướng dẫn 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ân biệt các lỗ hổng của trình biên dịch Solidity với các lỗ hổng của chính EVM. Các lỗ hổng của EVM đề cập đến các vấn đề 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 vào Ethereum, mã này cuối cùng sẽ chạy trong mỗi chương trình khách P2P của Ethereum, nếu EVM có lỗ hổng bảo mật, nó 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í 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ã lõi không được cập nhật thường xuyên, nên khả năng xảy ra các vấn đề trên là khá thấp.

Lỗi biên dịch Solidity đề cập đến các vấn đề khi trình biên dịch 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 các nhà phát triển hợp đồng thông minh, mà không chạy trên Ethereum. Do đó, lỗi biên dịch Solidity sẽ không ảnh hưởng đến mạng Ethereum.

Một trong những nguy cơ chính của lỗ hổng trình biên dịch Solidity là có thể dẫn đến việc mã EVM được tạo ra không nhất quán với mong đợi của các 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, nên bất kỳ lỗi hợp đồng thông minh nào do trình biên dịch gây ra đề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 vấn đề thực hiện logic mã hợp đồng, cũng như các vấn đề an ninh ở cấp độ Solidity như tấn công tái nhập và tràn số nguyên. Tuy nhiên, rất khó để phát hiện 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. 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.

Phân tích lỗ hổng của trình biên dịch Solidity và các biện pháp ứng phó

Ví dụ về lỗ hổng biên dịch viên Solidity

Dưới đây là một số trường hợp thực tế về lỗ hổng biên dịch Solidity, cho thấy hình thức cụ thể, nguyên nhân và tác hại của nó.

SOL-2016-9 HighOrderByteCleanStorage

Lỗ hổng này tồn tại trong các phiên bản 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 (uint32) { return b; } }

Biến lưu trữ b không bị sửa đổi, do đó 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 có lỗ hổng, run() sẽ trả về 1.

Nếu không hiểu lỗ hổng của trình biên dịch này, các nhà phát triển thông thường rất khó để phát hiện lỗi có trong đoạn mã trên thông qua việc xem xét mã đơn giản. Đây chỉ là một ví dụ đơn giản, không gây ra hậu quả đặc biệt nghiêm trọng. Nhưng nếu biến b được sử dụng cho xác thực quyền hạn, kế toán tài sản, v.v., sự không nhất quán này so với mong đợi có thể dẫn đến những vấn đề rất nghiêm trọng.

Nguyên nhân gây ra hiện tượng kỳ lạ này là do EVM sử dụng máy ảo dựa trên 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 ngăn trong bộ nhớ cơ sở storage cũng có kích thước 32 byte. Trong ngôn ngữ Solidity, có hỗ trợ cho các kiểu dữ liệu nhỏ hơn 32 byte như uint32, khi biên dịch viên xử lý các biến này, cần thực hiện các thao tác xóa thích hợp với các bit cao ( clean up ) để đảm bảo tính chính xác của dữ liệu. Trong trường hợp trên, khi phép cộng tạo ra tràn số nguyên, biên dịch viên không thực hiện đúng việc xóa các bit cao của kết quả, dẫn đến việc bit 1 ở vị trí cao được ghi vào storage, cuối cùng ghi đè lên biến a và làm thay đổi giá trị của biến b thành 1.

SOL-2022-4 InlineAssemblyMemorySideEffects

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; lắp ráp { x := mload(0) } return x; } }

Lỗ hổng 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. Biên dịch viên Solidity trong quá trình chuyển đổi ngôn ngữ Solidity thành mã EVM không chỉ đơn thuần là dịch. Nó cũng thực hiện phân tích sâu về điều khiển luồng 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 để giảm kích thước mã được sinh ra, tối ưu hóa mức tiêu thụ gas trong quá trình thực thi. Những hoạt động tối ưu hóa kiểu này rất phổ biến trong các biên dịch viên của nhiều ngôn ngữ lập trình cấp cao khác nhau, nhưng do các tình huống cần xem xét rất phức tạp, nên cũng rất dễ xuất hiện lỗi hoặc lỗ hổng bảo mật.

Lỗ hổng của đoạn mã trên xuất phát từ các thao tác tối ưu hóa kiểu này. Giả sử trong một hàm có mã sửa đổi dữ liệu tại vị trí 0 của bộ nhớ, nhưng không có bất kỳ nơi nào trong phần sau sử dụng dữ liệu đó, thì thực tế có thể trực tiếp loại bỏ mã sửa đổi bộ nhớ 0, từ đó tiết kiệm gas và 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 thực hiện mã của trình biên dịch Solidity cụ thể, loại tối ưu hóa này chỉ được áp dụng cho một khối assembly duy nhất. Đối với mã PoC trên, việc ghi và truy cập vào bộ nhớ 0 nằm 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ẻ. Do 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ập luận rằng lệnh ghi này là thừa, nó 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ế mã trên nên trả về giá trị đúng là 0x42.

SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup

Xem xét mã sau:

solid hợp đồng C { function f(string[1] calldata a) external returns (string memory) { return abi.decode(abi.encode(a), (string)); } }

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 dưới 0.8.16. Thông thường, biến a được trả về bởi 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 "".

Lỗi này xảy ra 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 các dữ liệu khác liền kề, gây ra sự không nhất quán giữa dữ liệu được mã hóa và giải mã.

Cần lưu ý rằng, khi thực hiện gọi bên ngoài và phát sự kiện, Solidity sẽ ngầm định mã hóa tham số bằng abi.encode, vì vậy xác suất xuất hiện của mã lỗ hổng trên sẽ cao hơn so với cảm giác trực quan.

Phân tích lỗ hổng trình biên dịch Solidity và các biện pháp đối phó

Đề xuất an toàn

Đội ngũ an ninh blockchain của Cobo đã phân tích mô hình đe dọa của lỗ hổng trình biên dịch Solidity và tổng hợp các lỗ hổng lịch sử, đư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 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 đến những 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 thử nghiệm đơn vị. Hầu hết các lỗi ở cấp độ biên dịch sẽ dẫn đến kết quả thực thi mã không nhất quán với dự kiến. Những vấn đề này rất khó phát hiện qua việc xem xét mã, nhưng rất dễ bị lộ ra trong giai đoạn thử nghiệm. Do đó, bằng cách nâng cao tỷ lệ phủ mã, có thể tối đa hóa việc tránh những vấn đề như vậy.

  • Cố gắng tránh việc sử dụng lắp ghép nội tuyến, mã hóa và giải mã abi cho mảng nhiều chiều và cấu trúc phức tạp, nếu không có yêu cầu rõ ràng thì nên tránh việc sử dụng đặc điểm ngôn ngữ mới và tính năng thử nghiệm một cách mù quáng chỉ để thể hiện kỹ năng. Theo phân tích của đội ngũ an ninh Cobo về các lỗ hổng lịch sử trong Solidity, 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 khác. Trình biên dịch thực sự dễ gặp lỗi hơn khi xử lý các đặc điểm ngôn ngữ phức tạp. Mặt khác, các nhà phát triển cũng dễ gặp phải những lầm lẫn khi sử dụng các đặc điểm mới, dẫn đến các vấn đề về an ninh.

Đối với nhân viên an ninh:

  • Trong quá trình kiểm tra 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 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 nhóm 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.

  • Nhưng 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ể, và 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 có rủi ro về an toàn, ảnh hưởng thực tế đến an toàn cần được đánh giá cụ thể dựa trên tình hình dự án.

Tài nguyên hữu ích:

  • Các bài đăng Cảnh báo An ninh được đội ngũ Solidity phát hành định kỳ:

  • Danh sách lỗi được cập nhật định kỳ từ repo chính thức của Solidity:

  • Danh sách lỗi của các phiên bản biên dịch:

Mũi tên hình tam giác ở góc trên bên phải trang Code có thể cho biết 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 tắt

Bài viết bắt đầu từ các khái niệm cơ bản về trình biên dịch, giới thiệu lỗ hổng của trình biên dịch Solidity và phân tích những rủi ro an ninh có thể xảy ra trong môi trường phát triển Ethereum thực tế, cuối cùng cung cấp cho các nhà phát triển và nhân viên an ninh một số lời khuyên bảo mật thực tế.

Phân tích lỗ hổng trình biên dịch Solidity và biện pháp đối phó

ETH2.9%
Xem bản gốc
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.
  • Phần thưởng
  • 5
  • Chia sẻ
Bình luận
0/400
staking_grampsvip
· 16giờ trước
Lỗ hổng nhiều quá, ai dám phát triển nữa?
Xem bản gốcTrả lời0
JustHereForMemesvip
· 07-23 05:15
Biên dịch viên cũng có vấn đề lớn quá. Chạy thôi.
Xem bản gốcTrả lời0
BakedCatFanboyvip
· 07-22 21:56
Lỗ hổng toàn dựa vào việc nhặt phải không?
Xem bản gốcTrả lời0
OldLeekNewSicklevip
· 07-22 21:56
Cao cấp đồ ngốc nhìn lỗ hổng, đồ ngốc tầng dưới chú ý Sổ lệnh
Xem bản gốcTrả lời0
RektCoastervip
· 07-22 21:52
Trình biên dịch cũng gặp lỗi? Thật sự làm tôi bất ngờ.
Xem bản gốcTrả lời0
  • Ghim
Giao dịch tiền điện tử mọi lúc mọi nơi
qrCode
Quét để tải xuống ứng dụng Gate
Cộng đồng
Tiếng Việt
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)