Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[workspace]
resolver = "2"
members = ["attack", "ctf"]

[workspace.package]
Expand All @@ -7,9 +8,9 @@ exclude = [".github/", "**/tests/", "**/contracts/", "**/cache/", "**/out/"]

[workspace.dependencies]
eyre = "0.6"
ethers = "2.0.8"
rand = "0.8.5"
serde = "1.0.164"
serde_json = "1.0.99"
async-trait = "0.1.68"
tokio = { version = "1.19", features = ["macros", "rt-multi-thread", "time"] }
ethers = { version = "2", default-features = false, features = ["rustls"] }
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
1. bind-attack
2. cargo test -p attack -- --nocapture
3. deploy-levels (перед этим анвил остановить, который крутит блокчейн) //нужно, если set up изменена
ps axu | grep anvil
anvil --steps-tracing --state state.json

4. cargo build (если удалить папку target) //не трогать

cast disassemble /ввести байткод/

Comment thread
levsgit marked this conversation as resolved.
# 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 +108,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 All @@ -122,7 +132,7 @@ impl ctf::Exploit for Exploit {

If you then run `cargo test -p attack -- --nocapture` you should see something like this

``` text
```text
$ cargo test -p attack -- --nocapture
Compiling attack v0.1.0 (/home/gleb/code/0xgleb/data-cartel/dex-offender/attack)
Finished test [unoptimized + debuginfo] target(s) in 6.83s
Expand Down Expand Up @@ -161,7 +171,7 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini

If you're using the dev container then you already have a local blockchain running in one of the VS code terminals. If not, you can start it by running `anvil`. I would recommend turning on tracing to make debugging easier.

