Hàm fallback
1 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 fallback
vì FallbackVulnerability
chỉ gửi msg.value
mà không gửi msg.data
.