Mono Audit logo

Danh sách lỗ hổng phổ biến

Lỗ hổng hợp đồng thông minh có thể dẫn đến hậu quả thảm khốc và thường là không thể đảo ngược sau khi được triển khai. Việc khai thác những điểm yếu này là một thách thức lớn đối với bảo mật blockchain, dẫn đến việc hàng tỷ đô la tài sản bị đánh cắp và làm suy giảm niềm tin của người dùng. Do đó, việc đạt được bảo mật hợp đồng thông minh mạnh mẽ là tối quan trọng. Nó đòi hỏi nhiều hơn là chỉ triển khai mã lên blockchain. Nó yêu cầu các quy trình code an toàn nghiêm ngặt, kiểm thử kỹ lưỡng và thường là kiểm toán hợp đồng thông minh độc lập để xác định các vấn đề trước khi chúng có thể bị khai thác. Hiểu rõ các cạm bẫy phổ biến – từ các cuộc tấn công tái nhập như vụ hack DAO khét tiếng đến các lỗi logic tinh vi và lỗ hổng Ethereum như tràn số nguyên – là bước quan trọng đầu tiên để giảm thiểu rủi ro. Trang này cung cấp một cái nhìn tổng quan thiết yếu về các lỗ hổng hợp đồng thông minh thường gặp nhằm nâng cao nhận thức và thúc đẩy sự phát triển an toàn hơn trong hệ sinh thái phi tập trung.

  1. Reentrancy
  2. Unexpected Ether
  3. DoS
  4. Overflow
  5. tx.origin Authentication
  6. Access Control
  7. Weak Randomness
  8. Hash Collision
  9. Precision Loss
  10. msg.value in loop
  11. ecrecover
  12. Replay
  13. Inheritance Order
  14. MEV
Link

Reentrancy

Lỗ hổng tái nhập (Reentrancy) là một trong những lỗ hổng hợp đồng thông minh sớm nhất và có sức tàn phá nặng nề nhất, gây ra các vụ khai thác lịch sử nghiêm trọng như vụ hack The DAO, dẫn đến hard fork của Ethereum. Nó vẫn là một mối đe dọa nghiêm trọng với những hậu quả tiềm ẩn khủng khiếp, có khả năng rút cạn toàn bộ tiền trong hợp đồng.

Lỗ hổng này xuất phát từ một phản mẫu lập trình phổ biến, thường được mang sang từ thói quen Web2: thực hiện các tương tác bên ngoài (như chuyển Ether hoặc gọi một hợp đồng khác) trước khi cập nhật trạng thái nội bộ của hợp đồng (ví dụ: số dư của người dùng). Khi một hợp đồng gửi Ether hoặc gọi một hợp đồng bên ngoài, nó tạm thời chuyển quyền kiểm soát thực thi. Kẻ tấn công kiểm soát hợp đồng nhận có thể khai thác điều này bằng cách ngay lập tức gọi ngược lại vào hợp đồng gốc, thường thông qua hàm receive hoặc fallback được kích hoạt bởi việc chuyển Ether, hoặc thông qua các hook (móc nối) của chuẩn token.

Bởi vì trạng thái của hợp đồng gốc (ví dụ: số dư của kẻ tấn công) chưa được cập nhật, lệnh gọi tái nhập vượt qua các kiểm tra ban đầu, cho phép kẻ tấn công lặp lại hành động (như rút tiền) nhiều lần trong cùng một giao dịch. Vòng lặp đệ quy này tiếp tục cho đến khi tiền bị rút cạn hoặc đạt đến giới hạn gas.

Mặc dù cuộc tấn công cổ điển liên quan đến một hàm duy nhất, các biến thể phức tạp hơn vẫn tồn tại, chẳng hạn như tái nhập liên hàm (khai thác trạng thái được chia sẻ giữa các hàm) và tái nhập chỉ đọc (thao túng việc đọc trạng thái thông qua các hàm xem - view functions).

Biện pháp phòng thủ chính là mô hình Kiểm tra - Hiệu ứng - Tương tác (Checks-Effects-Interactions - CEI): thực hiện tất cả các kiểm tra cần thiết, sau đó cập nhật các biến trạng thái (hiệu ứng), và chỉ sau những cập nhật này mới tương tác với các hợp đồng bên ngoài.

Link

Unexpected Ether

Lỗ hổng "Thao túng số dư do nhận Ether bất ngờ" phát sinh do các hợp đồng thông minh Solidity có thể nhận Ether thông qua các cơ chế bỏ qua các hàm `receive` và `fallback` được xác định của chúng. Các phương pháp chính cho việc bơm Ether "bất ngờ" này là opcode `selfdestruct`, opcode này buộc chuyển số dư của một hợp đồng đang tự hủy sang một địa chỉ được chỉ định, và các giao dịch coinbase (phần thưởng khối).

