-
Notifications
You must be signed in to change notification settings - Fork 288
/
ApproveScam.sol
124 lines (101 loc) · 3.87 KB
/
ApproveScam.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "forge-std/Test.sol";
/*
Name: Over-Permissive Approve Scam
Description:
This vulnerability is associated with the approval process in ERC20 tokens.
In this scenario, Alice approves Eve to transfer an unlimited (type(uint256).max) amount of tokens
from Alice's account. Later, Eve exploits this permission and transfers 1000 tokens from Alice's account to hers.
Most current scams use approve or setApprovalForAll to defraud your transfer rights. Be especially careful with this part.
Mitigation:
Users should only approve the amount of tokens necessary for the operation at hand.
*/
contract ContractTest is Test {
ERC20 ERC20Contract;
address alice = vm.addr(1);
address eve = vm.addr(2);
function testApproveScam() public {
ERC20Contract = new ERC20();
ERC20Contract.mint(1000);
ERC20Contract.transfer(address(alice), 1000);
vm.prank(alice);
// Be Careful to grant unlimited amount to unknown website/address.
// Do not perform approve, if you are sure it's from a legitimate website.
// Alice granted approval permission to Eve.
ERC20Contract.approve(address(eve), type(uint256).max);
console.log(
"Before exploiting, Balance of Eve:",
ERC20Contract.balanceOf(eve)
);
console.log(
"Due to Alice granted transfer permission to Eve, now Eve can move funds from Alice"
);
vm.prank(eve);
// Now, Eve can move funds from Alice.
ERC20Contract.transferFrom(address(alice), address(eve), 1000);
console.log(
"After exploiting, Balance of Eve:",
ERC20Contract.balanceOf(eve)
);
console.log("Exploit completed");
}
receive() external payable {}
}
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(
address owner,
address spender
) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
contract ERC20 is IERC20 {
uint public totalSupply;
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;
string public name = "Test example";
string public symbol = "Test";
uint8 public decimals = 18;
function transfer(address recipient, uint amount) external returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}
function approve(address spender, uint amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool) {
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
}
function mint(uint amount) external {
balanceOf[msg.sender] += amount;
totalSupply += amount;
emit Transfer(address(0), msg.sender, amount);
}
function burn(uint amount) external {
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Transfer(msg.sender, address(0), amount);
}
}