LapTrinhBlockchain

Chia sẻ kiến thức về Lập Trình Blockchain

An toàn Bảo mật, Kiến thức Blockchain, Nâng cao Kiến thức

XSURGE bị tấn công mất $5.57M qua lỗ hổng Reentrancy – Hacker đã tấn công như thế nào?

XSURGE bị tấn công mất $5.57M qua lỗ hổng Reentrancy - Hacker đã tấn công như thế nào?

XSURGE bị tấn công mất $5.57M qua lỗ hổng Reentrancy - Hacker đã tấn công như thế nào?

Chia sẻ bài viết
5
(68)

Vào ngày 2021-08-16, XSURGE bị hacker tấn công lấy đi $5.57M (13,112 BNB). Vậy chi tiết cuộc tấn công như thế nào? Lỗ hổng nào mà gây thiệt hại lớn như vậy? Làm sao mô phỏng lại cuộc tấn công của hacker?

Chi tiết giao dịch tấn công của hacker: https://bscscan.com/tx/0x7e2a6ec08464e8e0118368cb933dc64ed9ce36445ecf9c49cacb970ea78531d2

Lỗ hổng gây ra cuộc tấn công

Sở dĩ hacker có thể thực hiện cuộc tấn công là do smart contract có lỗi bảo mật “Reentrancy“. Đây là 1 lỗi rất cơ bản và đã từng được khai thác rất nhiều trước đây.

Trong xử lý hàm sell() sẽ gọi hàm payable(seller).call() để chuyển BNB cho người bán. Việc gọi này sẽ kích hoạt hàm payback nếu người bán là smart contract, trong hàm payback này người bán thực hiện gọi hàm purchase() để mua lại thì sẽ có lợi thế về giá. Bởi vi total_supply được cập nhật sau.

Lỗi bảo mật Reentrancy trong Smart Contract của XSURGE
Lỗi bảo mật Reentrancy trong Smart Contract của XSURGE

Chi tiết cuộc tấn công của hacker

Chi tiết cuộc tấn công như sau:

  • B1: Hacker thực hiện flashloan vây 10,000 BNB
  • B2: Kích hoạt hàm purchase() để mua 202 nghìn tỷ SURGE
  • B3: Gọi hàm sell() để bán 202 nghìn tỷ SURGE và nhận được 9,066 BNB. Việc này cũng kích hoạt hàm payback, và trong hàm này hacker thực hiện mua 290 nghìn tỷ SURGE từ 9,066 BNB.
  • B4: Lặp lại B3 8 lần và thu được 22,191 BNB.
  • B5: Hoàn trả 10,030 BNB và thu được lợi nhuận 12,161 BNB.
  • B6: Hacker lặp lại quá trình từ B1 đến B5 5 lần và thu được tổng 13,112 BNB.
Chi tiết cuộc tấn công của Hacker vào SURGE
Chi tiết cuộc tấn công của Hacker vào SURGE

Chi tiết xem tại: https://twitter.com/peckshield/status/1427504583971155989

Tái hiện lại lỗi trên môi trường Testnet

Tìm hiểu về Surge Contract

Đầu tiên chúng ta đọc qua code của SURGE Contract, sẽ thấy:

  • Đọc qua contract ta thấy:
    • Trong hàm khởi tạo ta thấy: Owner sẽ được nhận 1 tỷ token đầu tiên
    • Hàm purchase() là hàm internal nên không gọi từ ngoài được.
    • Hàm purchase() được gọi từ hàm “receive() external payable” => Ta biết cách mua token.
    • Hàm sell() để bán token. Khi bán nó gọi hàm calculatePrice(), bạn chú ý đây là phép chia hai số nguyên.
  • Làm thế nào để mua token:
    • Để mua token, bạn chỉ cần gửi BNB vào contract của SURGE. Công thức tính lượng token nhận được như sau:
      tokenAmount = 94%*totalSupply*bnbAmount/contractBnbBalance
      Trong đó:
      totalSupply: Là tổng lượng SURGE hiện tại trước khi thực hiện lệnh mua
      bnbAmount: Số lượng BNB dùng để mua token.
      contractBnbBalance: Lượng BNB có trong contract trước khi thực hiện mua. Nếu ban đầu contract không có BNB thì nó bằng giá trị bnbAmount.
    • Sau khi mua xong, các thông số sẽ thay đổi:
      totalSupply = totalSupply + tokenAmount
      contractBnbBalance = contractBnbBalance + bnbAmount
  • Làm thế nào để bán token:
    • Để bán token người dùng gọi hàm sell().
    • Công thức tính bnb như sau:
      amountBNB = 0.94*tokenAmount*TRUNC(contractBnbBalance/totalSupply)
  • Nếu ta thực hiện 1 cặp (SELL + BUY qua payback) thì bạn sẽ thấy:
    • Sau thi thực hiện cả quá trình thì totalSupply thay đổi nhưng contractBnbBalance lại không đổi.
    • Quá trình sau khi SELL và trước khi BUY thì totalSupply lại không đổi và contractBnbBalance lại thay đổi.