Vấn đề cốt lõi là các giao dịch chuyển khoản bắt buộc này trực tiếp làm tăng số dư Ether của hợp đồng (`address(this).balance`) mà không kích hoạt logic được lập trình của hợp đồng để xử lý quỹ đến. Điều này phá vỡ một giả định hoặc bất biến phổ biến, thường là ngầm định: rằng số dư thực tế của hợp đồng phản ánh chính xác tổng số tiền đã được xử lý thông qua các hàm `payable` dự kiến của nó hoặc được hạch toán nội bộ.

Các hợp đồng sử dụng không chính xác `address(this).balance` cho các kiểm tra logic quan trọng sẽ trở nên dễ bị tấn công. Ví dụ, các hợp đồng có thể kiểm tra rằng `address(this).balance == soTienDuKien`. Kẻ tấn công có thể khai thác điều này bằng cách sử dụng `selfdestruct` để gửi một lượng nhỏ Ether, qua đó thao túng số dư. Điều này có thể dẫn đến:

Từ chối dịch vụ (DoS): Các kiểm tra đẳng thức nghiêm ngặt có thể bị phá vỡ vĩnh viễn, khiến các hàm không thể sử dụng được.

Thao túng logic: Các ngưỡng có thể đạt được sớm, kích hoạt các thay đổi trạng thái hoặc thanh toán ngoài ý muốn.

Vi phạm `assert`: Trong một số trường hợp hiếm hoi, số dư không mong đợi có thể dẫn đến mâu thuẫn trạng thái nội bộ khiến `assert()` thất bại, tiêu tốn toàn bộ gas giao dịch.

Biện pháp giảm thiểu cơ bản là không bao giờ dựa vào `address(this).balance` cho logic hợp đồng. Thay vào đó, các hợp đồng phải duy trì việc hạch toán nội bộ chính xác bằng cách sử dụng các biến trạng thái chuyên dụng, chỉ cập nhật chúng trong các hàm xử lý quỹ hợp lệ. Tất cả các kiểm tra quan trọng và chuyển đổi trạng thái phải dựa trên các biến nội bộ đáng tin cậy này.

Link

DoS

Lỗ hổng Tấn công Từ chối Dịch vụ (Denial of Service, DoS) trong hợp đồng thông minh Solidity nhằm mục đích làm gián đoạn hoặc dừng hoàn toàn chức năng dự kiến của hợp đồng, ngăn người dùng hợp lệ truy cập vào các dịch vụ của nó. "Dịch vụ" bị từ chối chính là khả năng thực thi các hàm của hợp đồng theo như đã được lập trình và mong đợi bởi người dùng.

Kẻ tấn công đạt được điều này bằng cách khai thác các lỗ hổng trong logic mã của hợp đồng, thao túng các giới hạn tài nguyên (chủ yếu là gas) hoặc lợi dụng các phụ thuộc bên ngoài.

Các mẫu phổ biến bao gồm:

Cạn kiệt Gas (Gas Exhaustion): Tạo ra các giao dịch hoặc thao túng trạng thái để làm cho chi phí thực thi hàm vượt quá giới hạn gas của khối hoặc giới hạn gas của giao dịch. Điều này thường liên quan đến việc kích hoạt các hoạt động tốn kém về mặt tính toán hoặc, rất phổ biến là, lặp qua các mảng không giới hạn có kích thước có thể tăng lên vô hạn. Khi dữ liệu tăng lên, chi phí gas của vòng lặp vượt quá giới hạn, khiến hàm không thể sử dụng được.

Hoàn trả Bất ngờ (Unexpected Reverts): Gây ra việc hoàn trả (revert) bất ngờ cho các hàm quan trọng. Điều này có thể được kích hoạt bằng cách buộc các lệnh gọi bên ngoài thất bại (ví dụ: gửi tiền đến một hợp đồng từ chối chúng), thao túng trạng thái để làm thất bại các điều kiện `require`, hoặc các ngoại lệ khác không được xử lý. Nếu một hợp đồng phụ thuộc vào một lệnh gọi bên ngoài bị lỗi (có thể do cố ý gây ra), toàn bộ hoạt động có thể bị chặn.

Bản chất bất biến của hợp đồng thông minh làm cho DoS trở nên đặc biệt nghiêm trọng. Một cuộc tấn công thành công có thể dẫn đến tiền bị khóa vĩnh viễn hoặc chức năng không thể phục hồi, vì mã hợp đồng không thể dễ dàng sửa chữa sau khi triển khai.

Việc giảm thiểu dựa vào các thực hành lập trình an toàn: sử dụng các hoạt động có giới hạn thay vì các vòng lặp không giới hạn, ưu tiên các mẫu "pull-payment" (thanh toán kéo) hơn là đẩy tiền cho người dùng ("push-payment"), điều này cô lập các lỗi chuyển tiền, xử lý mạnh mẽ các lỗi gọi hàm bên ngoài (ví dụ: sử dụng `try/catch` khi thích hợp) và quản lý gas cẩn thận.

Link

Overflow

