Slow Mist: Phân tích toàn bộ quá trình Lendf.Me bị đánh cắp và đề xuất phòng thủ
慢雾科技
2020-04-19 09:23
本文约2480字,阅读全文需要约10分钟
Theo phân tích thống kê sơ bộ của hệ thống chống rửa tiền (AML) SlowMist, thiệt hại lũy kế của Lendf.Me bị tấn công là khoảng 24.696.616 đô la Mỹ.

Lưu ý của biên tập viên: Bài viết này đến từCông nghệ phun sương chậm (ID: SlowMist)Lưu ý của biên tập viên: Bài viết này đến từ

Công nghệ phun sương chậm (ID: SlowMist)

, được in lại với sự ủy quyền từ Odaily.

WETH: 55159.02134,
WBTC: 9.01152,
CHAI: 77930.93433,
HBTC: 320.27714,
HUSD: 432162.90569,
BUSD: 480787.88767,
PAX: 587014.60367,
TUSD: 459794.38763,
USDC: 698916.40348,
USDT: 7180525.08156,
USDx: 510868.16067,
imBTC: 291.3471

Theo phân tích thống kê sơ bộ của hệ thống phòng chống rửa tiền (AML) của SlowMist Technology, thiệt hại lũy kế của Lendf.Me khi bị tấn công là khoảng 24.696.616 đô la Mỹ, số tiền và số tiền bị đánh cắp cụ thể như sau:

Sau đây là quá trình phân tích chi tiết:

chi tiết tấn công

tiêu đề phụ

chi tiết tấn công

Địa chỉ của kẻ tấn công Lendf.Me lần này là 0xa9bf70a420d364e923c74448d9d817d3f2a77822 và kẻ tấn công đã tấn công Lendf.Me bằng cách triển khai hợp đồng 0x538359785a8d5ab1a741a0ba94f26a800759d91d.

Bằng cách xem một trong những giao dịch của kẻ tấn công trên Etherscan: https://etherscan.io/tx/0xae7d664bdfcc54220df4f18d339005c6faf6e62c9ca79c56387bc0389274363b

Chúng tôi phát hiện ra rằng kẻ tấn công đã gửi 0,00021593 imBTC lần đầu tiên, nhưng đã rút thành công 0,00043188 imBTC từ Lendf.Me và số tiền đã rút gần gấp đôi số tiền đã gửi. Vậy làm thế nào mà kẻ tấn công có được số dư gấp đôi từ một giao dịch ngắn? Điều này đòi hỏi chúng ta phải phân tích sâu sắc từng hành động trong giao dịch để xem điều gì đã xảy ra.

Bằng cách xem giao dịch trên bloxy.info, chúng tôi có thể biết toàn bộ quá trình giao dịch

Ngay sau đó, trong lần gọi thứ hai của chức năng supply(), kẻ tấn công bắt đầu một cuộc gọi đến chức năng rút tiền() của Lendf.Me trong hợp đồng của chính mình và cuối cùng rút tiền mặt.

Ở đây, chúng ta có thể dễ dàng thấy rằng lệnh gọi rút tiền () của kẻ tấn công xảy ra trong hàm transferFrom, nghĩa là khi Lendf.Me gọi hàm hook tokensToSend() của người dùng thông qua transferFrom. Rõ ràng, kẻ tấn công đã nhập lại hợp đồng Lendf.Me thông qua hàm supply(), gây ra cuộc tấn công vào lại, vậy chi tiết cụ thể của cuộc tấn công là gì? Tiếp theo chúng ta cùng theo dõi mã hợp đồng của Lendf.Me nhé.

tiêu đề phụ

phân tích mã

Sau một loạt xử lý, hàm supply() của Lendf.Me sẽ gọi hàm doTransferIn để ký gửi loại tiền do người dùng cung cấp vào hợp đồng, sau đó gán một số thông tin của biến thị trường. Nhìn lại quá trình tấn công vừa đề cập, kẻ tấn công đã gọi chức năng rút tiền () để rút tiền thông qua tái nhập trong hàm cung cấp () thứ hai, nghĩa là, trong hàm cung cấp () thứ hai, sau dòng 1590 Thao tác sẽ không được thực hiện trước khi rút() và mã sau dòng 1590 sẽ tiếp tục thực hiện sau khi rút() được thực hiện. Các hoạt động ở đây dẫn đến sự gia tăng số dư có thể rút của kẻ tấn công.

