Ngày 29 tháng 9 năm 2021, Compound Finance – giao thức cho vay DeFi đã phát minh ra khai thác lưu lượng – đã nâng cấp hợp đồng phân phối tiền của mình thông qua một cuộc bỏ phiếu quản trị thường lệ. Cải tiến, Dự án 062, đã thay đổi khoảng mười dòng của Solidity. Trong vòng vài giờ, bất kỳ người dùng Compound nào tiếp xúc với các thị trường cho vay nhất định đều có thể yêu cầu tiền điện tử quản trị COMP bằng tổng số dư gửi của họ – không phải một phần nhỏ của lợi nhuận, mà là bản sao 1:1 của tài sản thế chấp của họ trong COMP. Đến sáng hôm sau, khoảng 80 triệu đô la trong số COMP thừa đã được yêu cầu; thêm 67 triệu đô la nằm trong kho tàng Reservoir của giao thức đang chờ người tiếp theo gọi một hàm. Tổng thiệt hại là 147 triệu đô la (tăng lên 162 triệu đô la tùy thuộc vào giá COMP trong tuần đó). Cách khắc phục – Dự án 063 – không thể thực hiện trong sáu ngày vì timelock của Compound, được thiết kế để bảo vệ người dùng khỏi các hành vi quản trị xấu, giờ đã bảo vệ lỗi khỏi được sửa chữa.
Cách hack tài chính phức tạp này không phải là vụ đánh cắp private key, vay tiền nhanh chóng, hay khai thác lỗ hổng. Đó là một lỗi phân phối do chính sách gây ra – bản nâng cấp của quy trình là lỗ hổng, người dùng của nó là những người hưởng lợi, và sự trì hoãn an toàn của nó là lý do khiến cho tình trạng rò rỉ kéo dài một tuần. Hiểu được những gì đã xảy ra giúp làm rõ lý do tại sao tất cả các quy trình cho vay lớn hiện nay đều coi các thay đổi chính sách đối với logic phân phối là một mặt phẳng tấn công quan trọng.
Compound Finance làm gì và tại sao quản trị của nó có ý nghĩa
Compound là một giao thức cho vay Ethereum được ra mắt vào năm 2020. Người dùng gửi tài sản (USDC, ETH, DAI, USDT, v.v.) vào các thị trường cToken – cUSDC, cETH, cDAI – và nhận lãi suất được trả bởi các bên vay. Vào tháng 6 năm 2020, Compound đã giới thiệu COMP, mã thông báo quản trị, được phân phối theo tỷ lệ tương ứng cho các nhà cung cấp và các bên vay như một phần thưởng khai thác khối lượng giao dịch. Đến tháng 9 năm 2021, Compound đã giữ khoảng 10 tỷ USD trong tổng giá trị bị khóa và COMP được giao dịch ở mức khoảng 320–400 USD.
Ba lựa chọn thiết kế đã làm cho sự cố này trở thành có thể xảy ra. Thứ nhất, quản trị có thể nâng cấp Comptroller – hợp đồng duy nhất theo dõi người nào nợ bao nhiêu COMP. Governor Bravo của Compound cho người sở hữu COMP bỏ phiếu về các đề xuất, sau khi có thời gian chờ, có thể thay thế hoàn toàn logic của Comptroller. Thứ hai, phân phối COMP được thực hiện thông qua một hồ chứa – một kho chứa ~280.000 COMP (~90 triệu USD) mà Comptroller rút ra mỗi khi người dùng yêu cầu phần thưởng. Do đó, một lỗi trong bố trí lưu trữ của Comptroller cho phép truy cập vào toàn bộ hồ chứa. Thứ ba, không có nút tạm dừng trong quá trình phân phối. Compound có thể tạm dừng các thị trường cToken riêng lẻ, nhưng không thể tạm dừng đường dẫn claimComp() toàn cầu – do đó, một khi lỗi được phát hành, không có cách nào để ngăn chặn nó trừ khi có một đề xuất quản trị khác.
Thời gian: 9 ngày từ nâng cấp yên tĩnh đến bản sửa lỗi
Khung tấn công được điều khiển ít nhất bởi tốc độ của kẻ tấn công hơn là bởi đồng hồ quản trị của Compound.
29 tháng 9, 2021, ~20:00 UTC — Đề xuất 062 được thực hiện sau khi được thông qua với sự ủng hộ tuyệt đối. Mục đích được nêu ra là đơn giản: sửa đổi cấu hình compSpeed cho thị trường cCOMP (thị trường tự tham chiếu nơi COMP có thể được cung cấp). Quản trị viên mới được triển khai; không có báo động được kích hoạt trong vài giờ.
29 tháng 9, 21:30 – 30 tháng 9, 03:00 UTC – Các nhà quan sát trên chuỗi (@banteg, @samczsun, @transmissions11) đã báo cáo các giao dịch claimComp() có số lượng lớn. Một người dùng đã cung cấp 1.000 cUSDC nhận được 1.000 COMP. Các bài viết trên Twitter của DeFi đã phân tích điều kiện supplyIndex == 1e36 trong vòng vài giờ. Máy móc MEV đã bắt đầu tự động hóa các chuỗi drip() + claimComp().
30 tháng 9, ~09:00 UTC — Chủ tịch của Compound, Robert Leshner, xác nhận lỗi trong một bài đăng trên Twitter: ~80 triệu đô la đã được yêu cầu, ~67 triệu đô la vẫn còn bị rủi ro trong Reservoir. Đội ngũ công bố rằng chức năng drip() của Comptroller – mà bất kỳ ai cũng có thể gọi để khởi tạo thu nhập cho một thị trường – hiện là trigger khai thác rẻ nhất, vì nó vội vàng thiết lập một chỉ số toàn cầu mà không khởi tạo trạng thái cho mỗi người dùng.
30 tháng 9 — Đề xuất 063 đã được gửi để sửa lỗi logic của Comptroller (đổi lại cách xử lý compInitialIndex và sửa lỗi supplierIndex == 0). Tuy nhiên, quy trình quản trị Compound yêu cầu một thời gian bỏ phiếu 3 ngày cộng với thời gian chặn 2 ngày sau khi xếp hàng, do đó, thời gian thực hiện sớm nhất là bảy ngày.
1–6 tháng 10 — lỗi tiếp tục diễn ra. Hồ chứa đã mất khoảng 120.000 COMP (~40 triệu USD) đến khoảng 18 địa chỉ. Đơn vị yêu cầu lớn nhất — 202.472 COMP (~65 triệu USD) — đến từ một địa chỉ giống như kho chứa, cung cấp và ngay lập tức yêu cầu trên cUSDC và cUSDT. Một số robot MEV đã chi khoảng 2.000 USD cho mỗi lần để kiếm được 500.000 USD đến 2 triệu USD. Một địa chỉ được đánh dấu là liên kết với Justin Sun đã yêu cầu khoảng 37.000 COMP (~12 triệu USD), sau đó trả lại khoảng 10.000 COMP sau khi có sự phản hồi công khai.
8 tháng 10, ~02:00 UTC — Đề xuất 063 được thực hiện sau khi thời gian giới hạn đã hết. Lỗi phân phối đã được sửa; số dư còn lại của Reservoir đã được bảo đảm. Compound cũng thêm một chức năng returnComp(uint amount) để các người yêu cầu có thể tự nguyện trả lại số COMP thừa.
8–18 tháng 10 — Khôi phục cộng đồng: khoảng 30–40 triệu USD COMP thừa nhận được qua returnComp và chuyển khoản trực tiếp (khoảng 35% số tiền thừa nhận). Số còn lại — 50–60 triệu USD — được giữ lại bởi những người yêu cầu và không bao giờ được khôi phục. Compound Labs đã đăng bài phân tích sau khi sự cố xảy ra vào ngày 18 tháng 10, xem sự cố này là một lỗi trong quá trình phân phối, không phải là hành vi trộm cắp; không có hành động thực thi pháp luật được tiến hành.
Số liệu: Khi lỗi là quy trình, 147 triệu USD sẽ chuyển động như thế nào
Vào ngày 30 tháng 9, khi giá COMP là ~$320, số lượng có rủi ro và giá trị đô la của nó phụ thuộc rất nhiều vào thời điểm bạn đo lường:
| Buồng chứa | COMP (khoảng) | USD ở mức 320 USD/COMP | Tình trạng |
|---|---|---|---|
| Điều khoản được yêu cầu trước ngày 30 tháng 9 | ~240,000 COMP | ~ 80 triệu USD | Trong ví của các người yêu cầu |
| Hồ chứa có nguy cơ (30/9) | ~280,000 COMP | ~ 90 triệu USD | Còn trong kho, có thể phân phối |
| Tổng số tiêu đề có nguy cơ | ~520,000 COMP | 147–162 triệu đô la | Tuyên bố thông tin đồng thuận (nhạy cảm với giá) |
| Đề nghị yêu cầu lớn nhất | 202,472 COMP | ~ 65 triệu USD | Một địa chỉ, một đợt phát |
| Hóa chất để khai thác | — | <5.000 USD | claimComp() không cần quyền |
| Được trả lại thông qua thiện chí | ~100,000 COMP | ~ 30–40 triệu USD | ~35% thừa |
| Được giữ bởi các người yêu cầu | ~150,000 COMP | ~50–60 triệu USD | Không bao giờ phục hồi |
Ba điểm nhỏ quan trọng. Thứ nhất, giá USD trên thị trường là phản xạ: COMP đã giảm ~30% trong tuần (từ ~$400 xuống ~$300 khi các người yêu cầu bán), do đó, một giá USD sau này không phản ánh đúng số lượng COMP. Luôn dựa vào số COMP, không phải USD. Thứ hai, cuộc tấn công không cần giấy phép và rẻ – claimComp() và drip() không yêu cầu bất kỳ vai trò đặc quyền nào và chỉ tốn một lượng gas của giao dịch duy nhất. Mọi địa chỉ Ethereum đều có thể tham gia, đó là lý do tại sao các bot MEV, chứ không phải một hacker chuyên nghiệp, đã chiếm được phần lớn kho dự trữ. Thứ ba, sự tự thị trường của cCOMP mà Đề xuất 062 mong muốn sửa chữa chính là con chim canary: một khi compSpeed được sửa lại, mọi thị trường có tốc độ bằng 0 đã thừa hưởng lỗi chỉ số thông qua đường dẫn drip() chung, làm tăng một lỗi của thị trường duy nhất thành một lỗ hổng trên nhiều thị trường.
Nguyên nhân: Khi supplyIndex trở thành 1e36 và supplierIndex vẫn là 0
Hacker về tài chính phức tạp là một ví dụ điển hình về sự không đồng bộ hóa trạng thái khởi tạo – hai khung lưu trữ phải di chuyển cùng nhau nhưng được cập nhật bởi các đường dẫn mã khác nhau. Điều vi phạm là rất đơn giản: không có người dùng nào nên nhận được COMP bằng với số dư cToken của họ.
Cách hoạt động bình thường của COMP Distribution
Đối với mỗi thị trường cToken, Quản lý tài chính theo dõi:
compSupplyState[cToken] = { index: uint224, block: uint32 }— một bộ nhớ tích lũy toàn cầu cho thị trường đó. Mỗi khối,deltaBlocks * compSpeedđược thêm vàoindex.
Mức COMP đang chờ xử lý của một người dùng là:
` deltaIndex = supplyIndex - supplierIndex pending = supplierTokens * deltaIndex / 1e36 `
Một nhà cung cấp mới bắt đầu với supplierIndex == 0. Trong lần tương tác đầu tiên, giao thức đặt supplierIndex = compInitialIndex (1e36) để deltaIndex == 0 và không có phần thưởng có hiệu lực về sau – người dùng chỉ nhận được phần thưởng khi tiếp tục hoạt động.
Đề xuất 062 đã thay đổi gì
Sự thay đổi của đề xuất đã tác động đến _setCompSpeed / updateCompSupplyIndex và helper drip(). Đối với các thị trường chưa bao giờ phân phối COMP - compSpeed == 0 và supplyIndex == 0 (hầu hết các thị trường cToken có tốc độ bằng 0 vào thời điểm đó) - mã mới đã được thiết lập một cách vội vàng:
` compSupplyState[cToken].index = compInitialIndex // 1e36 `
... trong lần gọi đầu tiên drip() hoặc speed-update. Tuy nhiên, nó không thay đổi compSupplierIndex cho các nhà cung cấp hiện có trong các thị trường đó. Những nhà cung cấp này vẫn có supplierIndex == 0.
Do đó, trong tương tác tiếp theo của nhà cung cấp – claimComp(), nhưng cũng như mint, redeem, borrow hoặc repayBorrow – phép tính trở thành:
` deltaIndex = 1e36 - 0 = 1e36 pending = supplierBalance * 1e36 / 1e36 = supplierBalance `
Một người sở hữu 1,000 cUSDC (tương đương với ~$21,000 trong USDC cơ sở) nhận được 1,000 COMP (~$320,000). Nếu áp dụng cho một nhà cung cấp lớn có 500,000 cUSDC và một lần gọi tạo ra 500,000 COMP. Vì Hệ thống dự trữ của Trợ lý kiểm toán có 280,000 COMP và công thức đang chờ xử lý là cộng dồn theo khối, các yêu cầu lặp lại trên nhiều thị trường đã làm tăng lợi nhuận.
Tại sao drip() là vũ khí thực sự
Hầu hết các báo cáo nói rằng "người dùng đã yêu cầu COMP". Đường dẫn khai thác rẻ hơn là drip(cToken). Ai đó - thậm chí không phải nhà cung cấp - có thể gọi drip() trên thị trường có chỉ số bằng 0 để kích hoạt quá trình chuyển đổi supplyIndex = 1e36. Gọi đó đã đặt bẫy; nhà cung cấp sau đó tiếp xúc với thị trường đã kích hoạt nó. Một kẻ tấn công có thể gọi drip() trên tất cả các thị trường bị ảnh hưởng trong một giao dịch, sau đó yêu cầu trên mỗi thị trường theo thứ tự, mà không cần bất kỳ payload phức tạp nào - chỉ cần một chuỗi các cuộc gọi không cần phép.
Tại sao các kiểm toán và kiểm tra đã bỏ qua điều này
CertiK và OpenZeppelin đã kiểm tra các phiên bản Comptroller trước đó; sự khác biệt là khoảng 10 dòng. Các điều kiện không thay đổi bị vi phạm — "mức độ chờ đợi nhà cung cấp phải nhỏ hơn số dư của nhà cung cấp" — không nằm trong tiêu chuẩn kiểm tra, và không có bất kỳ thử nghiệm nào hiện có thực hiện kết hợp trạng thái (supplyIndex == 1e36) ∧ (supplierIndex == 0) vì tất cả các thị trường thử nghiệm đều có chỉ số nhà cung cấp không bằng 0 từ việc ghi nhận trước đó. Một thử nghiệm của thuộc tính khởi tạo trạng thái — "cho bất kỳ thị trường nào, sau bất kỳ drip() nào, mức độ chờ đợi của nhà cung cấp cũ < 1 COMP" — sẽ phát hiện điều này.
Tại sao việc trì hoãn thời gian và quản trị đã làm trầm trọng vấn đề hơn
Governor Bravo + Timelock của Compound áp dụng thời gian chậm 48 giờ giữa thời điểm thông qua một đề xuất và thực hiện. Theo thiết kế, đây là một "khoảng thời gian minh bạch": người dùng có thể thoát ra nếu không đồng ý với một đề xuất trước khi nó được đưa vào hoạt động. Vào ngày 30 tháng 9, khoảng thời gian chậm đó trở thành kẻ thù.
Virus đã hoạt động. Cách sửa lỗi – Đề xuất 063 – cần 3 ngày bỏ phiếu cộng với 2 ngày thời gian giới hạn, tổng cộng 7 ngày trước khi nó có thể sửa lỗi một hợp đồng đang bị rò rỉ. Trong 7 ngày đó, lỗi tiếp tục rò rỉ COMP khoảng mỗi khối cho bất kỳ ai tương tác với các thị trường bị ảnh hưởng.
Compound Labs đã xem xét các lựa chọn thay thế và không tìm thấy bất kỳ lựa chọn nào khả thi:
- Không có gián đoạn toàn cầu. Compound có thể tạm dừng các thị trường cToken (cung cấp/cho vay) riêng lẻ, nhưng không thể tạm dừng đường dẫn phân phối
claimComp()trên toàn cầu. Đề xuất gián đoạn sẽ cần đến sự quản trị — và cũng sẽ gặp phải cùng một hạn chế thời gian.
Đây là lỗi token compound comp như một bài học về quản trị: một timelock bảo vệ chống lại quản trị xấu (một đề xuất xấu có thể được nhìn thấy và thoát ra), nhưng nó làm tăng thiệt hại từ quản trị có lỗi (một nâng cấp có thiện chí với lỗi logic không thể được nhanh chóng đảo ngược). Mọi giao thức cho phép nâng cấp logic phân phối quản trị mà không có một thời điểm ngừng hoặc một cửa thoát bảo vệ là đang chạy cùng nguy cơ mà Compound đã gặp phải.
Ai đã được lợi – và ai đã trả lại
Khác với các cuộc tấn công vào cầu được kiểm soát bởi một địa chỉ khai thác duy nhất, câu chuyện về cuộc tấn công governance defi không phù hợp ở đây – không có sự chiếm quyền quản trị. Những người nhận lợi ích chỉ đơn giản là những người dùng Compound hiện có (và các bot theo dõi mempool) đã hiểu được các thuật toán trước tiên.
Khoảng 18 địa chỉ đã tuyên bố có lợi nhuận quá mức vào ngày 6 tháng 10, theo Etherscan và Dune:
- Đề nghị lớn nhất – 202,472 COMP (~$65 triệu) – đến từ một địa chỉ cung cấp và ngay lập tức yêu cầu cUSDC và cUSDT trong một lần yêu cầu duy nhất.
returnComp(uint amount) trong đề xuất 063 là một tiền lệ quản trị đáng lưu ý: nó coi việc phân phối quá mức là một nợ xã hội, chứ không phải là nợ smart contract. Gọi nó là tự nguyện; hợp đồng không buộc phải trả lại. Nó đã hoạt động một phần – ~35% (~$30–40M) đã được trả lại vào ngày 18 tháng 10 – vì kích thước yêu cầu lớn và nhiều người yêu cầu có thể bị tiết lộ thông tin cá nhân hoặc có tính nhạy cảm về uy tín. ~60% (~$50–60M) còn lại được giữ lại. Nếu COMP chảy qua một máy trộn, cơ chế goodwill sẽ thất bại hoàn toàn – và đó chính là lý do tại sao Compound và các đối tác sau này đã thêm một người bảo vệ tạm dừng chính thức thay vì dựa vào việc trả lại tự nguyện một lần nữa.
Tiến trình chuyển tiền: Từ hồ chứa đến ví
Cách thức này đơn giản hơn – và trong một số cách, có nhiều bài học hơn – so với quy trình rửa tiền xuyên chuỗi thông thường. Không có bridge, không có máy trộn, không có các bước xuyên chuỗi:
- Nguồn — Địa chỉ hồ chứa chứa ~280k COMP được dành cho phân phối. Người kiểm toán là người duy nhất chi tiêu; bất kỳ
claimCompđược ủy quyền nào cũng lấy từ kho này. 2. Sự vượt quá phân phối —claimComp()tính toánpending == balancevà chuyển số tiền đó từ hồ chứa → người yêu cầu. Mỗi lần gọi đều hợp lệ theo quan điểm của EVM — chức năng được thực hiện đúng, chỉ là trạng thái mà nó đọc là sai. 3. Tính cách của người yêu cầu — Một số người yêu cầu ngay lập tức đổi COMP thành stablecoin trên Uniswap/Sushi (lý do tại sao giá COMP giảm), một số giữ, một số trả lại. Các sự kiệnSwaptrên chuỗi cho thấy có khoảng 100k COMP được đổi trong vòng 48 giờ. 4. Quãng đường phục hồi — Tiền được chuyển qua hai phương thức: chuyển trực tiếp đến hồ chứa / Timelock, vàreturnComp()trong Đề xuất 063, phát ra sự kiệnCompReturnedđể ghi sổ. Tài khoản còn lại của hồ chứa được bảo đảm khi Đề xuất 063 sửa lỗi logic chỉ số vào ngày 8 tháng 10.
Những điều không xảy ra cũng rất đáng chú ý: không có khoản tiền nào bị đóng băng bởi nhà phát hành (COMP không thể bị đóng băng như USDT), không có chuỗi bị ngừng hoạt động, và không có khoản tiền nào được thu hồi ở lớp đồng thuận. Câu chuyện về 147 triệu COMP đã được giải quyết thông qua coordination xã hội được đặt trên nền tảng của việc thực thi EVM không thể thay đổi.
Các bài học: 5 biện pháp phòng vệ có thể ngăn chặn vụ tấn công Compound Finance
Mỗi giao thức cho vay đã phát hành một giám đốc điều hành có thể nâng cấp trước tháng 10 năm 2021 đều có một số phiên bản của rủi ro này. Cách khắc phục của Compound và phản ứng của hệ sinh thái kể từ đó đã tập trung vào năm biện pháp phòng thủ.
1. Các thử nghiệm không thay đổi và xác minh chính thức về toán học phân phối. Tính chất duy nhất "cho bất kỳ thị trường và bất kỳ nhà cung cấp nào, đang chờ < supplierBalance>" sẽ bị thất bại ngay trong ngày đầu tiên của một mạng thử nghiệm bị chia tách với mã của Đề xuất 062. Certora, Halmos, hoặc thậm chí một thiết bị không thay đổi của Foundry được gieo hạt với (supplyIndex=0, supplierIndex=0, speed>0) sẽ báo hiệu sự thay đổi 1e36. Lógica phân phối xứng đáng có một tiêu chuẩn xác minh riêng, không chỉ là các kiểm tra tái nhập chung.
2. Bảo vệ tạm dừng có phạm vi hạn chế – không phải là quyền quản lý toàn diện trong trường hợp khẩn cấp. Một bảo vệ đa ký (ví dụ, 3-of-5) chỉ có khả năng tạm dừng claimComp / drip – không phải là chuyển tiền – sẽ làm đông lại quá trình rò rỉ trong vòng vài giờ, không cho bất kỳ ai quản lý Địa điểm dự trữ. Đây là tiêu chuẩn Compound được áp dụng sau đó (và Aave, Uniswap, và các nền tảng khác sao chép): quyền tạm dừng có phạm vi hạn chế, có thể thu hồi và nhanh hơn so với khóa thời gian.
3. Các giới hạn tốc độ theo khối và theo yêu cầu trên kho. Một thiết bị cắt circuit – ví dụ, bất kỳ một claimComp > 10k COMP hoặc tổng cộng > 50k COMP trong một khối – sẽ kích hoạt một gián đoạn tự động và kiểm tra Timelock – sẽ hạn chế bán kính của vụ nổ chỉ ở một giao dịch bất thường thay vì một tuần mất nước. Các vụ trộm có trọng tâm là stablecoin cho thấy các lệnh ngừng dựa trên khối lượng là phương pháp kiểm soát đơn giản có tác động cao nhất.
4. Bypass chế độ Timelock cho các bản sửa lỗi khẩn cấp. Bảo lưu cửa sổ minh bạch cho các tính năng và thay đổi tham số mới, nhưng cho phép một người bảo vệ hoặc một đường dẫn đề xuất nhanh (quy trình bỏ phiếu ngắn hơn + thời gian chặn ngắn hơn, được kiểm soát bởi một hội đồng an ninh) cho các đề xuất sửa lỗi khi một đề xuất trước đó đang thải ra tiền. Giới hạn sửa lỗi 7 ngày là bài học sâu sắc nhất: thời gian chậm trễ trong quản trị không bao giờ được vượt quá thời gian khai thác cho một hợp đồng hoạt động có lỗ hổng được biết đến.
5. Kiểm toán phân tích so sánh cho mỗi đề xuất của Quản lý tài chính. Mỗi đề xuất quản trị liên quan đến phân phối, Quản lý tài chính hoặc mã kho chứa đều cần được kiểm tra độc lập – ngay cả khi chỉ là một bản so sánh 10 dòng. Khuyến nghị của OpenZeppelin sau sự cố là một cam kết thường xuyên, trong đó bản so sánh của bất kỳ đề xuất nào, không chỉ là hợp đồng đầy đủ, được kiểm tra trước khi đến lượt bỏ phiếu trên chuỗi. Chi phí kiểm tra 10 dòng là rất nhỏ so với chi phí vận chuyển chúng mà không được kiểm tra.
Các yếu tố này kết hợp lại loại bỏ các trường hợp thất bại cụ thể: (1) phát hiện lỗi logic trước khi phát hành, (2) + (4) rút ngắn thời gian nếu lỗi tràn qua, (3) hạn chế thiệt hại cho mỗi khối khi nó đang hoạt động, và (5) đảm bảo bước (1) không bị bỏ qua khi thay đổi trông nhỏ. Dạy bài học về quản trị timelock là một bài học có giá trị: timelock là một tính năng minh bạch, không phải là một giới hạn bảo mật, và kết hợp nó với một nhà phân phối không thể ngừng hoạt động sẽ biến mỗi lỗi trong quản trị thành một kho lưu trữ mở.
Những câu hỏi thường gặp
Số tiền bị đánh cắp trong vụ tấn công của Compound Finance là bao nhiêu? Tùy thuộc vào giá của COMP khi được tính toán, từ 147 triệu USD đến 162 triệu USD. Đến ngày 30 tháng 9, đã có khoảng 80 triệu USD (240.000 COMP) được yêu cầu quá mức, với khoảng 67–90 triệu USD (280.000 COMP) vẫn còn bị rủi ro trong khu chứa. Số tiền 147 triệu USD trong tiêu đề bao gồm cả số tiền đã được thu về và số tiền còn bị rủi ro; 162 triệu USD bao gồm số tiền được tính trong kỳ khắc phục 7 ngày. Khoảng 30–40 triệu USD (35%) đã được trả lại tự nguyện; 50–60 triệu USD được giữ lại.
Hack Compound Finance hoạt động như thế nào? Đó không phải là một hack bên ngoài mà là một lỗi quản trị. Đề xuất 062 nâng cấp Comptroller và thay đổi cách khởi tạo chỉ số phân phối COMP. Đối với các thị trường chưa bao giờ phân phối COMP, supplyIndex toàn cầu được đặt thành 1e36 trong khi chỉ số lưu trữ của các nhà cung cấp hiện có vẫn là 0, do đó, phép tính thưởng tiếp theo cho ra kết quả pending = supplierBalance * 1e36 / 1e36 = supplierBalance - phát hành COMP bằng số tương đương với toàn bộ số dToken của người dùng theo yêu cầu. Bất kỳ ai cũng có thể kích hoạt nó bằng cách gọi claimComp() hoặc drip().
Có phải Compound bị tấn công bởi một nhóm quản trị không? Không. Một cuộc tấn công quản trị (ví dụ, Beanstalk, nơi một kẻ tấn công sử dụng một khoản vay nhanh để chiếm quyền bỏ phiếu và thông qua một đề xuất độc hại) liên quan đến việc chiếm quyền bỏ phiếu theo cách xấu. Trong trường hợp của Compound, đó là một lỗi quản trị: cộng đồng đã hợp pháp bỏ phiếu thông qua Đề xuất 062, nhưng mã được cài đặt có lỗi logic. Thời gian giới hạn và quá trình bỏ phiếu hoạt động theo cách thiết kế; hợp đồng mà họ ủy quyền không làm được.
Tại sao phải mất 7 ngày để sửa lỗi Compound? Quản trị Compound yêu cầu một thời gian bỏ phiếu 3 ngày cộng với sự chậm trễ 2 ngày của Timelock sau khi xếp hàng – khoảng 7 ngày từ đầu đến cuối. Bản sửa lỗi (Dự án 063) được gửi vào ngày 30 tháng 9 nhưng không thể thực hiện cho đến ngày 8 tháng 10. Trong 7 ngày đó, người quản lý lỗi tiếp tục phân phối quá nhiều COMP mỗi khối. Compound không có thời gian ngưng toàn cầu trên claimComp() và không có đường dẫn nhanh của người bảo vệ, vì vậy không có cách nhanh hơn.
Có phải người dùng Compound đã nhận lại tiền của họ không? Tiền "được trộm" là COMP từ kho dự trữ mà không nên được phân phối - không phải là tiền gửi của người dùng (cTokens và tài sản đảm bảo liên quan không bao giờ được chuyển). Khoảng 35% COMP thừa thãi đã được tự nguyện trả lại thông qua các chuyển khoản trực tiếp và chức năng returnComp() được thêm vào trong Đề xuất 063. Còn lại được giữ bởi những người có quyền yêu cầu. Tiền gửi và vay của người dùng không bị ảnh hưởng; thiệt hại là do sự phân tán (cung cấp thêm COMP) chứ không phải do quản lý.
Có xảy ra sự cố tương tự như Compound kể từ đó không? Có — các lỗi trong phân phối có thể nâng cấp quản trị đã tái phát (ví dụ, đề xuất quản trị của Audius, Build Finance), và danh mục rộng hơn của các sự cố trong quản trị định vị thời gian (timelock governance defi) hiện nay thường bao gồm một giám sát ngừng và kiểm tra khác biệt như là các biện pháp phòng ngừa tiêu chuẩn. Compound sau đó đã thêm một giám sát ngừng và hiện nay, mọi đề xuất khác biệt của Comptroller đều được kiểm tra độc lập trước khi được đưa ra cho bình chọn trên chuỗi.
Kết luận
Hacker tài chính phức tạp tồn tại không phải vì nó là tổn thất DeFi lớn nhất năm 2021 — 611 triệu USD của Poly Network và 625 triệu USD của Ronin đã vượt qua nó — mà vì nó đã lộ ra chính bản chất của quản trị như một bề mặt tấn công. Một nâng cấp mười dòng, được thông qua bằng cách bỏ phiếu của cộng đồng, đã biến nhà phân phối thưởng của quy trình thành một vòi nước mở; một sự trì hoãn minh bạch hai ngày, được thiết kế để bảo vệ người dùng khỏi các đề xuất xấu, đã bảo vệ lỗi khỏi việc sửa chữa trong một tuần đầy đủ; và phục hồi không phụ thuộc vào mật mã mà phụ thuộc vào sự phối hợp xã hội — một hàm returnComp() chỉ hoạt động vì những người yêu cầu đã chọn gọi nó. Lời khuyên kỹ thuật là chính xác: đừng phát hành logic phân phối mà không có điều kiện "pending < balance>", đừng kết hợp nhà phân phối không thể ngừng với một đường dẫn quản trị hoàn toàn bị khóa thời gian, và đừng xem một sự khác biệt nhỏ là quá nhỏ để kiểm tra. Lời khuyên về quản trị có thể duy trì hơn: trong DeFi, phiếu bầu chính là mã, và khóa thời gian không phải là một giới hạn bảo mật — nó là một đồng hồ đếm thời gian cho lỗi và sửa chữa.