Lỗ hổng tràn số (integer overflow) và tràn dưới số nguyên (integer underflow) đại diện cho một mối đe dọa dai dẳng trong lĩnh vực bảo mật hợp đồng thông minh, bắt nguồn từ những hạn chế cơ bản của phép toán số nguyên kích thước cố định. Tràn số xảy ra khi kết quả của một phép toán số học vượt quá giá trị tối đa cho kiểu dữ liệu của nó, và tràn dưới xảy ra khi kết quả thấp hơn giá trị tối thiểu.

Trong lịch sử, ở các phiên bản Solidity trước 0.8.0, những lỗi số học này khiến giá trị âm thầm "quay vòng" (wrap around). Ví dụ, cộng 1 vào giá trị uint8 tối đa (255) sẽ cho kết quả là 0, và trừ 1 từ 0 sẽ cho kết quả là 255. Hành vi có thể dự đoán nhưng không được kiểm tra này là một nguồn khai thác lỗ hổng (exploit) lớn, cho phép kẻ tấn công thao túng số dư token (ví dụ: đúc số lượng lớn hoặc rút cạn tiền bằng cách làm cho số dư quay vòng), bỏ qua các kiểm tra bảo mật quan trọng, làm hỏng trạng thái hợp đồng hoặc gây ra từ chối dịch vụ (denial-of-service).

Phiên bản Solidity 0.8.0 đã giới thiệu một cải tiến bảo mật quan trọng: các phép toán số học tiêu chuẩn (+, -, *, /) giờ đây thực hiện kiểm tra tràn số và tràn dưới theo mặc định. Nếu một phép toán dẫn đến việc quay vòng, giao dịch sẽ được hoàn tác (revert) thay vì làm hỏng trạng thái một cách âm thầm.

Tuy nhiên, rủi ro không hoàn toàn bị loại bỏ. Các nhà phát triển có thể bỏ qua các kiểm tra mặc định này một cách rõ ràng bằng cách sử dụng các khối `unchecked`, thường là để tối ưu hóa gas. Mã trong khối `unchecked` quay trở lại hành vi quay vòng âm thầm nguy hiểm của các phiên bản trước 0.8.0 và đòi hỏi việc xác thực cực kỳ cẩn thận. Tương tự, mã assembly EVM cấp thấp không được hưởng lợi từ các kiểm tra của Solidity, yêu cầu quản lý an toàn thủ công cho các phép toán số học. Ngoài ra, các phép dịch chuyển bit (shift operations) (<<, >>) vẫn không được kiểm tra ngay cả trong chế độ mặc định >=0.8.0 và sẽ cắt bớt kết quả. Việc ép kiểu không đúng cách (improper type casting) (ví dụ: chuyển đổi một uint256 lớn sang một kiểu nhỏ hơn như uint8) cũng có thể dẫn đến việc cắt bớt giá trị, có khả năng gây ra hành vi không mong muốn trong các phép tính tiếp theo.

Do đó, mặc dù đã được giảm thiểu đáng kể theo mặc định trong Solidity hiện đại, tràn số/tràn dưới số nguyên vẫn là một lỗ hổng liên quan, đặc biệt là trong các hợp đồng cũ (legacy contracts), mã sử dụng khối `unchecked` hoặc assembly, và thông qua các hoạt động cụ thể không được kiểm tra hoặc chuyển đổi kiểu bất cẩn.

Link

tx.origin Authentication

Việc sử dụng `tx.origin` cho logic ủy quyền trong các hợp đồng thông minh (smart contracts) Solidity tạo thành một lỗ hổng bảo mật nghiêm trọng. Điều này xuất phát từ sự hiểu lầm cơ bản về hành vi của nó so với `msg.sender`. Trong khi `tx.origin` luôn xác định EOA (Externally Owned Account / Tài khoản sở hữu bên ngoài) đã khởi tạo giao dịch, `msg.sender` xác định bên gọi ngay lập tức. Việc sử dụng `tx.origin` để kiểm tra quyền thất bại trong việc xác thực thực thể tương tác trực tiếp với hợp đồng.

Sai sót này mở đường cho các cuộc tấn công kiểu lừa đảo (phishing), trong đó một hợp đồng trung gian độc hại, được gọi bởi một người dùng có đặc quyền, có thể gọi thành công các hàm được bảo vệ trên hợp đồng đích. Việc kiểm tra `tx.origin` xác thực không chính xác người dùng gốc, cho phép hợp đồng độc hại thực thi các hành động với thẩm quyền của người dùng. Hậu quả bao gồm từ việc đánh cắp trực tiếp Ether và token đến việc thao túng trái phép trạng thái hợp đồng, có khả năng gây ra tổn thất tài chính đáng kể và thiệt hại không thể khắc phục đối với tính toàn vẹn và uy tín của giao thức.

Link

Access Control

