Hackchain / hackchain

Continuous bitcoin-inspired capture-the-flag challenge. (Alpha)
https://api.hackcha.in/help
56 stars 9 forks source link

Hackchain

NPM version

Alpha version

Continuous bitcoin-inspired capture-the-flag competition.

Preamble

A hackchain server was running in a cloud. Some hacker has installed the client for it:

$ npm install -g hackchain

Hacker has also requested a server info:

$ hc-client --info
{ lastBlock: '4e9c8600ca954bbfd88a1ca8715e5cf5a751a8e5945802912086e632bec85e1f',
  nextBlockIn: 17,
  nextCoinbaseIn: 85817,
  'proof-of-work-complexity': 17 }

$ hc-client --leaderboard
===== Current top transactions =====
NOTE: It is important to use both hash and index to steal a transaction
[ { hash: '61d9faa5dc429c8eb4f00835f285b3a5f7022f8b557432a7af542d0b778bc14e',
    index: 4,
    value: '1500000000' },
    ... ]

"I wonder if I can capture some of these coins...", she thought.

Description for Hackers

The server is running bitcoin-like blockchain with the main difference that the blocks are issued automatically every 5 minutes. One may request the latest block hash either by using --info argument of the hc-client, or by running:

curl https://api.hackcha.in/

The whole point of this "continuous CTF competition" is to capture someone else's coins and protect them. It is very close to CTF competitions, but participants compete with each other instead of attacking some predefined software.

New coins are minted only once every 24 hours, so hackers are encouraged to steal coins from each other. Current leaderboard (list of unspent coins) can be found by using --leaderboard argument of hc-client, or by running:

curl https://api.hackcha.in/leaderboard

Being very similar in structure to bitcoin blockchain, hackchain provides an opportunity to learn about bitcoin internals, and most importantly have some fun!

While recommended to read in order, one may skip all sections except Capturing, where the process of capturing (stealing) coins is described in detail.

Community

Feel free to join #hackchain IRC channel on freenode server to discuss things with other hackers.

Block

Blocks have a link to the parent block (or a genesis block with the hash 0000000...., all zeroes), and a list of at least one transaction (TX). First TX in a block is called a coinbase and (in contrast to bitcoin) can be spent by anyone, unless its output value is 0.

Block may be inspected using --block <hash> argument of the hc-client:

<Block: b1e6483...
   parent: 6017999...
   txs: [ <Coinbase: 735c742...
      v=1
      inputs: [ <Input hash: 6017999...
          index: -1
          script: <Script len: 0
          opcodes: [
    ]>> ]
      outputs: [ <Output value: 2500000000
          script: <Script len: 2
          opcodes: [
    irq success]>> ]> ]>

Raw hex data may be fetched by running:

curl https://api.hackcha.in/v1/block/<hash>

Spec for the binary encodings of all structures is available below.

TX

Every transaction has at least one input and output.

Each input has a hash of input TX, index of output in that TX, and script to capture it. (See "Capturing" section below).

Each output has a value number of coins, and script to prevent capturing these coins.

TX cannot spend more coins than it gets from the inputs, but it can spend less. The difference is called fee and is added to the coinbase of the block where TX is stored.

TX may be inspected using --tx <hash> argument of the hc-client:

<TX: 10319ac...
    v=1
    inputs: [ <Input hash: 699eb0e...
        index: 0
        script: <Script len: 8
        opcodes: [
  lui r1 64
  addi r1 r1 0
  sw r0 r1 8
  irq success]>> ]
    outputs: [ <Output value: 2500000000
        script: <Script len: 4
        opcodes: [
  irq yield
  irq success]>> ]>

Raw hex data may be inspected by running:

curl https://api.hackcha.in/v1/tx/<hash>

Capturing

In order to capture someone's coin (or coinbase), the attacker must implement an input script that will be able to defeat the output script of the TX.

