Skip to content

Commit

Permalink
chore: merge PR #37 from BlockheaderWeb3-Community/security
Browse files Browse the repository at this point in the history
feat: add vulnerable contracts
  • Loading branch information
sprtd authored Oct 21, 2024
2 parents ef71102 + 27f9c25 commit 75910e5
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target
.snfoundry.toml
.snfoundry.toml
.snfoundry_cache
48 changes: 48 additions & 0 deletions src/attack_counter.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// AttackCounter Contract
// Whereas a function is considered as a read-only function if its self is a snapshot of storage as depicted thus (self: @TContractState), it is not true that such a functin cannot modify state
// By leveraging syscalls like `call_contract_syscall`, such function can modify state
// In this contract, we showcase this possibility of using `call_contract_syscall` in our AttackCounter contract to modify the `count` state of our simple counter contract

#[starknet::interface]
pub trait IAttackCounter<TContractState> {
// get count - retrieve the count from storage
// a read-only function
fn counter_count(self: @TContractState) -> u32;

// set count
fn attack_count(self: @TContractState);
}


#[starknet::contract]
pub mod AttackCounter {
use crate::counter::{ ICounterDispatcher, ICounterDispatcherTrait};
use starknet::{ContractAddress, syscalls:: call_contract_syscall};
#[storage]
struct Storage {
counter_address: ContractAddress
}

#[constructor]
fn constructor(ref self: ContractState, counter_addr: ContractAddress) {
self.counter_address.write(counter_addr)
}

#[abi(embed_v0)]
impl CounterImpl of super::IAttackCounter<ContractState> {
fn counter_count(self: @ContractState) -> u32 {
let counter_addr = self.counter_address.read();
ICounterDispatcher { contract_address: counter_addr }.get_count()
}

fn attack_count(self: @ContractState) {
let counter_addr = self.counter_address.read();
let selector = selector!("set_count");

let mut args: Array<felt252> = array![];
let value = 100;
value.serialize(ref args);
call_contract_syscall(counter_addr, selector, args.span());
}
}
}
3 changes: 3 additions & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ pub mod ownable_counter;
pub mod ownable;
pub mod addition;
pub mod aggregator;
pub mod vulnerable_token;
pub mod vulnerable_stake;
pub mod attack_counter;

43 changes: 43 additions & 0 deletions src/vulnerable_stake.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// VulnerableStake contract
// Both deposit and withdraw functions of this contract contains flawed logic
use starknet::ContractAddress;
#[starknet::interface]
pub trait IVulnerableStake<T> {
fn deposit(ref self: T, amount: u256, stake_addr: ContractAddress);
fn withdraw(ref self: T, amount: u256, reward_addr: ContractAddress);
}


#[derive(Debug, Drop, Serde, Copy, starknet::Store)]
pub struct StakerInfo {
pub stake_addr: ContractAddress,
pub reward_amount: u256,
pub stake_amount: u256,
pub unclaimed_rewards_own: u256,
}


#[starknet::contract]
mod VulnerableStake {
use starknet::{ContractAddress, get_caller_address};
use starknet::storage::{StoragePointerWriteAccess, StoragePathEntry, Map};
use super::{StakerInfo, IVulnerableStake,};
#[storage]
struct Storage {
staker_info: Map<ContractAddress, StakerInfo>,
}

#[abi(embed_v0)]
impl StakeImpl of IVulnerableStake<ContractState> {
fn deposit(ref self: ContractState, amount: u256, stake_addr: ContractAddress) {
let staker_info = StakerInfo {
stake_addr, reward_amount: 0, stake_amount: amount, unclaimed_rewards_own: 0
};
let caller = get_caller_address();
self.staker_info.entry(caller).write(staker_info);
}


fn withdraw(ref self: ContractState, amount: u256, reward_addr: ContractAddress) {}
}
}
36 changes: 36 additions & 0 deletions src/vulnerable_token.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// NB
// this is not an ERC-20 token contract as it doesn't implement the ERC-20 standard
// This minimalistic VulnerableToken contract is a token contract whose `mint_token` method lacks the requisite access control to for the execution of critical operations like minting/burning of tokens; in our case here, we are targetting the increase total supply operation of the contract.
// It is being used here to showcase the security vulnerability of exposing critical functions without access control

#[starknet::interface]
pub trait IVulnerableToken<T> {
fn mint_token(ref self: T);
fn get_token_supply(self: @T) -> u256;
}

#[starknet::contract]
mod VulnerableToken {
const MINT_AMOUNT: u256 = 1000;

#[storage]
struct Storage {
total_supply: u256
}


#[abi(embed_v0)]
impl VulnerableTokenImpl of super::IVulnerableToken<ContractState> {
// increase token total supply by MINT_AMOUNT
fn mint_token(ref self: ContractState) {
let current_supply: u256 = self.total_supply.read();
self.total_supply.write(current_supply + MINT_AMOUNT)
}

// get total supply of token
fn get_token_supply(self: @ContractState) -> u256 {
self.total_supply.read()
}
}
}

0 comments on commit 75910e5

Please sign in to comment.