Hãy cùng phân tích sâu về hàm supply():

Theo hình trên ta thấy khi kết thúc hàm supply() số dư của thị trường và người dùng sẽ được cập nhật, trước đó số dư của người dùng sẽ được lấy trước khi bắt đầu hàm và được lưu trong localResults.userSupplyCurrent, như sau:

Bằng cách gán một giá trị cho biến localResults, thông tin chuyển khoản của người dùng sẽ được lưu trữ tạm thời trong biến này trước, sau đó kẻ tấn công sẽ thực hiện hàm rút tiền (), lúc này chúng ta hãy xem mã của hàm rút tiền ():

Có hai điểm chính ở đây:

1. Khi bắt đầu chức năng, hợp đồng đầu tiên nhận được thị trường lưu trữ và các biến supplyBalance.

Theo logic rút tiền thông thường, khi rút tiền () được thực hiện một mình, số dư của người dùng sẽ được khấu trừ và cập nhật bình thường, nhưng vì kẻ tấn công nhúng rút tiền () vào cung (), số dư của người dùng được cập nhật trong chức năng rút tiền () Sau số dư (supplyBalance), mã tiếp theo sẽ được thực thi trong hàm supply(), tức là sau dòng 1590, số dư của người dùng sẽ được cập nhật lại và giá trị được sử dụng để cập nhật sẽ được lưu ở đầu dòng trước đó hàm supply() Khoản ký gửi ban đầu của người dùng trong localResults cộng với giá trị của lệnh gọi đầu tiên của kẻ tấn công đến khoản ký gửi hàm supply().

Theo thao tác như vậy, mặc dù số dư của người dùng đã bị trừ sau khi rút tiền, logic của hàm supply() tiếp theo sẽ ghi đè giá trị khi người dùng chưa trừ lại số tiền rút, khiến kẻ tấn công thực hiện thao tác rút tiền, nhưng thay vì khấu trừ số dư, nó làm cho số dư tăng lên. Bằng cách này, kẻ tấn công có thể rút tiền mặt theo cấp số nhân cho đến khi Lendf.Me cạn kiệt.

Khi nhiều hợp đồng được kết nối, cũng cần kiểm tra bảo mật mã và bảo mật kinh doanh của hợp đồng nhiều bên và xem xét đầy đủ các vấn đề bảo mật trong sự kết hợp của các tình huống kinh doanh khác nhau

  • Thêm cơ chế khóa vào các phương thức vận hành kinh doanh chính, chẳng hạn như: OpenZeppelin's ReentrancyGuard

  • Khi phát triển hợp đồng, hãy sử dụng phong cách viết trước tiên thay đổi các biến của hợp đồng này, sau đó thực hiện các cuộc gọi bên ngoài

  • Trước khi dự án được triển khai trực tuyến, một nhóm bảo mật xuất sắc của bên thứ ba được mời tiến hành kiểm tra bảo mật toàn diện để khám phá các vấn đề bảo mật tiềm ẩn nhiều nhất có thể

  • Khi nhiều hợp đồng được kết nối, cũng cần kiểm tra bảo mật mã và bảo mật kinh doanh của hợp đồng nhiều bên và xem xét đầy đủ các vấn đề bảo mật trong sự kết hợp của các tình huống kinh doanh khác nhau

  • Hợp đồng nên đặt công tắc tạm dừng càng nhiều càng tốt, để kịp thời phát hiện và ngăn chặn thua lỗ khi có sự kiện “thiên nga đen” xảy ra

  • Bảo mật luôn năng động và mỗi bên dự án cũng cần nắm bắt thông tin tình báo về mối đe dọa có thể liên quan đến dự án của mình một cách kịp thời và nhanh chóng điều tra các rủi ro bảo mật tiềm ẩn

Hợp đồng nên đặt công tắc tạm dừng càng nhiều càng tốt, để kịp thời phát hiện và ngăn chặn thua lỗ khi có sự kiện “thiên nga đen” xảy ra

OpenZeppelin ReentrancyGuard

慢雾科技
作者文库