Giới thiệu về chuỗi bài
Damn Vulnerable Defi là chuỗi bài tập về tấn công vào các lỗ hổng của các ứng dụng Defi, được viết bởi @tinchoabbate, trước đây từng làm việc cho Open Zeppelin, và giờ đang là một security reseacher.
Khác với The Ethernaut là tương tác trực tiếp trên browser và metamask, chuỗi bài này tái hiện lại lỗi bằng cách deploy các contract ở local, ta cần viết test với một account là attacker
để pass qua được các test case. Cách này sẽ tốn nhiều công sức hơn so với The Ethernaut, nhưng ta sẽ học hỏi được nhiều hơn về cách deploy, test, tương tác với contract cũng như các thư viện liên quan.
Các công cụ:
Challenge #1 - Unstoppable
Nhiệm vụ: Có một lending pool với hàng triệu DVT token trong đó, cho phép thực hiện flashloan mà không mất phí. Bằng cách nào đó hãy làm cho pool không còn flashloan được nữa. Ta bắt đầu với 100 DVT token.
Phân tích
Nếu chưa hiểu về flash loan thì bạn đọc có thể tham khảo thêm ở bài viết này của mình: Flash Loan - vay nóng không thế chấp - the good points
Luồng thực thi flashloan trong bài này khá đơn giản:
- Tại
ReceiverUnstoppable
gọi hàmexecuteFlashloan
- Khi này pool
UnstoppableLender
sẽ chạy hàmflashLoan
, gửi choReceiverUnstoppable
lượng vay và thực hiện hàmreceiveTokens
trongReceiverUnstoppable
, mục đích để trả lại token cho pool - Để đảm bảo flash loan thành công, ta cần đảm bảo balance của pool sau khi thực hiện không hề bị giảm sút bằng điều kiện check
require(balanceAfter >= balanceBefore, "Flash loan hasn't been paid back");
Trong flashLoan
của pool có một điều kiện check rất “lỗi”:
assert(poolBalance == balanceBefore);
vì poolBalance
là một biến uint
được gán chỉ khi gọi hàm depositTokens
, còn balanceBefore
luôn phản ánh balance thật của pool; nên nếu ta chuyển thêm token vào cho contract mà không thông qua gọi hàm depositTokens
thì sẽ khiến thay đổi balance thật của pool, trong khi biến poolBalance
không thay đổi, dẫn đến điều kiện check kia sai.
Exploit
Ta đang có 100 DVT token, đơn giản chuyển cho pool một ít là được
it("Exploit", async function () {
/** CODE YOUR EXPLOIT HERE */
await this.token.connect(attacker).transfer(this.pool.address, 1);
});
Check lại kết quả:
[Challenge] Unstoppable
✓ Exploit
1 passing (2s)
Đây là bài đầu tiên, logic cũng đơn giản, lời giải cũng vô cùng ngắn gọn! Hãy sẵn sàng để đón chờ các thử thách tiếp theo.