Grand Theft Read.Cash

9 185
Avatar for BigBlockIfTrue
4 years ago

Earlier today, the people behind Read.Cash drafted a proposal for a smart contract allowing password-based wallet recovery. I think this has both positive and negative aspects. I like the general idea of ...

The tools

  • Bitcoin ABC (required, blockchain history not needed)

  • text editor (required)

  • Electron Cash (optional)

  • Mission Impossible theme music (optional)

The plan

Step 1. Use Electron Cash to generate a receiving address for the booty. Upon success, Electron Cash will automatically initiate CashShuffle for the perfect getaway. I generated bitcoincash:qpq9y25eejhx7pdk53eul93r563v68jz4s4stdkf5j. Alternatively, use the wallet within Bitcoin ABC, but then the treasure remains traceable.

Step 2. Bitcoin ABC comes with an optional command-line utility bitcoin-tx to construct transactions. You can find it in the bin directory. Summon its manual:

$ bitcoin-tx -?
Bitcoin ABC bitcoin-tx utility version v0.20.6-unk

Usage:  bitcoin-tx [options] <hex-tx> [commands]  Update hex-encoded bitcoin transaction
or:     bitcoin-tx [options] -create [commands]   Create hex-encoded bitcoin transaction

Options:

  -?
       This help message

  -create
       Create new, empty TX.

  -json
       Select JSON output

  -txid
       Output only the hex-encoded transaction id of the resultant transaction.

Chain selection options:

  -testnet
       Use the test chain

Commands:

  delin=N
       Delete input N from TX

  delout=N
       Delete output N from TX

  in=TXID:VOUT(:SEQUENCE_NUMBER)
       Add input to TX

  locktime=N
       Set TX lock time to N

  nversion=N
       Set TX version to N

  outaddr=VALUE:ADDRESS
       Add address-based output to TX

  outdata=[VALUE:]DATA
       Add data-based output to TX

  outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
       Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS.
       Optionally add the "S" flag to wrap the output in a
       pay-to-script-hash.

  outpubkey=VALUE:PUBKEY[:FLAGS]
       Add pay-to-pubkey output to TX. Optionally add the "S" flag to wrap the
       output in a pay-to-script-hash.

  outscript=VALUE:SCRIPT[:FLAGS]
       Add raw script output to TX. Optionally add the "S" flag to wrap the
       output in a pay-to-script-hash.

  sign=SIGHASH-FLAGS
       Add zero or more signatures to transaction. This command requires JSON
       registers:prevtxs=JSON object, privatekeys=JSON object. See
       signrawtransactionwithkey docs for format of sighash flags, JSON
       objects.

Register Commands:

  load=NAME:FILENAME
       Load JSON file FILENAME into register NAME

  set=NAME:JSON-STRING
       Set register NAME to given JSON-STRING

Step 3. Figure out what coin you want to steal. In this case, the mission is output 1 (count from zero) of transaction b48e260f36142340cc1652bbc2da5028303e21f469d169393d3b08f234165dfc, worth 0.02290635 BCH.

Step 4. Create the transaction, ignoring the amount of the output for a moment:

$ bitcoin-tx -json -create in=b48e260f36142340cc1652bbc2da5028303e21f469d169393d3b08f234165dfc:1 outaddr=0:qpq9y25eejhx7pdk53eul93r563v68jz4s4stdkf5j
{
    "txid": "d0dc399a668008fa6a0bebc0680ba16d3ca33187fa0ea37adf7a5a367f330dbd",
    "hash": "d0dc399a668008fa6a0bebc0680ba16d3ca33187fa0ea37adf7a5a367f330dbd",
    "version": 2,
    "size": 85,
    "locktime": 0,
    "vin": [
        {
            "txid": "b48e260f36142340cc1652bbc2da5028303e21f469d169393d3b08f234165dfc",
            "vout": 1,
            "scriptSig": {
                "asm": "",
                "hex": ""
            },
            "sequence": 4294967295
        }
    ],
    "vout": [
        {
            "value": 0.00000000,
            "n": 0,
            "scriptPubKey": {
                "asm": "OP_DUP OP_HASH160 40522a99ccae6f05b6a473cf9623a6a2cd1e42ac OP_EQUALVERIFY OP_CHECKSIG",
                "hex": "76a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac",
                "reqSigs": 1,
                "type": "pubkeyhash",
                "addresses": [
                    "16s6gJN1t7yPZGfzUFvMo9xkFvN7MzjkHH"
                ]
            }
        }
    ],
    "hex": "0200000001fc5d1634f2083b3d3969d169f4213e302850dac2bb5216cc402314360f268eb40100000000ffffffff0100000000000000001976a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac00000000"
}