Tính toán với tham số như thế nào thì có lãi

Để có lãi thì sau mỗi cặp (SELL+BUY) thì lượng token phải tăng thêm, sau đó càng thực hiện nhiều lần ta được càng nhiều token SURGE. Cuối cùng ta bán, trừ phí và có lợi nhuận.

Tính toán thử trên contract SURGE thật ở thời điểm hiện tại

Sau đợt tấn công ngày 2021-08-16, hiện tại trên contract thật của SURGE chỉ còn 2.83578 BNB và 1.83753E+18 token SURGE (Số liệu này có thể thay đổi trong tương lai). Với lượng hiện tại như này thì tấn công sẽ không có lợi nhuận. Bạn xem bảng tính toán dưới sẽ thấy lượng token thư được giảm dần và số BNB cũng giảm:

Bảng tính toán nếu thực hiện tấn công tại thời điểm hiện tại
Bảng tính toán nếu thực hiện tấn công tại thời điểm hiện tại

Các tính toán này hoàn toàn khớp với thử nghiệm của tôi tại giao dịch: 0x047c34bd61e4bc763381f227afc2710d6519ac2c56f7e89bcf235687aa8d3395

Như vậy hiện tại chúng ta không thể thử nghiệm tấn công contract thật của SURGE trên Mainnet.

Tính toán để tái hiện giao dịch thành công trên testnet

Nhưng để tạo ra case thành công tôi đã thử nghiệm trên môi trường testnet với số liệu khác. Tôi đã khởi tạo contract SURGE trên testnet với 5 BNB1,940,000,000 SURGE token. Sau đó thử tỉnh toán:

Với số liệu thử nghiệm thì sau 4 lần lặp thì bắt đầu có lãi
Với số liệu thử nghiệm thì sau 4 lần lặp thì bắt đầu có lãi

Tái hiện trên BSC Testnet

Bây giờ chúng ta từng bước tái hiện lại như sau:

  • B1: Dựng lại SURGE Contract trên BSC Testnet: 0x3FE4Eaf8EBF820C8E804a237038438303111c0AE
  • B2: Viết contract cài đặt hàm attack() để tấn công (Các bạn tự làm nhé):
    • B2.1: Contract này sẽ sử dụng flashloan để vay 1 WBNB từ Pancake, sau đó convert lượng WBNB sang BNB. Tham khảo bài viết: Hướng dẫn triển khai Flashloan trên Binance Smart Chain BSC Testnet
    • B2.2: Sử dụng BNB để tấn công, trong code bạn phải lặp lại quá trình SELL SURGE – Buy SURGE nhiều lần để thành công => Trong code tôi thực hiện lặp lại 8 lần (Theo như tính toán ở trên thì ít nhất phải lặp 4 lần nhé).
    • B2.3: Convert từ BNB sang WBNB và trả lại Pancake.
      => Contract này các bạn tự viết để nâng cao kỹ năng nhé. Đây là contract tôi đã triển khai thử nghiệm: 0x62C4cDA6ac6E9838529BcFdfe18f14068588E051
  • B3: Sau khi đã có contract thì ta bắt đầu thử tấn công nhé:
    • B3.1: Để tấn công thì ban đầu SURGE contract phải có BNB trước đã. Tôi sử dụng 1 account khác chuyển 5 BNB vào SURGE Contract để mua SURGE.
    • B3.2: Chuyển sang account chạy bot, gọi hàm attack() để chạy. Đây là giao dịch tấn công: 0x4b55d4f0985a4cecded54b023703dc3f81c7dd6835255deb5485b5666ca444af
      Nhìn vào giao dịch này bạn sẽ thấy ban đầu tôi vay 1 WBNB từ Pancake, cuối cùng tôi trả lại 1.003 WBNB cho Pancake. Và tôi dư ra 0.226 WBNB => Đây chính là lợi nhuận thu được.
    • B3.3: Sau khi tấn công thành công tôi rút tiền lợi nhuận này về qua giao dịch: 0x0026ab7a86d8691123dc244adeb8121f0dad5b85b60a27e813c24ba0acd25392

Để có thể viết một contract bảo mật, các bạn phải biết cách hacker tấn công như thế nào, từ đó bạn mới biết cách code để phòng ngừa lỗi này.

Bài viết này có hữu ích với bạn?

Kích vào một biểu tượng ngôi sao để đánh giá bài viết!

Xếp hạng trung bình 5 / 5. Số phiếu: 68

Bài viết chưa có đánh giá! Hãy là người đầu tiên đánh giá bài viết này.

Trả lời

Giao diện bởi Anders Norén