Kiểm soát truy cập không đầy đủ là một lỗ hổng nghiêm trọng trong các hợp đồng thông minh Solidity, xảy ra khi các hạn chế về việc ai có thể thực thi hàm nào bị thiếu hoặc được triển khai không đúng cách. Do Solidity thiếu các mô hình cấp quyền tích hợp sẵn, các nhà phát triển phải tự thêm các bước kiểm tra, thường sử dụng các bộ sửa đổi (modifier) như onlyOwner hoặc triển khai Kiểm soát truy cập dựa trên vai trò (RBAC). Việc không thực hiện đúng điều này sẽ tạo ra các lỗ hổng bảo mật.

Lỗ hổng này thường biểu hiện dưới dạng các hàm không được bảo vệ. Các hàm dành cho các hoạt động quản trị hoặc nhạy cảm - chẳng hạn như chuyển quyền sở hữu hợp đồng (changeOwner), rút tiền (withdraw), tạm dừng hợp đồng, đúc token hoặc thậm chí hủy hợp đồng (selfdestruct) - có thể bị bỏ ngỏ để bất kỳ tài khoản bên ngoài nào cũng có thể gọi. Điều này thường xảy ra do thiếu các bộ sửa đổi kiểm soát truy cập hoặc khả năng hiển thị hàm không chính xác (ví dụ: các hàm mặc định là public nếu không được chỉ định). Các hàm khởi tạo bị lộ, vốn chỉ nên chạy một lần, cũng có thể là một vector tấn công nếu chúng vẫn có thể được gọi sau khi triển khai, có khả năng cho phép kẻ tấn công đặt lại quyền sở hữu hoặc các tham số quan trọng.

Hậu quả rất nghiêm trọng, từ việc người dùng trái phép giành được đặc quyền quản trị đến việc toàn bộ số tiền do hợp đồng quản lý bị đánh cắp hoặc bản thân hợp đồng bị phá hủy không thể phục hồi. Các vụ khai thác trong thực tế, như sự cố ví Parity và vụ hack LAND Token, cho thấy tiềm năng tàn phá của việc kiểm soát truy cập không đầy đủ.

Các biện pháp giảm thiểu bao gồm áp dụng nghiêm ngặt các kiểm tra kiểm soát truy cập cho tất cả các hàm nhạy cảm, tuân thủ Nguyên tắc Đặc quyền Tối thiểu, sử dụng các mẫu hình đã được thiết lập như Ownable hoặc RBAC (thường thông qua các thư viện như của OpenZeppelin) và tiến hành kiểm thử cũng như kiểm toán kỹ lưỡng.

Link

Weak Randomness

Việc tạo ra tính ngẫu nhiên an toàn trên Máy ảo Ethereum (EVM) là một thách thức do bản chất xác định của nó, vốn rất cần thiết cho sự đồng thuận mạng. Điều này tạo ra một "ảo giác entropy", nơi các giá trị có vẻ ngẫu nhiên được lấy hoàn toàn từ dữ liệu trên chuỗi thực sự có thể dự đoán được.

Các nhà phát triển thường lạm dụng các biến khối có sẵn như block.timestamp, blockhash và prevrandao sau Hợp nhất (truy cập qua block.difficulty) làm nguồn tạo số giả ngẫu nhiên. Các biến này không an toàn vì chúng có thể bị dự đoán hoặc bị tác động.

Thợ đào (trong Proof-of-Work) hoặc trình xác thực (trong Proof-of-Stake) có thể thao túng các giá trị này ở một mức độ nhất định để giành lợi thế không công bằng, thường là một phần của chiến lược MEV (Giá trị có thể trích xuất tối đa). Quan trọng là, ngay cả người dùng thông thường hoặc hợp đồng tấn công cũng thường có thể dự đoán kết quả nếu logic ngẫu nhiên chỉ dựa vào các đầu vào đã biết trước hoặc trong quá trình thực hiện giao dịch, cho phép các khai thác như chạy trước (front-running). Khả năng dự đoán này làm suy yếu tính công bằng của các ứng dụng như xổ số, trò chơi và đúc NFT.

Link

Hash Collision

Va chạm Hash thông qua `abi.encodePacked` với nhiều loại động không phát sinh từ điểm yếu của hàm băm Keccak-256 cơ bản, mà từ cách dữ liệu được mã hóa trước khi băm, đặc biệt là khi sử dụng hàm `abi.encodePacked` của Solidity. Không giống như `abi.encode` tiêu chuẩn, vốn đệm các đối số thành 32 byte và bao gồm tiền tố độ dài cho các loại động, `abi.encodePacked` tạo ra một mã hóa nhỏ gọn, không chuẩn bằng cách nối các đối số sử dụng số byte tối thiểu cần thiết, bỏ qua phần đệm cho các loại tĩnh nhỏ và, quan trọng nhất là, bỏ qua thông tin độ dài cho các loại động như `string`, `bytes` hoặc mảng động.