Scripts are written is RiSC-16 (Ridiculously Simple Computer) instruction set and are running in a shared memory space of 0x10000 16-bit words. Yes, you read it right, the code is living in the same space, and the scripts are allowed to modify each other.

The process:

  1. Spending TX (the one that you sent) hash is loaded to 0x0 offset of the memory
  2. output script is loaded to 0x1000 offset of the memory and executed until irq yield/irq success, or until it executes more than 16 * 1024 opcodes (if so - coin is captured, and further steps are skipped)
  3. If irq success was executed in step 2 - coin is captured and the process ends. If irc yield was executed - proceed to step 4
  4. input script is loaded to 0x2000 offset of the memory
  5. One opcode of output is executed
  6. If any irq ... was executed - the process ends with captured coin
  7. One opcode of input is executed
  8. If any irq ... was executed - steps 7-8 are replaced by no op
  9. Memory stores from steps 6 and 8 are applied to the shared memory
  10. If number of opcodes executed in output after step 4 exceeds 32 * 1024: process terminates, and coin is not captured

Scripts

Quoting RiSC-16, there are 8 different 16-bit registers (r0, ..., r7), and 10 different instructions:

They are encoded just as they are described in a paper.

Additional opcodes are added in hackchain:

NOTE: Reading value of r0 always returns 0 for your convenience!

Additional opcode-combos are available using the assembler in this repo:

Examples:

Spending TX with hc-client

asciicast

It is possible to generate and send TX using hc-client. In order to do this, a yaml-formatted tx-name.tx file must be created:

version: 1
inputs:
  - hash: 'a1dd41f1efd2498dd8126684fe164d22d6188046b1c71a7d5325c2158965237b'
    index: 0
    script:
      - irq success
outputs:
  - value: '2500000000'
    script:
      - irq yield
      - irq success

(See examples/ for more various TX examples)

NOTE: script arrays have string values. , or separators may be used between opcode and arguments, and between arguments. Arguments are either:

Afterwards, one may execute:

$ hc-client --spend tx-name.tx

It will parse file, generate TX, confirm it, and send it to server. If the server will accept the TX, the confirmation will be printed. If the server will reject the TX, a (hopefully) meaningful error message will be printed.

NOTE: While TXs are accepted immediately, they are not available for spending until the server will mint a new block. Please check output of hc-client --info to get the time until the next block.

Debugger

asciicast

While some scripts may be easy to follow, others may definitely require more detailed investigation. This is where internal debugger may come out handy:

$ hc-debug --tx examples/tx/debug.tx

Or alternatively, if you'd like to debug output script that it is not in the chain yet:

$ hc-debug examples/debugger/prog1.yaml

NOTE: yaml file with the contents of both scripts and TX hash, must be supplied to debugger. See debugger examples

Binary Format

All values are in Big Endian.

Block

[ 32-bit number ] version
[ 32 bytes      ] parent sha256 hash
[ 32-bit number ] TX count
...               TXs

TX

[ 32-bit number ] version
[ 32-bit number ] input count
[ 32-bit number ] output count
...               inputs
...               outputs

Input

[ 32 bytes      ] sha256 hash of input TX
[ 32-bit number ] input index
...               script

Output

[ 8 bytes       ] big endian big number
...               script

Script

[ 32-bit number ] size of binary data below
...               binary opcodes for RiSC-16

Additional Server endpoints

$ curl https://api.hackcha.in/help | jq .
{
  "/": "information about server and last block",
  "/help": "this message",
  "/leaderboard": "list of currently unspent transactions",
  "/v1/block/(hash)": "GET block data",
  "/v1/tx/(hash)": "GET/POST transaction data",
  "/v1/tx/(hash)/block": "GET the hash of transaction's block",
  "/v1/tx/(hash)/(output index)/spentby": "GET the hash of spending tx"
}

Bugs

If any bugs, please file an issue. We will make sure to figure it out!

LICENSE

This software is licensed under the MIT License.

Copyright Fedor Indutny, 2016.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.