Bối cảnh

Vào tháng 5 năm 2026, dự án ShapeShift FOX Colony triển khai hợp đồng EtherRouterCreate3 trên Arbitrum đã bị tấn công. Kẻ tấn công đã lợi dụng cơ chế giao dịch meta của hợp đồng với khả năng "tự gọi tùy ý", kết hợp với logic ủy quyền tự động của DSAuth trên address(this), để vượt qua bộ điều chỉnh auth và thay thế thành phần định tuyến cốt lõi của hợp đồng là resolver bằng một phiên bản độc hại, từ đó sử dụng delegatecall để xóa sạch toàn bộ tài sản ERC20 mà hợp đồng nắm giữ. Bản chất của cuộc tấn công này là một cuộc vượt quyền hoàn toàn do "xung đột ngữ nghĩa giữa giao dịch meta và mô hình ủy quyền tự gọi nội bộ" gây ra.

Tổng quan về tấn công

Nguyên nhân lỗ hổng

Tự gọi bất kỳ của executeMetaTransaction: address(this).call(callData) không lọc selector nhạy cảm

Hợp đồng EtherRouter chính nó là một kiến trúc proxy có thể nâng cấp dựa trên resolver: đối với selector chức năng không biết, fallback() gọi resolver.lookup(msg.sig) để tìm địa chỉ thực hiện rồi thực thi qua delegatecall. Chức năng giao dịch meta (executeMetaTransaction) đã được router bởi resolver cũ 0x7490022b0e44aa65c030ac0d6728382a29458fc5 đến hợp đồng thực hiện 0x4e7f1e1e263678590007e89b7e129686ba7758d4 để thực thi. Hợp đồng thực hiện này chưa mã nguồn mở, dưới đây dựa trên kết quả phản biện:

Bản chất vấn đề: thiết kế của executeMetaTransaction là cho phép người dùng thực hiện một số thao tác không nhạy cảm qua chữ ký, nhưng nó không lọc functionSignature. Kẻ tấn công có thể sử dụng chữ ký hợp lệ của chính mình, khiến hợp đồng gọi setResolver (địa chỉ độc hại).

Ủy quyền tự động của DSAuth.isAuthorized: src == address(this) thì được phép

EtherRouter.setResolver(address) được bảo vệ bởi bộ sửa đổi auth, chỉ nên cho phép owner hoặc authority gọi:

Nhưng trong DSAuth.isAuthorized(address src, bytes4 sig) có logic ủy quyền tự động tự gọi:

Khi executeMetaTransaction được kích hoạt qua address(this).call(setResolver(...)), msg.sender trong setResolver là hợp đồng tự nó 0x5c59..., do đó bị DSAuth tự động cho phép.

Xét riêng, hai thiết kế này không phải là lỗ hổng rõ ràng — tự gọi ủy quyền rất phổ biến trong nhiều mô hình proxy, cơ chế giao dịch meta cũng hợp lý. Nhưng khi hai bộ logic đồng thời tồn tại, mâu thuẫn ngữ nghĩa phát sinh: khả năng 'tự gọi bất kỳ' mà giao dịch meta cung cấp va chạm với logic 'tự gọi tức là tin tưởng' của DSAuth, kết hợp lại tạo thành chuỗi vượt quyền hoàn chỉnh.

Dynamic routing delegatecall của EtherRouter.fallback(): quyền kiểm soát hoàn toàn được chuyển giao sau khi Resolver bị chiếm

Sau khi resolver bị thay thế, kẻ tấn công chỉ cần gọi bất kỳ selector chức năng nào không tồn tại trên EtherRouter, fallback() sẽ ủy quyền vô điều kiện đến thực hiện độc hại do kẻ tấn công kiểm soát.

Resolver độc hại và thực hiện Drain: bảng đăng ký chức năng không có quyền + dọn sạch tài sản address(this)

Kẻ tấn công đã triển khai trước hai hợp đồng:

Resolver độc hại

0x4e321af09012e15a67756522187c05b108b7ee0a (chưa mã nguồn mở, đã phản biện):

Thực hiện Drain độc hại

0x0b971e0a8ecc7d5b2465c903cf75aeaedbfc39e2 (chưa mã nguồn mở, đã phản biện):

Công thức lợi nhuận tấn công

Quá trình tấn công

Quá trình tấn công chỉ hoàn thành trong một giao dịch, tất cả logic thực thi trong constructor của hợp đồng tấn công tạm thời 0x835a701fd76b96a76ee84de037d41f059ee29f5c.

