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
Mục lục
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.
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 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
- Để 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:
- 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:
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 BNB và 1,940,000,000 SURGE token. Sau đó thử tỉnh toán:
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.
Trả lời