Vấn đề cốt lõi xảy ra khi `abi.encodePacked` được sử dụng với hai hoặc nhiều đối số loại động liền kề. Bởi vì độ dài của mỗi đối số động không được mã hóa, ranh giới giữa chúng trở nên mơ hồ trong chuỗi byte kết quả. Sự mơ hồ này làm cho việc tạo ra các bộ đầu vào logic khác nhau (thường là dễ dàng) để tạo ra cùng một chuỗi byte được đóng gói chính xác trở nên khả thi. Ví dụ: `abi.encodePacked("a", "bc")` tạo ra cùng một đầu ra byte như `abi.encodePacked("ab", "c")`.

Khi đầu ra byte giống hệt này sau đó được băm (ví dụ: `keccak256(abi.encodePacked(...))`), nó dẫn đến cùng một giá trị băm, một va chạm băm do mã hóa gây ra.

Lỗ hổng va chạm mã hóa này có thể bị khai thác theo nhiều cách nếu giá trị băm kết quả được sử dụng trong ngữ cảnh nhạy cảm về bảo mật:

Bỏ qua Xác minh Chữ ký: Kẻ tấn công có thể lấy một chữ ký hợp lệ được tạo cho một bộ tham số và sử dụng lại nó với một bộ tham số khác, độc hại tạo ra một hàm băm va chạm. Việc xác minh chữ ký của hợp đồng (`ecrecover`) sẽ thành công, cấp quyền thực thi trái phép.

Làm hỏng Trạng thái thông qua Va chạm Khóa Mapping: Nếu hàm băm dễ va chạm được sử dụng làm khóa trong mapping (`mapping(bytes32 => ...)`), kẻ tấn công có thể tạo ra các đầu vào để tạo khóa va chạm với khóa của người dùng hợp pháp, có khả năng ghi đè dữ liệu của họ, bỏ qua kiểm soát truy cập hoặc gây ra từ chối dịch vụ.

Sự cố Xác thực Tin nhắn: Lỗ hổng làm suy yếu các kiểm tra dựa vào hàm băm để đảm bảo tính toàn vẹn của dữ liệu, vì các thông điệp logic khác nhau có thể xuất hiện giống hệt nhau sau khi băm.

Hậu quả của việc khai thác thành công có thể nghiêm trọng, bao gồm Truy cập trái phép vào các chức năng hoặc dữ liệu, Trộm cắp quỹ trực tiếp (Fund Theft), Hỏng hóc trạng thái nghiêm trọng và Từ chối dịch vụ (DoS).

Link

Precision Loss

Các lỗ hổng mất mát độ chính xác bắt nguồn từ việc phụ thuộc vào số học số nguyên và việc thiếu hỗ trợ gốc cho số dấu phẩy động. Thiết kế này ưu tiên thực thi xác định nhưng yêu cầu nhà phát triển phải quản lý thủ công các giá trị phân số, tạo cơ hội cho lỗi.

Vấn đề cốt lõi là việc cắt cụt phép chia số nguyên: Solidity loại bỏ phần dư và làm tròn kết quả phép chia về không. Hành vi có thể dự đoán này có thể bị khai thác, thường thông qua các mẫu như:

Chia trước nhân sau: Tính toán (a / b) * c thay vì (a * c) / b sẽ cắt cụt kết quả trung gian a / b, làm tăng thêm tổn thất độ chính xác.

Làm tròn xuống 0: Nếu tử số A nhỏ hơn mẫu số B (và cả hai đều dương), A / B luôn cho kết quả là 0. Điều này rủi ro đối với các phép tính liên quan đến phí, phần thưởng hoặc chuyển đổi token nhỏ.

Những kẻ tấn công khai thác các thuộc tính toán học này để thao túng logic hợp đồng nhằm trục lợi tài chính. Các chiến lược phổ biến bao gồm:

Thao túng trạng thái/giá: Kích hoạt lỗi làm tròn để làm sai lệch các giá trị giao thức quan trọng như tỷ giá hối đoái, dự trữ pool, giá cổ phần vault hoặc tỷ lệ tài sản thế chấp, sau đó có thể bị khai thác trong các giao dịch tiếp theo.

Nhắm mục tiêu các trường hợp biên: Sử dụng các giao dịch có đầu vào rất nhỏ hoặc đầu vào được thiết kế để tương tác với các giá trị nội bộ lớn nhằm tối đa hóa tác động của việc cắt cụt, thường khiến các phép tính cho kết quả bằng không.

Các cuộc tấn công mất mát độ chính xác thành công có thể dẫn đến hậu quả tiêu cực đáng kể:

Giảm chi phí: Kẻ tấn công trả phí/chi phí thấp hơn hoặc bằng không.

Lợi nhuận thổi phồng: Kẻ tấn công nhận được nhiều token, cổ phần hoặc phần thưởng một cách bất hợp pháp.

Cơ hội kinh doanh chênh lệch giá (Arbitrage): Tạo ra sự khác biệt giá nhân tạo trong giao thức để kẻ tấn công khai thác.

Vượt qua các cơ chế rủi ro: Bỏ qua việc thanh lý hoặc các kiểm tra an toàn khác do tính toán không chính xác.