``` sh
```sh
anvil --steps-tracing --load-state state.json
```

Expand Down Expand Up @@ -310,7 +320,7 @@ $ cast rpc trace_transaction 0x6d2ec4f84ff1308695afd6a0ef130af5bde3f26eefc112b22

This is hard to read. Let's pipe it thorugh `jq`.

``` sh
```sh
$ cast rpc trace_transaction 0x6d2ec4f84ff1308695afd6a0ef130af5bde3f26eefc112b22d39840502528635 | jq .
[
{
Expand Down
12 changes: 6 additions & 6 deletions ctf/contracts/ethernaut/lvl12/Privacy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ pragma solidity ^0.8.0;
* Advanced gameplay could involve using remix, or your own web3 provider.
*/
contract Privacy {
bool public locked = true;
uint256 public ID = block.timestamp;
uint8 private flattening = 10;
uint8 private denomination = 255;
uint16 private awkwardness = uint16(block.timestamp);
bytes32[3] private data;
bool public locked = true; // 1 byte | slot 0
uint256 public ID = block.timestamp; // 32 bytes | slot 1
uint8 private flattening = 10; // 1 byte | slot 2
uint8 private denomination = 255; // 1 byte | slot 2
uint16 private awkwardness = uint16(block.timestamp); // 2 bytes | slot 2
bytes32[3] private data; //32 * 3 = 96 bytes | slots 3, 4, 5, 6

Comment thread
levsgit marked this conversation as resolved.
constructor(bytes32[3] memory _data) {
data = _data;
Expand Down
14 changes: 14 additions & 0 deletions ctf/contracts/ethernaut/lvl14/GatekeeperTwo.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
This gatekeeper introduces a few new challenges.
Register as an entrant to pass this level.

Things that might help:
- Remember what you've learned from getting past
the first gatekeeper - the first gate is the same.
- The assembly keyword in the second gate allows a contract to access
functionality that is not native to vanilla Solidity.
See here for more information. The extcodesize call in this gate will get the size of a contract's code at a given address - you can learn more about how and when this is set in section 7 of the yellow paper.
- The ^ character in the third gate is a bitwise operation (XOR),
and is used here to apply another common bitwise operation (see here).
The Coin Flip level is also a good place to start when approaching this challenge.
*/
Comment thread
levsgit marked this conversation as resolved.

contract GatekeeperTwo {
address public entrant;
Expand Down
12 changes: 12 additions & 0 deletions ctf/contracts/ethernaut/lvl15/NaughtCoin.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
NaughtCoin is an ERC20 token and you're already holding all of them.
The catch is that you'll only be able to transfer
them after a 10 year lockout period. Can you figure out how to get them out
to another address so that you can transfer them freely?
Complete this level by getting your token balance to 0.

Things that might help

> The ERC20 Spec
> The OpenZeppelin codebase
*/
Comment thread
levsgit marked this conversation as resolved.

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

Expand Down
16 changes: 16 additions & 0 deletions ctf/contracts/ethernaut/lvl16/Preservation.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
This contract utilizes a library to store two different times
for two different timezones. The constructor creates two
instances of the library for each time to be stored.

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.
> Understanding what it means for delegatecall to be context-preserving.
> Understanding how storage variables are stored and accessed.
> Understanding how casting works between different data types.
*/

contract Preservation {
// public library contracts
Expand Down
11 changes: 11 additions & 0 deletions ctf/contracts/ethernaut/lvl17/Recovery.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title Ethernaut Level 17: Recovery
*
* A contract creator has built a very simple token factory contract.
* Anyone can create new tokens with ease. After deploying the first
* token contract, the creator sent `0.001` ether to obtain more tokens.
* They have since lost the contract address.
*
* This level will be completed if you can recover (or remove) the `0.001`
* ether from the lost contract address.
*/
contract Recovery {
//generate tokens
function generateToken(string memory _name, uint256 _initialSupply) public {
Expand Down
10 changes: 10 additions & 0 deletions ctf/contracts/ethernaut/lvl17/RecoverySolution.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract RecoverySolution {
function solution(address creator) public pure returns (address) {
address token =
address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), creator, bytes1(0x01))))));
return token;
}
}
6 changes: 6 additions & 0 deletions ctf/contracts/ethernaut/lvl18/Interface.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface MeaningOfLife {
function whatIsTheMeaningOfLife() external returns (uint256);
}
16 changes: 16 additions & 0 deletions ctf/contracts/ethernaut/lvl18/MagicNumber.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/*
To solve this level, you only need to provide the Ethernaut with a Solver,
a contract that responds to whatIsTheMeaningOfLife() with the right number.

Easy right? Well... there's a catch.

The solver's code needs to be really tiny. Really reaaaaaallly tiny.
Like freakin' really really itty-bitty tiny: 10 opcodes at most.

Hint: Perhaps its time to leave the comfort of the Solidity compiler momentarily,
and build this one by hand O_o. That's right: Raw EVM bytecode.

Good luck!

*/

contract MagicNum {
address public solver;

Expand Down
10 changes: 10 additions & 0 deletions ctf/contracts/ethernaut/lvl19/AlienCodex.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.5.0;

/*
You've uncovered an Alien contract. Claim ownership to complete the level.

Things that might help

> Understanding how array storage works
> Understanding ABI specifications
> Using a very underhanded approach
*/

import "../helpers/Ownable-05.sol";

contract AlienCodex is Ownable {
Expand Down
10 changes: 10 additions & 0 deletions ctf/contracts/ethernaut/lvl20/Denial.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title Ethernaut Level 20: Denial
*
* This is a simple wallet that drips funds over time. You can withdraw the
* funds slowly by becoming a withdrawing partner.
*
* If you can deny the owner from withdrawing funds when they call withdraw()
* (whilst the contract still has funds, and the transaction is of 1M gas or less)
* you will win this level.
*/
contract Denial {
address public partner; // withdrawal partner - pay the gas, split the withdraw
address public constant owner = address(0xA9E);
Expand Down
7 changes: 7 additions & 0 deletions ctf/contracts/ethernaut/lvl21/Shop.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
Сan you get the item from the shop for less than the price asked?

Things that might help:
> Shop expects to be used from a Buyer
> Understanding restrictions of view functions
*/

interface Buyer {
function price() external view returns (uint256);
Expand Down
35 changes: 35 additions & 0 deletions ctf/contracts/ethernaut/lvl22/Dex.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/*
The goal of this level is for you to hack the basic DEX contract below
and steal the funds by price manipulation.

You will start with 10 tokens of token1 and 10 of token2.
The DEX contract starts with 100 of each token.

You will be successful in this level if you manage
to drain all of at least 1 of the 2 tokens from the contract,
and allow the contract to report a "bad" price of the assets.


Quick note
Normally, when you make a swap with an ERC20 token,
you have to approve the contract to spend your tokens for you.
To keep with the syntax of the game, we've just added the approve method
to the contract itself.

So feel free to use contract.approve(contract.address, <uint amount>)
instead of calling the tokens directly, and it will automatically approve
spending the two tokens by the desired amount.
Feel free to ignore the SwappableToken contract otherwise.

Things that might help:

> How is the price of the token calculated?
> How does the swap method work?
> How do you approve a transaction of an ERC20?
> Theres more than one way to interact with a contract!
> Remix might help
> What does "At Address" do?

*/

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
Expand Down Expand Up @@ -53,6 +87,7 @@ contract SwappableToken is ERC20 {
_dex = dexInstance;
}

// In Rust bindings it's called `approve_with_owner_and_spender`
function approve(address owner, address spender, uint256 amount) public {
require(owner != _dex, "InvalidApprover");
super._approve(owner, spender, amount);
Expand Down
15 changes: 15 additions & 0 deletions ctf/contracts/ethernaut/lvl23/DexTwo.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/*
This level will ask you to break DexTwo,
a subtlely modified Dex contract from the previous level, in a different way.

You need to drain all balances of token1 and token2 from the DexTwo
contract to succeed in this level.

You will still start with 10 tokens of token1 and 10 of token2.
The DEX contract still starts with 100 of each token.

Things that might help:

> How has the swap method been modified?
*/

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
Expand Down
Loading