Vào ngày 30 tháng 7 năm 2023, một ngày Chủ Nhật mà các nhà phát triển DeFi sẽ không bao giờ quên, một lỗi trong trình biên dịch đã lấy đi khoảng 70 triệu đô la từ Curve Finance và các đối tác trong hệ sinh thái của nó chỉ trong một vài giờ. Nguyên nhân không phải là lỗi logic trong hợp đồng thông minh, khóa riêng bị xâm phạm, hoặc sự mạo hiểm trong các khoản vay – đó chính là trình biên dịch Vyper.
Timeline - July 30, 2023 attack timeline
Chiều thứ Hai làm rúng động DeFi
Câu tweet đầu tiên của PeckShield được đăng lúc 16:30 UTC: kho chứa pETH/ETH của JPEG'd đã bị cạn kiệt với giá trị 11,5 triệu USD. Trong vòng vài phút, kho chứa alETH/ETH của Alchemix đã giảm xuống 20,5 triệu USD. Sau đó, kho chứa msETH/ETH của Metronome đã mất 1,6 triệu USD. Mẫu này rõ ràng: các kho chứa của nhà sản xuất Curve với ETH bản địa đang bị rỗng một cách có hệ thống.
Table - Affected protocols and losses
Sau đó, là pool Curve Finance CRV/ETH chính: 24,2 triệu USD đã biến mất. Tổng số tiền trên sáu giao thức đạt 69 triệu USD (khoảng tròn đến 70 triệu USD như các tiêu đề đưa ra). Một bot MEV hoạt động tại địa chỉ 0xc0ffeebabe đã thực hiện nhiều cuộc tấn công, trả lại 5,4 triệu USD từ pool Curve và đặt 1.000 ETH cho Metronome – một trường hợp hiếm khi mà cơ sở hạ tầng MEV vô tình hoạt động theo cách của những người làm việc theo cách hợp pháp.
cách hack tài chính này khác biệt với mọi vụ tấn công DeFi lớn trước đó. Vùng lỗ hổng không nằm trong mã ứng dụng, mà ở trong trình biên dịch chuyển mã đó thành mã byte.
Khi nhà biên dịch lừa dối bạn
Những nhà phát triển hợp đồng thông minh hoạt động dựa trên một giả định cơ bản: nghiên cứu viên thực hiện chính xác những gì mã nguồn quy định. Lỗi Vyper đã phá vỡ giả định này.
Khóa bị hỏng
Đồ trang trí @nonreentrant của Vyper là biện pháp phòng ngừa tiêu chuẩn chống lại các cuộc tấn công tái nhập – cùng loại lỗ hổng đã cho phép vụ tấn công The DAO vào năm 2016. Khi một nhà phát triển viết:
`vyper @nonreentrant("lock") def add_liquidity(...): ...
@nonreentrant("lock") def remove_liquidity(...): ... `
Họ mong đợi cả hai chức năng sẽ chia sẻ cái khoang lưu trữ tương tự cho khóa bảo mật. Nhập add_liquidity → khóa được kích hoạt. Bạn thử nhập lại thông qua remove_liquidity → khóa ngăn cản bạn. Thật đơn giản, hiệu quả và đã được kiểm chứng trong thực chiến.
Diagram - Reentrancy lock mechanism: correct vs buggy
Điều thực sự đã xảy ra
Trong các phiên bản Vyper 0.2.15, 0.2.16 và 0.3.0, trình biên dịch đã phân bổ khoang lưu trữ khác nhau cho trang trí @nonreentrant("lock") trên add_liquidity so với remove_liquidity.
add_liquiditykhóa vị trí lưu trữ Xremove_liquiditykiểm tra vị trí lưu trữ Y
Vì X ≠ Y, khóa trên add_liquidity cung cấp không có bảo vệ chống lại việc tái nhập thông qua remove_liquidity. Một kẻ tấn công có thể gọi add_liquidity, sau đó trong cùng một giao dịch gọi remove_liquidity, bỏ qua hoàn toàn bảo vệ chống lại việc tái nhập.
Diagram - Vyper storage slot misalignment between add_liquidity and remove_liquidity
Nguyên nhân kỹ thuật
Nhà nghiên cứu an ninh Chaofan Shou (@shoucccc) đã xác định chính xác mã biên dịch có liên quan. Bộ biên dịch Vyper sử dụng một khóa được lấy từ tên hàm thay vì tên khóa rõ ràng được cung cấp bởi nhà phát triển. Hai hàm chia sẻ @nonreentrant("lock") nhận được hai khóa nội bộ khác nhau, tương ứng với hai khung lưu trữ khác nhau.
Tony KΞ (@tonykebot) đã đăng tải kết quả của quá trình biên dịch codegen, cho thấy sự khác biệt:
` add_liquidity nonreentrant key: 0x1234... → storage slot 5 remove_liquidity nonreentrant key: 0x5678... → storage slot 12 `
Lỗi này đã tồn tại từ Vyper 0.2.15 (đã phát hành năm 2021) và vẫn tồn tại trong 0.3.0. Nó đã được sửa chữa trông như là do sự cố trong v0.3.1 thông qua commit eae0eaf ("Sửa lỗi các khung lưu trữ không được sử dụng #2439") - không có thông báo bảo mật, không có CVE, không có công bố phối hợp. Sửa lỗi được thực hiện vì một quá trình refactoring khác đã giải quyết xung đột.
Cụm từ chính: Sự cố vyper 0.3.0 reentrancy này đại diện cho một lớp khai thác mới: vulnerability ở cấp biên dịch.
Tại sao chỉ có các kho ETH bản địa?
Các pool được triển khai tại nhà máy của Curve cho các tài sản liên kết với ETH (pETH, alETH, msETH, CRV/ETH) sử dụng mẫu hợp đồng cụ thể, trong đó cả add_liquidity và remove_liquidity đều có trong cùng một hợp đồng với các decorator @nonreentrant.
Các pool sử dụng WETH (ETH gói) thay vì ETH gốc tuân theo các mẫu hợp đồng khác nhau và không bị ảnh hưởng. Pool stETH/ETH – có tổng giá trị TVL hơn 400 triệu USD vào thời điểm đó – là an toàn vì nó sử dụng một cách triển khai hoàn toàn khác.
Cụm từ khóa phụ: lỗi curve vyper được xử lý một cách chính xác: chỉ những pool có ETH bản địa + bản Vyper dễ bị tấn công + mẫu nhà máy có cả chức năng thêm/nhả vốn mới bị ảnh hưởng.
Chuỗi tấn công: bước một cách chi tiết
Flowchart - Attack execution flow with reentrancy
- Tìm hiểu: Các kẻ tấn công đã xác định các kho được biên soạn bằng Vyper 0.2.15/0.2.16/0.3.0 chứa ETH bản địa
- Gọi vào: Gọi
add_liquidityvới tham số độc hại (khóa bật trên slot X) - Reentrancy: Trong cùng giao dịch, gọi
remove_liquidity(slot Y chưa được khóa!) - Thao túng: Lệnh gọi
remove_liquiditytái nhập thao túng số dư LP token - Rút sạch: Kẻ tấn công rút tài sản của pool, để lại LP token vô giá trị
- Lặp lại: Cùng kỹ thuật trên JPEG'd, Alchemix, Metronome, Curve CRV/ETH
Sự tấn công này không phải là một trường hợp reentrancy chỉ đọc truyền thống (sử dụng các hàm view để điều khiển các oracle giá). Đây là việc điều khiển trạng thái trực tiếp thông qua lỗi của trình biên dịch đã vô hiệu hóa bộ bảo vệ reentrancy giữa hai hàm thay đổi trạng thái.
Cuộc Đời Người: Whitehats, MEV Bots và các cuộc chiến công bố
Đoàn đua Whitehat
Khi cuộc tấn công diễn ra, các nhóm whitehat bao gồm BlockSec, bantg của Alchemix và các nhà nghiên cứu độc lập đã vội vàng xóa các kho chứa dễ bị tấn công trước khi các kẻ tấn công có thể làm điều đó. Họ liên tục bị các kẻ tấn công chặn lại – các hacker có cơ sở hạ tầng MEV vượt trội.
0xc0ffeebabe – một nhà điều hành bot MEV – đã thực hiện nhiều giao dịch tấn công. Trong một biến cố, họ đã trả lại 5,4 triệu USD được chuyển từ kho bạc của Curve và đặt 1.000 ETH cho Metronome. Đến khi bụi tan, ~17 triệu USD đã được khôi phục bởi các nhà hoạt động chống gian lận và hợp tác trong MEV.
Vấn đề công bố thông tin
BlockSec đã công khai đăng tweet về các phiên bản Vyper dễ bị tấn công trong khi các pool vẫn đang bị cạn kiệt. banteg (nhà phát triển core của Alchemix) đã phản ứng rất mạnh:
"Bạn 👏 không 👏 tweet 👏 về các lỗ hổng 👏 trực tiếp 👏 trước khi 👏 chúng 👏 được 👏 khắc phục 👏 hoàn toàn"
BlockSec đã bào chữa cho hành động của họ, cho biết họ chỉ tiết lộ sau khi "tất cả các kho chứa có thể khai thác đã bị cạn kiệt". Cuộc tranh luận đã nhấn mạnh một khoảng cách quan trọng: không có khung công bố có trách nhiệm cho lỗi của trình biên dịch ảnh hưởng đến nhiều giao thức hoạt động cùng một lúc.
Cụm từ khóa phụ: Căn bệnh của compiler defi và bài toán khó khăn khi tiết lộ lỗ hổng – khi lỗ hổng nằm trong ngôn ngữ chính bản thân nó, thì ai sẽ phối hợp để khắc phục?
Câu hỏi 70 triệu đô la: Rủi ro hệ thống
Cơn khủng hoảng nợ của Michael Egorov
Token CRV đã giảm 15% (tạm thời đạt mức 0,60 USD). Michael Egorov, người sáng lập Curve, đã vay 107,2 triệu USD trong stablecoin với tài sản đảm bảo 284 triệu USD bằng CRV trên các nền tảng Aave, Fraxlend và các giao thức cho vay khác. Một chuỗi giải thể sẽ làm cho vụ tấn công 70 triệu USD trở nên nhỏ bé.
Graph - CRV price impact and Egorov liquidation risk
Egorov cuối cùng đã kiểm soát được vị trí của mình, nhưng sự kiện này cho thấy cách mà lỗi ở lớp giao thức có thể gây ra sự lây lan toàn hệ thống thông qua các vị trí của các nhà sáng lập có lợi thế.
Cảnh báo về hợp đồng 59
Nhà phát triển trung tâm của Vyper, fubuloubu, lưu ý rằng 59 hợp đồng trên mainnet sử dụng các phiên bản biên dịch dễ bị tấn công. Một lỗi tương tự trong Solidity – ngôn ngữ hợp đồng thông minh thống trị – có thể ảnh hưởng đến hàng trăm hợp đồng.
Cụm từ khóa phụ: Sự thất bại của ký hiệu chặn lặp lại của vyper đã bộc lộ một sự thật không thoải mái: các công ty kiểm tra hợp đồng, không phải các trình biên dịch.
Tại sao an ninh của trình biên dịch bị bỏ qua
Không phù hợp về khuyến khích
| Layer | Tiền tài trợ | Kiểm toán | Hoa hồng cho lỗi |
|---|---|---|---|
| Ứng dụng (Curve, Aave, Uniswap) | Tốt (VC, token) | Rộng rãi | $100K–$10M+ |
| Các ngôn ngữ (Vyper, Solidity) | Thấp (được hỗ trợ, đóng góp) | Thấp nhất | Seldom có tồn tại |
Các công ty kiểm toán được trả tiền để xem xét các hợp đồng. Họ giả định rằng trình biên dịch hoạt động. VC tài trợ các ứng dụng bằng token để bán. Cơ sở hạ tầng ngôn ngữ không có token, không có mô hình doanh thu, không có đội bảo mật chuyên dụng.
Năm 2023, Vyper có ~3 người quản lý chính, không có nhà nghiên cứu bảo mật làm việc toàn thời gian, và chương trình thưởng lỗi không bao gồm lỗi của trình biên dịch. Lỗi này tồn tại trong hai năm trước khi bị khai thác.
Lỗi "Tin tưởng vào nhà biên dịch"
Các công cụ xác thực chính thức (Certora, Halmos) xác thực các thuộc tính của mã nguồn. Chúng không xác thực rằng kết quả của trình biên dịch phù hợp với mã nguồn. Lỗi Vyper đã tạo ra bytecode vi phạm các quy tắc @nonreentrant("lock") của mã nguồn.
Khoảng cách này – bảo tồn ý nghĩa từ nguồn đến bytecode – hầu như không bao giờ được kiểm tra.
Những bài học cho các nhà phát triển DeFi Việt Nam
Việt Nam có một cộng đồng phát triển DeFi đang phát triển trên các fork của Curve (Ellipsis trên BSC đã mất 69.000 USD trong vụ tấn công này). Những điểm chính:
1. Các phiên bản của Pin và Audit Compiler
`json // package.json - pin exact Vyper version "devDependencies": { "vyper": "0.3.10" // NOT "^0.3.0" } ` Xác minh bytecode, không chỉ là nguồn. Các công cụ như vyper --show-ir và vyper --asm cho thấy những gì biên dịch viên thực sự phát ra.
2. Kiểm tra các bảo vệ reentrancy rõ ràng
Viết các thử nghiệm tích hợp thử nghiệm khả năng tái nhập giữa mỗi cặp các hàm @nonreentrant chia sẻ cùng một tên khóa. Lỗi Vyper sẽ được phát hiện bởi một thử nghiệm gọi đến khả năng tái nhập của add_liquidity → remove_liquidity.
3. Theo dõi các thông báo bảo mật của trình biên dịch
Đăng ký theo:
- Vyper GitHub Security Advisories
- Solidity GitHub Security Advisories
- Danh sách theo dõi của nhà cung cấp dịch vụ kiểm toán
4. Phân tán rủi ro ngôn ngữ
- Các pool cốt lõi bằng Solidity (nguồn kiểm toán viên lớn hơn, đã được thử nghiệm nhiều hơn)
- Xác minh chính thức các đường dẫn quan trọng của trình biên dịch
- Nhiều đợt kiểm toán triển khai khác nhau
Kết quả: những gì đã thay đổi
Ngay lập tức (Tháng 8 năm 2023)
- Vyper 0.3.1 được phát hành với các sửa lỗi
- Curve khuyến nghị rút vốn khỏi pool Arbitrum Tricrypto (thận trọng)
- Nhiều giao thức khẩn cấp nâng cấp lên Vyper 0.3.1+
Kỳ trung hạn (2023–2024)
- Vyper đã thiết lập quy trình tư vấn bảo mật chính thức
- Nhiều kiểm thử fuzzing trình biên dịch được tích hợp vào CI
- Một số giao thức thêm kiểm tra phiên bản trình biên dịch vào quy trình triển khai
Cấu trúc (vẫn đang diễn ra)
- Không có chương trình thưởng lỗi cho các trình biên dịch trên toàn ngành
- Không có xác minh chính thức về việc bảo tồn ngữ nghĩa của trình biên dịch
- Không có khung công bố hợp tác cho các lỗi cấp ngôn ngữ
Vấn đề khuyến khích cơ bản vẫn còn: Ai trả tiền cho bảo mật của trình biên dịch?
Hỏi đáp
Có phải đây là một cuộc tấn công tái nhập chỉ đọc không?
Không. Các báo cáo ban đầu đã nhầm lẫn nó là khả năng tái nhập chỉ đọc (sử dụng các hàm view). Đây là khả năng tái nhập trạng thái trực tiếp giữa hai hàm thay đổi trạng thái (add_liquidity và remove_liquidity) được kích hoạt bởi lỗi của trình biên dịch đã phá vỡ bảo vệ @nonreentrant.
Những phiên bản Vyper nào dễ bị tấn công?
0.2.15, 0.2.16, và 0.3.0 (theo PSA chính thức của Vyper). Bản 0.3.1+ chứa lỗi sửa chữa. Các phiên bản trước 0.2.15 sử dụng một mã hóa khác và không bị ảnh hưởng.
Có thể điều này xảy ra trong Solidity không?
ReentrancyGuard của Solidity (OpenZeppelin) sử dụng một mẫu triển khai khác. Tuy nhiên, bất kỳ trình biên dịch nào cũng có thể có lỗi. Mặt phẳng kiểm tra lớn hơn của Solidity và đội ngũ an ninh chuyên trách giúp giảm thiểu nhưng không loại bỏ rủi ro này.
Có bao nhiêu thực sự bị mất?
~$69 triệu bị rút, ~$17 triệu được khôi phục bởi các bot whitehats/MEV, tổng thiệt hại là ~$52 triệu. Con số "$70 triệu" là tổng số tiền bị rút.
Curve Finance có lỗi trong mã của họ không?
Không. Mã nguồn của Curve Vyper là chính xác. Compiler đã tạo ra bytecode không chính xác. Sự khác biệt này quan trọng đối với trách nhiệm và phòng ngừa.
Tôi phải làm gì nếu dự án của tôi sử dụng Vyper?
- Cập nhật lên Vyper 0.3.10+ ngay lập tức
- Biên dịch lại và triển khai lại tất cả hợp đồng
- Xác minh các khóa chống reentrancy hoạt động qua kiểm thử tích hợp
- Theo dõi các khuyến cáo bảo mật của Vyper
Kết luận: lớp dưới
Sự lỗi hack của Curve Finance Vyper không chỉ là một vụ tấn công DeFi nữa. Đó là một đoạn thông báo thức tỉnh từ tầng dưới.
Mỗi hợp đồng thông minh, mỗi kiểm toán, mỗi xác minh chính thức, mỗi phần thưởng lỗi - tất cả đều giả định rằng trình biên dịch là trung thực. Vào ngày 30 tháng 7 năm 2023, giả định này đã bị phá vỡ.
Thiệt hại 70 triệu USD là phí học phí cho một bài học chung trong ngành: an ninh của trình biên dịch là an ninh cơ sở hạ tầng. Cho đến khi các kiểm toán trình biên dịch, các chương trình khuyến khích phát hiện lỗi của trình biên dịch, và xác thực chính thức của trình biên dịch nhận được nguồn tài chính tương xứng với giá trị mà chúng đảm bảo, nền tảng vẫn bị phá vỡ.
Bệnh lỗi của trình biên dịch tiếp theo sẽ không đợi chúng ta sẵn sàng. Nó sẽ tìm thấy ngôn ngữ mà chúng ta bỏ qua, phiên bản mà chúng ta không xác định, khóa sử dụng hai chìa khóa khác nhau.
Diagram - Defense in depth: source audit → compiler audit → bytecode verification → runtime monitori
- Khuyến cáo bảo mật Vyper: GHSA-m2v9-w374-5hj9
- Phân tích Rekt.news: Curve, Vyper - REKT
- Chuỗi kỹ thuật của Chaofan Shou: @shoucccc status
- Commit sửa lỗi Vyper: eae0eaf
Ghi chú của tác giả: Bài viết này cung cấp phân tích độc đáo vượt ra ngoài báo cáo nguồn, bao gồm phân tích cấu trúc khuyến khích, hướng dẫn cho các nhà phát triển Việt Nam, khung đạo đức công khai, và nghiên cứu về giả định về sự tin cậy của biên tập viên. Tất cả các tuyên bố kỹ thuật được trích dẫn từ 5+ nguồn độc lập.