Step 5. You see the scriptSig field of the input, the signature script that authorises spending the input, is still missing. Since the coin we're stealing is a Pay-to-Script-Hash (P2SH) coin, the signature script consists of the unhashed P2SH script (b251f93c45baea5c72bae18311c75eccad404291 OP_OVER 'read.cash' OP_EQUAL OP_NIP OP_NIP), preceded by all other input data to this P2SH script itself (again the password 'read.cash'):

             Password length (9) push operation: 09
                                       Password: 726561642e63617368
Unhashed P2SH script length (35) push operation: 23
                           Unhashed P2SH script: 14b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777

Full signature script:

09726561642e636173682314b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777

Step 6. Our signature script is 46 bytes long, or 47 bytes including a 1-byte length indicator prefix (2e). This must replace the 0-byte signature script with 1-byte length indicator (00) in our transaction template. The transaction will therefore grow from 85 bytes to 131 bytes. With the minimum fee rate of 1 sat/b, we thus need to attach a fee of 0.00000131 BCH, so we can claim 0.02290504 BCH for ourselves. Generate a new transaction template accordingly:

$ bitcoin-tx -json -create in=b48e260f36142340cc1652bbc2da5028303e21f469d169393d3b08f234165dfc:1 outaddr=0.02290504:qpq9y25eejhx7pdk53eul93r563v68jz4s4stdkf5j
{
    "txid": "0229cef50d62eff07dd7133ce9db29364c46866b0b46f8437f58bf3e2012318e",
    "hash": "0229cef50d62eff07dd7133ce9db29364c46866b0b46f8437f58bf3e2012318e",
    "version": 2,
    "size": 85,
    "locktime": 0,
    "vin": [
        {
            "txid": "b48e260f36142340cc1652bbc2da5028303e21f469d169393d3b08f234165dfc",
            "vout": 1,
            "scriptSig": {
                "asm": "",
                "hex": ""
            },
            "sequence": 4294967295
        }
    ],
    "vout": [
        {
            "value": 0.02290504,
            "n": 0,
            "scriptPubKey": {
                "asm": "OP_DUP OP_HASH160 40522a99ccae6f05b6a473cf9623a6a2cd1e42ac OP_EQUALVERIFY OP_CHECKSIG",
                "hex": "76a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac",
                "reqSigs": 1,
                "type": "pubkeyhash",
                "addresses": [
                    "16s6gJN1t7yPZGfzUFvMo9xkFvN7MzjkHH"
                ]
            }
        }
    ],
    "hex": "0200000001fc5d1634f2083b3d3969d169f4213e302850dac2bb5216cc402314360f268eb40100000000ffffffff0148f32200000000001976a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac00000000"
}

Step 7. To complete the transaction, the signature script must be inserted into the transaction template manually. To do so, copy and paste the hexadecimal representation of the transaction into a text editor. Use the transaction format documentation to decode it and find the right place for inserting the signature:

                    Version number (2): 02000000
        Number of inputs (1) as varint: 01
                    Parent transaction: fc5d1634f2083b3d3969d169f4213e302850dac2bb5216cc402314360f268eb4
   Parent transaction output index (1): 01000000
 Signature script length (0) as varint: 00
              Remainder of transaction: ffffffff0148f32200000000001976a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac00000000

Step 8. Replace the zero-length signature script with the actual signature script constructed earlier:

                    Version number (2): 02000000
        Number of inputs (1) as varint: 01
                    Parent transaction: fc5d1634f2083b3d3969d169f4213e302850dac2bb5216cc402314360f268eb4
   Parent transaction output index (1): 01000000
Signature script length (46) as varint: 2e
                      Signature script: 09726561642e636173682314b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777
              Remainder of transaction: ffffffff0148f32200000000001976a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac00000000