Cạn kiệt quỹ dần dần: Rút ruột giá trị thông qua các giao dịch lặp đi lặp lại khai thác lỗi làm tròn nhỏ ("tấn công 1 wei").

Việc giảm thiểu đòi hỏi xử lý số học cẩn thận, chẳng hạn như thực hiện phép nhân trước phép chia, sử dụng tỷ lệ số (mô phỏng toán học dấu phẩy cố định), sử dụng các thư viện toán học chuyên dụng và triển khai logic làm tròn phù hợp. Các kiểm tra tràn tiêu chuẩn (như SafeMath hoặc Solidity >=0.8) không ngăn chặn được việc mất mát độ chính xác từ phép chia.

Link

msg.value in loop

Lỗ hổng này xảy ra khi một hợp đồng thông minh (smart contract) sử dụng msg.value không đúng cách trong một vòng lặp. Vấn đề cốt lõi xuất phát từ việc msg.value không đổi trong toàn bộ ngữ cảnh thực thi của một giao dịch. Nếu một vòng lặp lặp lại nhiều lần, thực hiện kiểm tra hoặc hành động dựa trên giá trị msg.value ban đầu này trong mỗi lần lặp mà không theo dõi chính xác giá trị tích lũy đã được xử lý hoặc chi tiêu qua các lần lặp đó, nó sẽ tạo ra cơ hội khai thác (exploit).

Kẻ tấn công có thể khai thác điều này bằng cách gửi một lượng Ether cụ thể để kích hoạt hàm dễ bị tổn thương. Bên trong vòng lặp, các kiểm tra như require(msg.value >= amount_per_item) có thể vượt qua nhiều lần, hoặc các cập nhật trạng thái có thể sử dụng sai giá trị msg.value ban đầu đầy đủ nhiều lần. Điều này xảy ra do logic của hợp đồng không tính đến giá trị đã được 'chi tiêu' hoặc phân bổ hiệu quả trong các lần lặp trước đó của cùng một vòng lặp.

Lỗi này cho phép kẻ tấn công kích hoạt các hành động (như chuyển Ether hoặc ghi có số dư nội bộ) mà tổng giá trị của chúng vượt quá đáng kể lượng Ether mà họ thực sự đã gửi cùng với giao dịch.

Link

ecrecover

ecrecover là một precompile EVM thiết yếu cho phép các hợp đồng thông minh khôi phục địa chỉ của người ký từ một hash tin nhắn và chữ ký ECDSA (v, r, s). Điều này cho phép các chức năng quan trọng như xác minh các tin nhắn được ký ngoài chuỗi (off-chain) cho các giao dịch meta (meta-transactions) hoặc hàm cấp phép (permit functions). Tuy nhiên, việc sử dụng trực tiếp nó tiềm ẩn những rủi ro bảo mật đáng kể, thường bị đánh giá thấp nếu không được xử lý cẩn thận.

Lỗ hổng trả về địa chỉ Zero: Một vấn đề nghiêm trọng xuất phát từ cách xử lý lỗi độc đáo của `ecrecover`. Khi được cung cấp một chữ ký không hợp lệ hoặc không thể thực hiện về mặt toán học, nó không hoàn lại (revert) giao dịch. Thay vào đó, nó âm thầm thất bại và trả về địa chỉ zero (`address(0)`). Các hợp đồng gọi `ecrecover` nhưng thiếu kiểm tra địa chỉ zero rất dễ bị tổn thương. Kẻ tấn công có thể cố ý gửi dữ liệu chữ ký không hợp lệ, khiến `ecrecover` trả về `address(0)`. Nếu đầu ra này không được kiểm tra và từ chối một cách rõ ràng, hợp đồng có thể tiếp tục xử lý sai, coi `address(0)` là người ký hợp lệ. Điều này có thể dẫn đến hậu quả nghiêm trọng như thay đổi trạng thái trái phép, phát ra sự kiện không chính xác hoặc cấp quyền, đặc biệt nếu địa chỉ zero có các đặc quyền đặc biệt hoặc trạng thái có ý nghĩa trong logic cụ thể của hợp đồng. Mã nguồn mạnh mẽ phải luôn xác thực `recoveredAddress != address(0)` ngay sau lệnh gọi `ecrecover`.

