Score:2

ECDSA Common Nonce Reuse Attack

bf flag

so I recently stumbled upon this video by @bertcmiller who created two transactions with the same nonce "k". That seen I researched quite a lot of pages explaining how to recover the private key from this. I know that I first have to find out the "k" value to recover the private key.

The formula for getting k is:

$k = (H(m1)-H(m2)) \div (s1-s2)$

Luckily @bertcmiller exposed the "k" value in his video to be "1".

Picture of elliptic signature tool in NodeJs

Am I right that H(m1) is the hash of the signed transaction, which is the transaction hash where you can lookup on etherscan for example?

To continue cracking it down on his example a bit further lets collect some static information:

Transaction 1:
Signed Raw Tx: 0x02f86b0103849502f90085194f55e7ec82afc894e9cb1f44f70ed976d033cee45d745d838185c9368080c001a0340709f674d030dda4aa8794bffb578030870bdfe583e7f41aea136a7ca1ed94a02c17cf960d1af7602f875afc56d01eddcc67bb03e962877444ed8d4c1287ab7a
Signed Tx Hash: 0xe3299f4fc18300208992a27c65c5a3ae14564eb594b4fa363e2bc81759a45317
r = 0x340709f674d030dda4aa8794bffb578030870bdfe583e7f41aea136a7ca1ed94
s = 0x2c17cf960d1af7602f875afc56d01eddcc67bb03e962877444ed8d4c1287ab7a
v = 1

Transaction 2:
Signed Raw Tx: 0x02f86b0104849502f900851bb39bdfc282afc894e9cb1f44f70ed976d033cee45d745d838185c9368080c001a0340709f674d030dda4aa8794bffb578030870bdfe583e7f41aea136a7ca1ed94a03e5b92d8c5a2a033c9e5eb53ff2946681b28ab82fa5b17d0a9c48d139e78fe2c
Signed Tx Hash: 0x339f409b13bdb2e579fcc4c9a7404bae5e499d6a806a6b1c7088c354988f7eee
r = 0x340709f674d030dda4aa8794bffb578030870bdfe583e7f41aea136a7ca1ed94
s = 0x3e5b92d8c5a2a033c9e5eb53ff2946681b28ab82fa5b17d0a9c48d139e78fe2c
v = 1

His Wallet:
Address: 0xE9cb1f44F70ed976d033CEe45d745D838185c936
Public Key (uncompressed): 0x04fba1b0e8f41c19627439d5d467e3d41e8514c55057bd73e1f9f50aad85ad1238cce7a4892aefd639c17a6bf3d0009cf7636954dac01f130e486be286bac4bfcd
Public Key (compressed): 0x03fba1b0e8f41c19627439d5d467e3d41e8514c55057bd73e1f9f50aad85ad1238

I know there are several posts out there explaining how that ECDSA based system is working, but I don't really get to the point recovering the private key, even with the knowledge of knowing "k" and "v,r,s" and all the other stuff.

The formula solved for priv (the actual private key) is:

$priv = r^{-1} (s⋅k−H(M))$

So what am I doing wrong? Here is my code btw:

import ecdsa
import libnum

# Core variables got from parsing the signed transaction hash with web3
account = 0xE9cb1f44F70ed976d033CEe45d745D838185c936
pubKey_uc = 0x04fba1b0e8f41c19627439d5d467e3d41e8514c55057bd73e1f9f50aad85ad1238cce7a4892aefd639c17a6bf3d0009cf7636954dac01f130e486be286bac4bfcd
pubKey_cp = 0x03fba1b0e8f41c19627439d5d467e3d41e8514c55057bd73e1f9f50aad85ad1238

txHash_1 = 0xe3299f4fc18300208992a27c65c5a3ae14564eb594b4fa363e2bc81759a45317
txHash_2 = 0x339f409b13bdb2e579fcc4c9a7404bae5e499d6a806a6b1c7088c354988f7eee
r = 0x340709f674d030dda4aa8794bffb578030870bdfe583e7f41aea136a7ca1ed94
s1 = 0x2c17cf960d1af7602f875afc56d01eddcc67bb03e962877444ed8d4c1287ab7a
s2 = 0x3e5b92d8c5a2a033c9e5eb53ff2946681b28ab82fa5b17d0a9c48d139e78fe2c

# Got the k (ecdsa nonce) value from the video
k = 1

# Initiate the elliptic curve and get base coordinates
# (G.x(),G.y()) and the order number (n) !important for modulo n
G = ecdsa.SECP256k1.generator
n = G.order()

# Verify that k is correct by recovering k mathematically
# I assume that txHash_1 and txHash_2 are z1 and z2 because z is the message hash?
# Formula: k = (((z1 - z2) % G) * inverse_mod(r,G)) % G
z1 = txHash_1
z2 = txHash_2
k_re = (((z1-z2) % n) * libnum.invmod(r,n)) % n
# Spoiler alert: returning False
# Why is that?
print(k_re == k)

# Verify r value from transaction
# Must be the same as above if correct?
# r is the x value hexed from k*G
R = k * G
r_re = R.x()
# Spoiler alert: returning False
# Why is that?
print(r_re == r)

# Trying to recover the private key, although k seems to be wrong and r too
# Formula: priv = (r1^-1 * ((k * s1) - z1)) % n
priv = (libnum.invmod(r,n) * ((k * s1) - z1)) % n
priv_re = (libnum.invmod(r_re,n) * ((k_re * s1) - z1)) % n

# Recovering the public key now
# Formula: pub = priv * G
pub = priv * G
pub_re = priv_re * G
# Construc uncompressed public key "0x04" means uncompressed followed by hex encoded x and y coordinates
pubKey = "0x04"+str(hex(pub.x()))[2:]+str(hex(pub.y()))[2:]
pubKey_re = "0x04"+str(hex(pub_re.x()))[2:]+str(hex(pub_re.y()))[2:]
# Spoiler alert: returning False
# Why is that?
print(pubKey == pubKey_uc)
print(pubKey_re == pubKey_uc)
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.