
SharkTeam: Phân tích nguyên lý lỗ hổng giả mạo địa chỉ tùy ý trong Multicall
Tuyển chọn TechFlowTuyển chọn TechFlow

SharkTeam: Phân tích nguyên lý lỗ hổng giả mạo địa chỉ tùy ý trong Multicall
Nguyên nhân gốc rễ của vụ tấn công này: trong ERC-2771, [Forwarder] không được thiết kế riêng cho multicall.
Tác giả: SharkTeam
Ngày 8 tháng 12 năm 2023, OpenZeppelin đã chính thức phát đi một cảnh báo an ninh quan trọng tới cộng đồng. Cảnh báo cho biết rằng khi tích hợp tiêu chuẩn ERC-2771 với các phương pháp kiểu Multicall trong dự án, có thể tồn tại rủi ro bị tấn công lừa đảo địa chỉ tùy ý.
SharkTeam đã ngay lập tức tiến hành phân tích kỹ thuật sự việc này và tổng hợp các biện pháp phòng ngừa an toàn, hy vọng các dự án sau này có thể lấy đó làm bài học, cùng nhau xây dựng hàng rào an toàn cho ngành công nghiệp blockchain.
Một, Phân tích giao dịch tấn công
Do tồn tại một loạt giao dịch tấn công liên quan đến lỗ hổng này, chúng tôi chọn ra một giao dịch để phân tích.
Địa chỉ tin tặc:
0xFDe0d1575Ed8E06FBf36256bcdfA1F359281455A
Giao dịch tấn công:
0xecdd111a60debfadc6533de30fb7f55dc5ceed01dfadd30e4a7ebdb416d2f6b6
Quy trình tấn công:
1. Trước tiên, tin tặc (0xFDe0d157) dùng 5 WETH đổi được khoảng 3.455.399.346 TIME.

2. Sau đó, tin tặc (0xFDe0d157) tạo tham số calldata độc hại và gọi hàm [Forwarder].execute.
3. Khi gọi hàm [Forwarder].execute, calldata độc hại kích hoạt hàm multicall của hợp đồng TIME. Tiếp theo, sử dụng phần còn lại của calldata để thực thi hàm burn của hợp đồng TIME, đốt cháy token TIME trong bể thanh khoản.

Hai, Phân tích lỗ hổng
Trước hết, sự kiện tấn công lần này chủ yếu liên quan đến một số khía cạnh: ERC2771, Multicall, calldata được cấu tạo cẩn thận. Chúng ta có thể tìm thấy các lớp kế thừa liên quan trong hợp đồng token TIME:

1. ERC2771 cung cấp khả năng có msg.sender ảo, cho phép người dùng ủy quyền cho bên thứ ba [Forwarder] thực hiện giao dịch nhằm giảm chi phí gas. Khi gửi giao dịch, địa chỉ msg.sender sẽ được thêm vào calldata.
2. Hợp đồng token TIME kế thừa từ ERC2771Context. Khi [Forwarder] gọi hợp đồng, _msgSender() sẽ kiểm tra dữ liệu calldata, dịch phải và cắt 20 byte cuối cùng làm msg.sender mong muốn.

3. Multicall là một phương pháp biến một lời gọi hàm đơn thành việc gọi tuần tự nhiều hàm trong cùng một hợp đồng. Nó nhận một mảng các lời gọi do người dùng mã hóa và thực hiện trên chính hợp đồng đó. Hàm này duyệt qua mảng lời gọi và thực hiện delegatecall() cho từng thao tác. Điều này cho phép người dùng kết hợp chuỗi thao tác riêng của mình và thực hiện tuần tự trong cùng một giao dịch, mà không cần định nghĩa trước tổ hợp thao tác nào đó trong giao thức. Mục đích chính cũng là tiết kiệm gas.

4. Đối với calldata được cấu tạo cẩn thận, tin tặc gọi hàm [Forwarder].execute và truyền các tham số liên quan.