Lỗ hổng Tính mềm dẻo của Chữ ký (Signature Malleability): Rủi ro lớn thứ hai phát sinh từ một thuộc tính cố hữu của chính thuật toán ECDSA: tính mềm dẻo của chữ ký. Đối với bất kỳ tin nhắn và khóa riêng nào, có thể tồn tại nhiều biểu diễn chữ ký riêng biệt nhưng hợp lệ về mặt mật mã (cụ thể, một chữ ký sử dụng thành phần `s` thường có thể được chuyển đổi thành một chữ ký hợp lệ sử dụng `n-s`, trong đó `n` là bậc của đường cong). Điều này trở thành lỗ hổng nếu hợp đồng giả định sai rằng chữ ký cho một tin nhắn là duy nhất. Kẻ tấn công có thể khai thác điều này bằng cách bỏ qua các kiểm tra tính duy nhất. Ví dụ: nếu một hợp đồng sử dụng hash của chính chữ ký làm nonce để ngăn chặn các cuộc tấn công lặp lại (replay attacks - một mẫu hình thiếu sót), kẻ tấn công có thể lấy một chữ ký hợp lệ, tính toán bản sao mềm dẻo của nó và gửi lại để thực hiện hành động một lần nữa, vì các hash chữ ký sẽ khác nhau. Nó cũng có thể gây ra hành vi không mong muốn hoặc lặp lại trong các hệ thống mong đợi dạng chữ ký cụ thể, nếu các hệ thống bên ngoài hoặc các phần của logic hợp đồng không được thiết kế để xử lý cả hai dạng chữ ký hợp lệ. Việc giảm thiểu hiệu quả bao gồm việc thực thi chuẩn hóa chữ ký (signature canonicalization) – một quy trình kiểm tra được triển khai mạnh mẽ trong các thư viện tiêu chuẩn như ECDSA của OpenZeppelin, nên được ưu tiên hơn việc sử dụng trực tiếp `ecrecover`.

Link

Replay

Tấn công phát lại liên chuỗi (CCRA): Một giao dịch được thực thi hợp lệ trên một chuỗi EVM bị chiếm và gửi lại thành công trên một chuỗi EVM khác. Điều này khai thác sự tương đồng về định dạng giao dịch và chữ ký giữa các chuỗi, đặc biệt là khi giao dịch thiếu mã định danh chuỗi duy nhất (Chain ID). Hard fork Ethereum/Ethereum Classic là một ví dụ điển hình cho thấy rủi ro này đã xảy ra. EIP-155 được giới thiệu để giảm thiểu rủi ro này bằng cách nhúng Chain ID vào chữ ký giao dịch tiêu chuẩn, khiến chúng trở nên riêng biệt cho từng chuỗi. Tuy nhiên, các hợp đồng thông minh (smart contract) sử dụng xác minh chữ ký tùy chỉnh cũng phải kiểm tra Chain ID một cách tường minh. Vụ khai thác lỗ hổng (exploit) trị giá 20 triệu đô la trên Optimism nhắm vào Wintermute là kết quả của việc thiếu kiểm tra Chain ID như vậy trong một hợp đồng được triển khai liên chuỗi (cross-chain).

Phát lại ở cấp độ Hợp đồng thông minh (Cùng chuỗi): Một tin nhắn hoặc giao dịch đã ký được phát lại trên cùng một hợp đồng thông minh, hoặc có thể trên một hợp đồng khác trong cùng một chuỗi. Điều này thường khai thác các lỗ hổng trong logic riêng của hợp đồng, đặc biệt là trong các cơ chế xác minh chữ ký tùy chỉnh được sử dụng cho các tính năng như giao dịch meta (meta-transactions) hoặc chức năng cấp phép (permit) của ERC-20. Lỗi phổ biến nhất là thiếu hoặc triển khai không đúng nonce ở cấp ứng dụng. Nonce («số chỉ dùng một lần») là một bộ đếm duy nhất được liên kết với người ký, phải được bao gồm trong bản tóm lược (digest) tin nhắn đã ký và được hợp đồng theo dõi trên chuỗi (on-chain) để đảm bảo mỗi chữ ký cụ thể chỉ ủy quyền cho một hành động duy nhất.

Link

Inheritance Order

Việc Solidity hỗ trợ đa kế thừa cho phép một hợp đồng kế thừa các tính năng từ nhiều hợp đồng cha cùng một lúc. Mặc dù mạnh mẽ cho việc tái sử dụng mã, điều này lại gây ra sự mơ hồ tiềm ẩn, được gọi là "Vấn đề Kim cương" (Diamond Problem): nếu hai hoặc nhiều hợp đồng cơ sở định nghĩa một hàm có cùng tên và tham số.

Để giải quyết vấn đề này, Solidity sử dụng thuật toán tuyến tính hóa C3 để thiết lập một Thứ tự Phân giải Phương thức (Method Resolution Order, MRO) duy nhất và xác định cho mỗi hợp đồng. MRO này quy định trình tự chính xác mà các hợp đồng cơ sở được kiểm tra khi phân giải các lệnh gọi hàm.

Lỗ hổng phát sinh trực tiếp từ cách xác định MRO này. Yếu tố quyết định là thứ tự mà nhà phát triển liệt kê các hợp đồng cơ sở trong câu lệnh `is`. Solidity yêu cầu liệt kê các hợp đồng từ "giống cơ sở nhất" đến "kế thừa nhiều nhất". Thứ tự được chỉ định này ảnh hưởng trực tiếp đến MRO cuối cùng do thuật toán C3 tạo ra.