Full transaction: 0200000001fc5d1634f2083b3d3969d169f4213e302850dac2bb5216cc402314360f268eb4010000002e09726561642e636173682314b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777ffffffff0148f32200000000001976a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac00000000

Step 9. Verify that the resulting transaction looks alright:

$ bitcoin-tx -json 0200000001fc5d1634f2083b3d3969d169f4213e302850dac2bb5216cc402314360f268eb4010000002e09726561642e636173682314b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777ffffffff0148f32200000000001976a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac00000000
{
    "txid": "b73ad30dcba9ed29512ae2c8ea23d6c6bfd8786042e1347938b96e1b296b1f2c",
    "hash": "b73ad30dcba9ed29512ae2c8ea23d6c6bfd8786042e1347938b96e1b296b1f2c",
    "version": 2,
    "size": 131,
    "locktime": 0,
    "vin": [
        {
            "txid": "b48e260f36142340cc1652bbc2da5028303e21f469d169393d3b08f234165dfc",
            "vout": 1,
            "scriptSig": {
                "asm": "726561642e63617368 14b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777",
                "hex": "09726561642e636173682314b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777"
            },
            "sequence": 4294967295
        }
    ],
    "vout": [
        {
            "value": 0.02290504,
            "n": 0,
            "scriptPubKey": {
                "asm": "OP_DUP OP_HASH160 40522a99ccae6f05b6a473cf9623a6a2cd1e42ac OP_EQUALVERIFY OP_CHECKSIG",
                "hex": "76a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac",
                "reqSigs": 1,
                "type": "pubkeyhash",
                "addresses": [
                    "16s6gJN1t7yPZGfzUFvMo9xkFvN7MzjkHH"
                ]
            }
        }
    ],
    "hex": "0200000001fc5d1634f2083b3d3969d169f4213e302850dac2bb5216cc402314360f268eb4010000002e09726561642e636173682314b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777ffffffff0148f32200000000001976a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac00000000"
}

Step 10. Start the Bitcoin ABC node. Open the debug window via the help menu and go to the console tab. Read the scammer warning and laugh evilly, because this time you are the criminal. Then, summon the command list:

$ help

== Blockchain ==
(...)

== Control ==
(...)

== Generating ==
(...)

== Mining ==
(...)

== Network ==
(...)

== Rawtransactions ==
combinepsbt ["psbt",...]
combinerawtransaction ["hexstring",...]
converttopsbt "hexstring" ( permitsigdata )
createpsbt [{"txid":"id","vout":n},...] [{"address":amount},{"data":"hex"},...] ( locktime )
createrawtransaction [{"txid":"id","vout":n},...] [{"address":amount},{"data":"hex"},...] ( locktime )
decodepsbt "psbt"
decoderawtransaction "hexstring"
decodescript "hexstring"
finalizepsbt "psbt" ( extract )
fundrawtransaction "hexstring" ( options )
getrawtransaction "txid" ( verbose "blockhash" )
sendrawtransaction "hexstring" ( allowhighfees )
signrawtransactionwithkey "hexstring" ["privatekey1",...] ( [{"txid":"id","vout":n,"scriptPubKey":"hex","redeemScript":"hex"},...] sighashtype )
testmempoolaccept ["rawtxs"] ( allowhighfees )

== Util ==
(...)

== Wallet ==
(...)

Step 11. Find the transaction broadcast command sendrawtransaction and look up how it works:

$ help sendrawtransaction

sendrawtransaction "hexstring" ( allowhighfees )

Submits raw transaction (serialized, hex-encoded) to local node and network.

Also see createrawtransaction and signrawtransactionwithkey calls.

Arguments:
1. "hexstring"    (string, required) The hex string of the raw transaction)
2. allowhighfees    (boolean, optional, default=false) Allow high fees

Result:
"hex"             (string) The transaction hash in hex

Examples:

Create a transaction
> bitcoin-cli createrawtransaction "[{\"txid\" : \"mytxid\",\"vout\":0}]" "{\"myaddress\":0.01}"
Sign the transaction, and get back the hex
> bitcoin-cli signrawtransactionwithwallet "myhex"

Send the transaction (signed hex)
> bitcoin-cli sendrawtransaction "signedhex"

As a json rpc call
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "sendrawtransaction", "params": ["signedhex"] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/