Sau khi định dạng lại giá trị data dưới dạng dễ đọc hơn, ta thu được:

Tin tặc (0xFDe0d157) thông qua thao tác offset trên calldata hiện tại để lấy giá trị data mới, sau đó truyền giá trị này vào hàm multicall(bytes[]). 4 byte đầu tiên của data mới là bộ chọn hàm burn(uint256), tham số amount là 62227259510000000000000000000.
5. Trong hàm multicall(bytes[]), thông qua delegatecall gọi hàm burn(uint256). Tại dòng 0x20, địa chỉ 0x760dc1e043d99394a10605b2fa08f123d60faf84 là địa chỉ được thêm vào cuối lúc tạo calldata. Địa chỉ này tương ứng với bể thanh khoản TIME-ETH trên Uniswapv2, tức là msg.sender mong muốn đã đề cập ở trên.

6. Vì sao msg.sender nói trên lại trở thành địa chỉ bể thanh khoản TIME-ETH? Lý do là ban đầu msg.sender là địa chỉ hợp đồng [Forwarder]. Để xác định xem [Forwarder] có đáng tin cậy hay không, nếu là [Forwarder] đáng tin cậy, thì msg.sender sẽ được đặt thành 20 byte cuối cùng của calldata.
Ba, Đề xuất an toàn
Nguyên nhân gốc rễ của sự kiện tấn công này: Trong ERC-2771, [Forwarder] không được thiết kế riêng cho multicall. Tin tặc thêm các tham số liên quan trong hàm _msgSender() vào lời gọi bên ngoài multicall, cụ thể là hàm [Forwarder].execute trong sự kiện này. Trong hàm multicall, một số hàm cũng sẽ thêm các tham số liên quan từ _msgSender(), cho phép tin tặc đánh lừa _msgSender(). Do đó, tin tặc có thể mô phỏng lời gọi từ bất kỳ địa chỉ nào bằng cách sử dụng multicall để gọi các hàm liên quan. Cuối cùng, thông qua ủy quyền, đốt cháy token TIME trong bể thanh khoản.
Đối với sự kiện này, có thể áp dụng các biện pháp giảm nhẹ và phòng ngừa sau:
1. Sử dụng phiên bản mới đã sửa lỗi, phiên bản mới của Multicall từ OpenZeppelin có độ dài hậu tố context dành cho dữ liệu ERC2771context, dùng để xác định độ dài hậu tố context mong muốn của ERC-2771. Do đó, mọi lời gọi nào từ [Forwarder] đáng tin cậy đều sẽ được nhận diện và điều chỉnh phù hợp cho từng lời gọi hàm con.

Dưới đây là hình ảnh so sánh giữa phiên bản có lỗi và phiên bản đã cập nhật:

2. Cấm bất kỳ hợp đồng nào gọi multicall để ngăn [Forwarder] sử dụng nó. Ví dụ như ThirdWeb, phương pháp này so với giải pháp của OpenZeppelin thì OpenZeppelin vẫn cho phép multicall thông qua hợp đồng. Dưới đây là hình ảnh so sánh giữa phiên bản lỗi và phiên bản đã cập nhật của ThirdWeb.

【Miễn trừ trách nhiệm】Thị trường tiềm ẩn rủi ro, đầu tư cần thận trọng. Bài viết này không cấu thành lời khuyên đầu tư. Người dùng nên cân nhắc xem bất kỳ ý kiến, quan điểm hay kết luận nào trong bài viết có phù hợp với hoàn cảnh cụ thể của họ hay không. Việc đầu tư dựa trên nội dung này hoàn toàn do người dùng chịu trách nhiệm.
Chào mừng tham gia cộng đồng chính thức TechFlow
Nhóm Telegram:https://t.me/TechFlowDaily
Tài khoản Twitter chính thức:https://x.com/TechFlowPost
Tài khoản Twitter tiếng Anh:https://x.com/BlockFlow_News










