Pages:
Author

Topic: A breakable P2SH address - Bitcoin testnet inside - page 2. (Read 602 times)

legendary
Activity: 2114
Merit: 1292
There is trouble abrewing
It is easier than you think to break it. And someone just did:

i don't know if you don't know the meaning of the word "break" or just misusing it but this is not what "breaking" means. breaking would be if and only if the only thing that was revealed were the hash of the script and nothing else.
as i said there is absolutely no way of breaking it when it is only the hash. but when you have already revealed the script here (which anyone can spend) all it takes is a copy and paste of it into a new transaction spending the new output(s).
copper member
Activity: 193
Merit: 235
Click "+Merit" top-right corner
Alright, let's talk about Bitcoin scripts and its opcodes.

A transaction is (partly - the most interesting part of it) an unlocking script and a locking script, which are combined - in that very order, evaluated (executed - run as a program), and must return "TRUE" (which technically means that the stack must contain a positive integer top value and nothing else).

A P2SH address is simply the base58check encoded version of the hashed locking script, so that it impossible to examine a "3-address" and determine its script (since it is hashed). This is where previous blockchain data comes in because my example address couldn't have been compromised if I hadn't prepared things for you and already spent from it once before writing the OP.

Let's take it step by step.

While there are some common formats for P2SH scripts (such as "OP_HASH160 20 OP_EQUAL") there is nothing that says that a P2SH address must adhere to any certain format. In fact, more complex scripts not only allowed but actually encouraged by Bitcoin. Multisig scripts, for example, can be very complex and contain a shitload of IF/THEN conditions. Remember however that when making a script, there must exist conditions for it (together with its inputs in the unlocking script) so that it may return "TRUE". If not, it is an unspendable address (and such addresses are curiously simple to create; you have been warned - don't unintentionally create Bitcoin black holes).

Let me lay out the locking script, so I can comment on it (and yes, I did use a few uncommon opcodes, but that's not illegal - they are standard by definition):

Code:
OP_SIZE
OP_SWAP
OP_SIZE
OP_NIP
8 0xd9ac1d9c37b651db
OP_ROT
OP_ROT
OP_EQUAL
OP_ROT
OP_DROP
OP_SWAP
OP_DROP
OP_1
8 0x99ac1d9c37b651db
OP_DROP
OP_EQUAL

It is quite messy, and intentionally so, but it can be deciphered. Basically, what it does it that is takes two inputs, calculates their sizes (and not their values!) and if they are equal, it will (again together with the unlocking code below) return "TRUE".

This locking script shuffles around stack values like crazy and adds instances of gibberish 8-byte strings twice, which are never evaluated but simply dropped. Also, since the script will be serialized (converted to a hexadecimal string), I was very happy to end up with an exactly 32-byte script, since I figured that strings of that length are easily mistaken for hash values and/or signatures. The serialized locking script is thus

Code:
0x827c827708d9ac1d9c37b651db7b7b877b757c75510899ac1d9c37b651db7587

and this is easily converted to testnet public address 2NA4ZpHbzfp7VkAXHjpZc8Ze6SpNfS7jSbF and mainnet public address 3JWMkYfy4Mc9YNtk4gwjWceqEUAViUiMF5 with a few lines of Python (see previous post by yours truly) or if you're even lazier (or smarter, depending on your your world-view) using a web service (one linked below).

So, the unlocking script then. Again, I went for 32-byte strings, for the same reason; easily mistaken for hashes that actually represent something. But I simply generated two random hexadecimal numbers with bash command "openssl rand -hex 32" (which - by the way - is an excellent one-liner for producing unbreakable private keys), so that:

Code:
32 0x2803d055a4a133bde555a39d37762c8354b6f7418817c5c4b516cf413b280209
32 0x3dbb8323f94bf9acd13a5f92e0d0a7e87f34e31b09a866fdc80437a57e24a114

Looks really serious, doesn't it?

Remember though, these numbers are completely random and are never evaluated! The only requirement by my script is that two inputs of equal length are provided. This means that another unlocking script that would have and will work equally well for this address is (this is what I hoped someone would realize):

Code:
OP_1
OP_1

Oh yeah, this will work too:

Code:
OP_4
OP_7

Same length, not values - remember?


If you want to see the unlocking and locking scripts together, how they are evaluated step by step and how this crazy combination eventually leads to "TRUE", here they are together (this page is in my humble opinion awesome for P2SH experiments):

https://bitcoin-script-debugger.visvirial.com/?input=32%200x2803d055a4a133bde555a39d37762c8354b6f7418817c5c4b516cf413b280209%2032%200x3dbb8323f94bf9acd13a5f92e0d0a7e87f34e31b09a866fdc80437a57e24a114%20OP_SIZE%20OP_SWAP%20OP_SIZE%20OP_NIP%208%200xd9ac1d9c37b651db%20OP_ROT%20OP_ROT%20OP_EQUAL%20OP_ROT%20OP_DROP%20OP_SWAP%20OP_DROP%20OP_1%208%200x99ac1d9c37b651db%20OP_DROP%20OP_EQUAL

Note especially the green "Result: OK".

The person (or bot? - I still don't know who cracked it) simply did a copy/paste of the combined unlocking and locking script from my first spending

Code:
63202803d055a4a133bde555a39d37762c8354b6f7418817c5c4b516cf413b280209203dbb8323f94bf9acd13a5f92e0d0a7e87f34e31b09a866fdc80437a57e24a11420827c827708d9ac1d9c37b651db7b7b877b757c75510899ac1d9c37b651db7587

and used it unchanged to create a new transaction. To view the raw transation, it can be done in Bitcoin Core console using "getrawtransaction 023f0dde4d0b11581cb81214462170995b85ec6be41c78c5fb44084c7d9e671a" or in Electrum console using "gettransaction('023f0dde4d0b11581cb81214462170995b85ec6be41c78c5fb44084c7d9e671a')", but remember to start them in testnet mode before trying.

In other words, the next time you want to snatch coins from these addresses, try the shorter redeem code:

Code:
23515120827c827708d9ac1d9c37b651db7b7b877b757c75510899ac1d9c37b651db7587

Unless you didn't already know OP_1 is "51" in hexadecimal, why the string begins with two instances of that.

Phew... that took some time to write up...

I hope you liked it, and in that casse feel free to pass over some testnet coins to 2N2EJxoRy5hRE74aV4NDiC22dcH3QEqzf5G, because I was just robbed of almost all I had there Smiley



Edit/additions:

1) To clarify further, what I tried to accomplish with three different 32-byte strings as the Sigcode

Code:
2803d055a4a133bde555a39d37762c8354b6f7418817c5c4b516cf413b280209
3dbb8323f94bf9acd13a5f92e0d0a7e87f34e31b09a866fdc80437a57e24a114
827c827708d9ac1d9c37b651db7b7b877b757c75510899ac1d9c37b651db7587

which are the values you see when you look at a transaction in a block explorer was an attempted deception. I.e NOT "hey, this looks like a broken signature".

2) Speaking of signatures. I should have written earlier that my script does zero signature evaluation and there is no elliptic curve cryptography (ECC) anywhere, why the address is another "anyone-can-spend" now that the required inputs have been disclosed, so any old redeem code that has worked will work again.


copper member
Activity: 193
Merit: 235
Click "+Merit" top-right corner
Show me you can break it and sweep the coins!

It's NOT of those "try and bruteforce my 32-byte private key" or the like. The solution is simple.

well unless you consider cheating to be a "solution", it is not possible to break something like this even if you knew the number of OP codes inside the script or knew the ballpark of their type. it contains 14 OP codes that are never used (ROT, SWAP, NIP, SIZE, ...) and 2 random data pushes!

It is easier than you think to break it. And someone just did:

https://www.blockchain.com/btc-testnet/tx/023f0dde4d0b11581cb81214462170995b85ec6be41c78c5fb44084c7d9e671a

I don't know whether it was someone reading here or not.

Edit: I will update with a more exhaustive answer and an even more elegant solution than simply copying the signatures from the first spending, as the "thief" in this case apparently did.
legendary
Activity: 2114
Merit: 1292
There is trouble abrewing
Show me you can break it and sweep the coins!

It's NOT of those "try and bruteforce my 32-byte private key" or the like. The solution is simple.

well unless you consider cheating to be a "solution", it is not possible to break something like this even if you knew the number of OP codes inside the script or knew the ballpark of their type. it contains 14 OP codes that are never used (ROT, SWAP, NIP, SIZE, ...) and 2 random data pushes!
hero member
Activity: 1194
Merit: 573
OGRaccoon
This is very interesting @BTCW.

I have a few transaction I would like to ask you about that you may know how I can resolve.

PM sent
copper member
Activity: 193
Merit: 235
Click "+Merit" top-right corner
but tBTC has no price...


meaning to waste time

Who says this exact same setup doesn't exist on the mainnet? Testnet is great for exercises like this, so we can take what we learn and apply it to the mainnet after.

Edit/clarifiaction: Let's caluculate the "mainnet twin" to the address (Python):

Code:
>>> import binascii, hashlib, base58
>>> hash160 = binascii.hexlify(base58.b58decode_check(b'2NA4ZpHbzfp7VkAXHjpZc8Ze6SpNfS7jSbF')).decode()[2:]
>>> hash160
'b8761d30be6f2408882e0b458f8fdfc4c896ed45'
>>> base58.b58encode_check(binascii.unhexlify('05'+hash160)).decode()
'3JWMkYfy4Mc9YNtk4gwjWceqEUAViUiMF5'
>>> base58.b58encode_check(binascii.unhexlify('c4'+hash160)).decode()
'2NA4ZpHbzfp7VkAXHjpZc8Ze6SpNfS7jSbF'

In other words, if you can spend from 2NA4ZpHbzfp7VkAXHjpZc8Ze6SpNfS7jSbF on the testnet, you can use the exact same "recipe" to spend from 3JWMkYfy4Mc9YNtk4gwjWceqEUAViUiMF5 on the mainnet, should it ever pop up there.

Cool?
newbie
Activity: 54
Merit: 0
but tBTC has no price...


meaning to waste time
copper member
Activity: 193
Merit: 235
Click "+Merit" top-right corner
Long story short, I created this not-very-standard P2SH public address: 2NA4ZpHbzfp7VkAXHjpZc8Ze6SpNfS7jSbF

And gave it some history on the blockchain (hint!) and just funded it again with 0.11111111 tBTC

Show me you can break it and sweep the coins!

It's NOT of those "try and bruteforce my 32-byte private key" or the like. The solution is simple.

If no one has touched the coins in a day or so, I'll show you exactly how.

Do it!



Edit: The updated challenge is here
Pages:
Jump to: