DeFi dễ bị tổn thương chết tiệt là một loạt trò chơi CTF trong đó người chơi phải tìm ra lỗ hổng để phá vỡ hoặc đánh cắp một giao thức. Những trò chơi giáo dục này rất thú vị theo nghĩa là chúng bắt chước các ứng dụng thực (flashloans, pool, yield,…), cho phép chúng ta không chỉ tìm hiểu về bảo mật web3 mà còn cả DeFi là gì.Link tại đây: https://www.damnvulnerabledefi.xyz/index.html Trong loạt bài viết này, tôi sẽ trình bày những thách thức và giải pháp của chúng.
Nếu bạn là người mới trong Solidity, tôi khuyên bạn trước tiên nên kiểm tra các thử thách trước đó vì đôi khi tôi nhanh chóng lướt qua một số khái niệm mà chúng ta đã thấy trước đây
Một nhóm cho vay hấp dẫn mới đã ra mắt! Nó hiện đang cung cấp các khoản vay flash của mã thông báo DVT. Nó thậm chí còn bao gồm một cơ chế quản trị ưa thích để kiểm soát nó. Điều gì có thể đi sai, phải không? Bạn bắt đầu mà không có mã thông báo DVT nào trong số dư và nhóm có 1,5 triệu. Mục tiêu của bạn là để có được tất cả
– Xem các hợp đồng
– Hoàn thành thử thách
Điều đầu tiên, chúng ta cần hiểu cách thức hoạt động của giao thức. Giao thức được tạo thành từ 3 hợp đồng chính:
- SelfiePool.sol
Nhóm này cũng giống như các nhóm khác, nó cung cấp các khoản vay flash cho người dùng (các khoản vay phải được trả lại khi kết thúc giao dịch, nếu không được trả lại, nó sẽ kích hoạt hoàn nguyên). Điểm đặc biệt nếu nhóm này so với các nhóm khác là nó được kiểm soát bởi một cơ quan quản lý (hợp đồng tiếp theo). Ban quản trị có khả năng rút hết tiền (nếu bạn thấy một giao thức như thế này, hãy chạy!) - SimpleGovernance.sol
Đây là hợp đồng quản trị, hợp đồng kiểm soát quỹ SelfiePool. Để trở thành một phần của quá trình quản trị này, người dùng cần nắm giữ mã thông báo quản trị, là mã thông báo CVT ở đây, với khả năng chụp nhanh được thêm vào. Người dùng có thể đề xuất một hành động và tùy thuộc vào một số điều kiện, hành động đó sẽ được thực hiện. - DamValuableTokenSnapshot.sol
Mã thông báo quản trị được sử dụng bởi hợp đồng quản trị. Mã thông báo ERC20 có khả năng chụp nhanh (có khả năng sàng lọc số dư của từng người dùng tại một thời điểm cụ thể)
Hồ bơi tự sướng
Ghi chú nhanh sau lần đọc đầu tiên:
L1 — solidity ^0.8.0 → tự động kiểm tra tràn/tràn
L4&13 — Hợp đồng kế thừa mô-đun OpenZeppelin Reentrancy Guard
L6&15— Sử dụng thư viện Địa chỉ OpenZeppelin. Thư viện này thêm kiểm tra khi một call
được thực hiện trên một địa chỉ. Thêm chi tiết tại đây, nhưng tóm lại địa chỉ phải là hợp đồng, cuộc gọi phải thành công và nếu không, thông báo lỗi sẽ được xử lý tốt hơn.
L17–18 & 27–30 — Mã thông báo quản trị và hợp đồng quản trị được khởi tạo tại hàm tạo, chúng không thể được sửa đổi sau đó
L22 — Công cụ sửa đổi để hạn chế việc sử dụng một chức năng chỉ dành cho quản trị
L32 — flashLoan
sử dụng bảo vệ reentrancy thông qua công cụ sửa đổi của nó.
L34 — Chức năng kiểm tra xem số dư ETH của nó có đủ cho khoản vay hay không và sau đó chuyển khoản vay cho người dùng.
L38 — Khi quá trình chuyển mã thông báo được thực hiện thành công, hàm sẽ kiểm tra xem msg.sender có phải là một hợp đồng hay không (L38). Trên thực tế, kiểm tra này có thể được gỡ bỏ, vì functionCall
đến từ mô-đun địa chỉ đã thực hiện kiểm tra này.
L39–45— Cuộc gọi này kích hoạt receiveTokens
trong hợp đồng người yêu cầu (nhưng nó sẽ gọi fallback()
chức năng nếu nó không thực hiện receiveTokens
)
L47–49 — Cuối cùng, chức năng xác minh khoản vay flash ít nhất đã được hoàn trả (hoặc nhiều hơn)
L52 — drainAllFunds
là một chức năng đặc biệt chỉ có thể được kích hoạt bởi quản trị (công cụ sửa đổi) được sử dụng để chuyển toàn bộ số dư mã thông báo đến một địa chỉ cụ thể.
Chức năng này nguy hiểmvới tư cách là một diễn viên hoặc nhóm diễn viên quyền lực (với hơn 50% quyền biểu quyết : kiểm tra chức năng _hasEnoughVotes của SimpleGovernance) có thể chọn rút tiền.
quản trị đơn giản
(Tôi sẽ xem nhanh hợp đồng này, làm nổi bật chức năng chính của nó)
Hợp đồng này là cái mà chúng ta thường gọi là DAO (đơn giản và không hoàn hảo) (Tổ chức tự trị phi tập trung). Để trở thành thành viên của tổ chức này, người dùng cần nắm giữ một số lượng mã thông báo quản trị.
Khi bạn là thành viên của quản trị, bạn có thể đề xuất một hành động L38 queueAction
nhưng để làm được điều này bạn cần có đủ Phiếu bầu (L87 hasEnoughVotes
) tương đương với ít nhất 50% nguồn cung.
Một hành động được đại diện bởi một cấu trúc (L15 GovernanceAction
), trong đó trường dữ liệu về cơ bản được sử dụng để lưu trữ một hàm được mã hóa abi sẽ được thực thi sau đó trong executeAction
hàm số.
Khi một hành động được xếp hàng đợi thành công, nó có thể được thực thi (L56 executeAction
) nếu điều kiện trong _canBeExecuted
(L79) đều hợp lệ, ở đây hành động chỉ có thể được thực hiện một lần và phải tôn trọng độ trễ tối thiểu 2 ngày.
Có thể bạn chưa để ý vì nó được xác định trong tệp challenge.js, nhưng nhóm cho vay mã thông báo quản trị. Cùng một mã thông báo quản trị kiểm soát việc quản trị… Bây giờ bạn nhớ rằng một kẻ xấu có đủ cổ phần mã thông báo có thể đề xuất một hành động. Hành động này bao gồm việc thực hiện một cuộc gọi tùy ý. Và cuộc gọi này có thể là ví dụ drainAllFunds
tại sao không phải sau khi tất cả?
Trên thực tế, điều này đã xảy ra nhiều lần, đây là một ví dụ về những gì chúng ta có thể gọi là tấn công flashloan quản trị (cuộc tấn công hơi khác một chút)
Hãy kiểm tra làm thế nào để làm điều đó với hợp đồng tấn công của tôi.
Hợp đồng của tôi bao gồm 3 chức năng, tương ứng với 3 giai đoạn của cuộc tấn công:
- L41 —
attackRewardPool()
mà gọiflashLoan
chức năng, yêu cầu một khoản vay bằng toàn bộ số dư của nhóm. - L47 —
receiveTokens
được gọi bởi nhóm trong flashloan.
Chức năng này kích hoạt mộtsnapshot
của mã thông báo quản trị, vì đây là thứ được quản trị tính toán để kiểm tra xem người dùng có quyền xếp hàng một hành động hay không.
L56–60 — Sau đó, sau khi đảm bảo ảnh chụp nhanh cho phép tôi xếp hàng hành động, tôi xếp hàngdrainAllFunds(address)
chức năng cho danh sách hành động quản trị, trong đó địa chỉ là chính tôi, chủ sở hữu của hợp đồng
L62 — Cuối cùng, tôi trả lại khoản vay vì nhóm cho vay yêu cầu không hoàn trả - Sau khoảng thời gian 2 ngày, tôi có thể gọi cho
finalAttack()
chức năng của hợp đồng của tôi, sẽ thực hiện hành động và gửi tất cả số tiền của tôi !
Để kết thúc điều này, bây giờ chúng ta hãy triển khai điều này trong tệp challenge.js bằng cách viết phần này trong phần Khai thác:
Các bước giảm thiểu được đề xuất
Ban quản trị đã nghĩ đến việc thực hiện một khoảng thời gian trì hoãn để hành động có thể thực thi được, đây thực sự là một ý tưởng hay.
Ngoài ra, một sự kiện được phát ra khi một hành động được xếp hàng đợi, sự kiện này có thể được ghi lại trên chuỗi và chẳng hạn như được chuyển tiếp tới các thành viên của DAO bằng e-mail, cho họ 2 ngày để tự bảo vệ mình khỏi những gì sắp xảy ra.
Nhóm không nên cho vay nhanh các mã thông báo quản trị kiểm soát nó!.. Nhìn chung, chức năng “drainAllFunds” rõ ràng là không mong muốn và là nguồn nguy hiểm, và nên được coi là dấu hiệu cảnh báo.
Nếu bạn quan tâm đến các cuộc tấn công flashloan, có nhiều phương pháp giảm thiểu tồn tại, nhưng không có phương pháp nào là hoàn hảo. Nó thường sẽ tăng tính tập trung (ví dụ: cho phép một danh sách địa chỉ đặc quyền bỏ hàng đợi một hành động hoặc ít xâm phạm hơn để tạm dừng hợp đồng quản trị)
Hy vọng bạn thích bài viết này và hẹn gặp lại lần sau cho một thử thách mới!
Mới giao dịch? Hãy thử bot giao dịch tiền điện tử hoặc sao chép giao dịch trên các sàn giao dịch tiền điện tử tốt nhất