Giai đoạn đầu tiên: Triển khai cơ sở hạ tầng độc hại

  1. Kẻ tấn công EOA 0xeed236afb6967f74099a0a6bf078bc6b865fbf28 phát động giao dịch, tạo hợp đồng tấn công tạm thời 0x835a701fd76b96a76ee84de037d41f059ee29f5c.

  2. Hợp đồng tấn công gọi resolver độc hại 0x4e321af09012e15a67756522187c05b108b7ee0a với set(bytes4,address), ánh xạ selector drain(address,address) 0x837971e4 đến thực hiện drain độc hại 0x0b971e0a8ecc7d5b2465c903cf75aeaedbfc39e2.

Giai đoạn hai: Chiếm quyền Resolver qua giao dịch meta tự gọi

  1. Hợp đồng tấn công gọi executeMetaTransaction() của hợp đồng bị hại 0x5c59d0ec51729e40c413903be6a4612f4e2452da. Do hàm này không có trong ABI của EtherRouter, gọi vào fallback(), được router bởi resolver cũ đến thực hiện giao dịch meta 0x4e7f1e....

  2. executeMetaTransaction qua ecrecover xác minh chữ ký, khôi phục ra EOA của kẻ tấn công, xác minh chữ ký thành công (kẻ tấn công đã sử dụng chữ ký hợp lệ của chính mình, không phải chữ ký giả mạo).

  3. executeMetaTransaction xây dựng calldata tự gọi setResolver(0x4e321af...) và thực thi address(this).call(callData). Lúc này bối cảnh là hợp đồng bị hại, do đó msg.sender == 0x5c59.... DSAuth.isAuthorized() do src == address(this) trả về true, resolver đã được thay thế thành công.

Giai đoạn ba: Dọn sạch tài sản qua Resolver bị chiếm

  1. Hợp đồng tấn công gọi hàm drain(USDC, 0xeed236...) của hợp đồng bị hại. Hàm này không có trong ABI gốc của EtherRouter, nên vào fallback(). Resolver bị chiếm trả về thực hiện drain độc hại 0x0b971e0..., hợp đồng bị hại thực hiện delegatecall. Mã độc truy vấn USDC.balanceOf(0x5c59...) nhận được 132704591501 (tức là 132,704.591501 USDC), sau đó gọi USDC.transfer() chuyển trực tiếp vào EOA của kẻ tấn công.

  2. Hợp đồng tấn công lại gọi drain(0xf929..., 0x835a701f...), theo cùng một đường dẫn chuyển token trung gian bị đánh cắp 841086343608217839604694 đơn vị vào hợp đồng tấn công.

  3. Hợp đồng tấn công thông qua Router 0x4752ba5dbc23f44d87826276bf6fd6b1c372ad24 đã trao đổi token trung gian bị đánh cắp trong Pair 0x5f6ce0ca... thành 1.949506469643782660 WETH, WETH trực tiếp chuyển vào EOA của kẻ tấn công.

Đóng lợi nhuận

Theo dõi tài chính

Phân tích địa chỉ EOA kẻ tấn công 0xeed236afb6967f74099a0a6bf078bc6b865fbf28 qua SlowMist MistTrack:

  • Dấu vết chính:

    • Relay.link — $4,368.08, DEX aggregator cho trao đổi tài sản.

    • LI.FI — $137,073.66, aggregator Cross-chain/DEX.

Tiền bị đánh cắp của kẻ tấn công đã chảy vào Spark.fi Saving, SlowMist MistTrack sẽ theo dõi liên tục động thái tài chính của các địa chỉ liên quan.

Tóm tắt

Bài học cốt lõi từ cuộc tấn công này là: Khi hợp đồng đồng thời có cả 'tự gọi bất kỳ trong giao dịch meta' và 'tự gọi ủy quyền tự động', cả hai sẽ tạo thành một chuỗi vượt quyền hoàn chỉnh — đây không phải là lỗ hổng mã đơn lẻ, mà là kết quả tất yếu của mâu thuẫn ngữ nghĩa giữa các thành phần. Các nhà phát triển hợp đồng khi thiết kế giao dịch meta hoặc cơ chế relay phải rõ ràng phân định ranh giới chức năng nhạy cảm, ít nhất trong executeMetaTransaction duy trì một danh sách selector cấm gọi, và thận trọng khi sử dụng ủy quyền tự gọi vô điều kiện src == address(this). Nhóm an ninh SlowMist khuyến nghị dự án thực hiện kiểm toán an ninh bên ngoài toàn diện trước khi triển khai.

Chú ý: Bài viết này là phân tích an ninh kỹ thuật, chỉ để tham khảo học tập, không cấu thành bất kỳ lời khuyên đầu tư nào. Tất cả dữ liệu trên chuỗi đều từ thông tin công khai.