Step 12. Now go for it. Don't let your dreams be dreams. Join the dark side. Commit the crime:

$ sendrawtransaction 0200000001fc5d1634f2083b3d3969d169f4213e302850dac2bb5216cc402314360f268eb4010000002e09726561642e636173682314b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777ffffffff0148f32200000000001976a91440522a99ccae6f05b6a473cf9623a6a2cd1e42ac88ac00000000

Alea iacta est. If the heist succeeds, Electron Cash will quickly detect an incoming transaction. But since the transaction has no cryptographic signature, the booty can still be stolen by a miner even more evil than you - until the transaction is confirmed. Electron Cash will then CashShuffle the proceeds to make it untraceable. Just make sure to avoid spending the shuffled coins together.

Proof of success

Message: All Read.cash's $5 are belong to BigBlockIfTrue!

Address: qpq9y25eejhx7pdk53eul93r563v68jz4s4stdkf5j

Signature: IDuoVZb/0Qm+TAaKMdvf4wbR0WvW/iq1zwN3VwYqf4xSDVY9kxfN3cu3pJ2rnbufmvbjntP9T+eAQ1suPPZIHDw=

3
$ 7.80
$ 5.00 from @Read.Cash
$ 1.00 from @btcfork
$ 1.00 from @molecular
+ 4
Avatar for BigBlockIfTrue
4 years ago

Comments

Very useful information. It is technically detailed and well structured for easy understanding. Thank you for the information

$ 0.00
4 years ago

cool!

congratulations on your successful heist! Now don't spend the booty too visibly or else they will catch you that way ;-)

with the tools you mentioned, how did you compile the script (b251f93c45baea5c72bae18311c75eccad404291 OP_OVER 'read.cash' OP_EQUAL OP_NIP OP_NIP) to 14b251f93c45baea5c72bae18311c75eccad4042917809726561642e63617368877777?

$ 0.00
4 years ago

Actually the script was already given in the example transaction included in the mission briefing, so there was no need to compile it. I used the decodescript command in the Bitcoin ABC console to make it readable. To encode/decode scripts manually you can use the opcode list on the Bitcoin wiki.

$ 0.00
4 years ago

Wait, what are the negatives? I see only positives here - heist accomplished, we can see read.cash's password recovery works great, and ... uh oh, people are probably working on a brute force recovery cracker based on your instructions ... ok, I see the possible negative now :-p

But future users of the scheme ought to be using a password manager, to generate strong recovery passwords instead of simple ones like "read.cash".

$ 0.00
User's avatar btcfork
This user is who they claim to be.
We have manually verified this user via some other channel.
4 years ago

I posted a more serious response to the proposal here. :)

$ 0.00
4 years ago

I mention it actually in the article:

The constantly happening brute-force means though, that the "recovery" word should still be at least a bit harder than "mom" to avoid accidental recoveries (which again is more of a nuisance, rather than a security problem).

Maybe the wallet can add something like the birthdate to the recovery word of the user to avoid "accidental" recoveries. (Which are again only are an inconvenience, not a security problem)

Also worth noting that "read.cash" heist address wasn't protected with CHECKDATASIG, so the money could be taken by attacker. Not the case with CHECKDATASIG protected addresses.

Even though it doesn't even make sense to spend the time guessing your recovery word, since all it can do is send the money to your another trusted account (a minor inconvenience at best).

So I don't think we need "strong" passwords, but rather a bit of password-stretching, like "add your birthday", the wallet can also add its name. Should be more than enough, since the hacker won't achieve anything except causing a minor inconvenience to the user.

But I'm also curious what downsides BigBlockIfTrue sees.

$ 1.00
4 years ago

But I'm also curious what downsides BigBlockIfTrue sees.

Me too, I don't see any real negatives (if using CHECKDATASIG protection). I wouldn't downplay unwanted recovery activation as merely a 'nuisance' as you did in your original article, but it's certainly not a big funds loss risk (other than recovery destination wallet being inaccessible for some reason). And it can be mitigated with something like your "birthday" suggestion.

$ 0.00
4 years ago

Thank you!

nuisance

Sure, but how would you describe it. Yeah, it's a hassle that you'd have to re-do the ritual of returning funds, but in the grand scheme of things - it's not a big deal at all. Maybe, 'inconvenience'?

$ 0.00
4 years ago