Lỗ hổng xảy ra khi nhà phát triển cung cấp một thứ tự kế thừa không khớp với hệ thống phân cấp hoặc ưu tiên logic dự định. Nếu một hợp đồng tổng quát hơn được liệt kê sau một hợp đồng cụ thể hơn, hoặc thứ tự đó khiến MRO ưu tiên một triển khai hàm không mong muốn, hợp đồng có thể hoạt động không như mong đợi. Ví dụ: một lệnh gọi có thể được phân giải thành một hàm cơ sở thiếu các kiểm tra bảo mật quan trọng hoặc logic cập nhật đã được triển khai trong một phương thức ghi đè của hợp đồng dẫn xuất dự định nhưng bị sắp xếp sai thứ tự.

Hậu quả của việc thực thi sai hàm do thứ tự kế thừa không chính xác bao gồm bỏ qua kiểm soát truy cập, thực thi logic nghiệp vụ lỗi thời hoặc không chính xác, hỏng trạng thái và tổn thất tài chính tiềm ẩn. Về cơ bản, luồng thực thi thực tế của hợp đồng đi chệch khỏi thiết kế của nhà phát triển, làm suy yếu tính bảo mật và chức năng.

Link

MEV

Giá trị Tối đa Có thể Trích xuất (Maximal Extractable Value - MEV) đại diện cho lợi nhuận mà những người sản xuất khối và các nhà tìm kiếm chuyên biệt (searchers) có thể thu được bằng cách thao túng việc đưa vào và sắp xếp thứ tự các giao dịch trong một khối, vượt ra ngoài phần thưởng khối tiêu chuẩn và phí gas. Ban đầu được gọi là "Giá trị Có thể Trích xuất bởi Thợ đào" (Miner Extractable Value), tên gọi này đã phát triển thành "Tối đa" để phản ánh sự tham lam về phần thưởng.

MEV phát sinh do các giao dịch đang chờ xử lý thường nằm trong một khu vực chờ công khai gọi là mempool, có thể được nhìn thấy bởi bất kỳ ai. Người sản xuất khối có thẩm quyền quyết định thứ tự cuối cùng của các giao dịch trong một khối. Các bot tự động, được điều hành bởi các "nhà tìm kiếm" (searchers), liên tục theo dõi mempool, mô phỏng các kết quả tiềm năng và khai thác các cơ hội sinh lời bằng cách sắp xếp các giao dịch một cách chiến lược, thường sử dụng đấu giá gas (Đấu giá Gas Ưu tiên - PGA) để đảm bảo vị trí ưu tiên.

Các chiến lược MEV phổ biến, thường được xem là các cuộc tấn công, bao gồm:

Chạy trước (Front-Running): Đặt giao dịch của kẻ tấn công trước giao dịch của nạn nhân (ví dụ: một giao dịch lớn trên DEX) để kiếm lợi từ tác động giá dự kiến.

Tấn công Kẹp (Sandwich Attacks): Sự kết hợp giữa chạy trước (front-running) và chạy sau (back-running) giao dịch DEX của nạn nhân để thu lợi từ chênh lệch giá (trượt giá) gây ra bởi sự thao túng.

Thanh khoản Đúng thời điểm (JIT Liquidity): Tạm thời thêm và xóa thanh khoản xung quanh một giao dịch hoán đổi (swap) lớn trên các DEX có thanh khoản tập trung để thu phí.

Thao túng Oracle (Oracle Manipulation): Khai thác các bản cập nhật hoặc sự không chính xác của các oracle giá để kiếm lợi, thường ảnh hưởng đến các giao thức cho vay.

Các loại khác bao gồm săn lùng NFT (NFT sniping) và tấn công bụi (dust attacks).

Hậu quả đối với người dùng bao gồm chi phí giao dịch tăng do các cuộc chiến gas, giá khớp lệnh tệ hơn (trượt giá) khi giao dịch, tổn thất tạm thời (impermanent loss) trầm trọng hơn đối với các nhà cung cấp thanh khoản và các vụ thanh lý bị kích hoạt một cách không công bằng.

Các chiến lược giảm thiểu nhằm mục đích làm giảm tác động tiêu cực của MEV. Gửi giao dịch qua các mempool riêng tư hoặc các trạm chuyển tiếp (relay) (như Flashbots Protect hoặc MEV Blocker) giúp che giấu chúng khỏi tầm nhìn công khai. Các thiết kế ở cấp độ ứng dụng như cơ chế Cam kết-Tiết lộ (Commit-Reveal) che giấu chi tiết giao dịch cho đến khi thứ tự được cố định, trong khi việc sử dụng Giá Trung bình theo Thời gian (TWAP) cho các oracle có thể giảm thiểu rủi ro thao túng.

Tư vấn bảo mật hợp đồng thông minh

Nhận tư vấn chuyên gia về blockchain trước khi bắt đầu phát triển. Chúng tôi giúp bạn thiết kế các hợp đồng và giao thức thông minh an toàn, hiệu quả, tránh những sai lầm tốn kém và tối ưu hóa hiệu suất. Nhanh hơn và tiết kiệm hơn so với thuê tại công ty. Đặt lịch tư vấn miễn phí ngay hôm nay!