├── found.txt ├── requirements.txt ├── LICENSE ├── btc-hack.py └── README.md /found.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | base58 2 | ecdsa 3 | requests 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /btc-hack.py: -------------------------------------------------------------------------------- 1 | # Purpyl Media Bitcoin Brute Forcer 2 | # Made by David Gilbert 3 | # https://github.com/purpyl-media/btc-hack 4 | # https://www.purpyl.media 5 | 6 | try: 7 | import sys 8 | import os 9 | import time 10 | import hashlib 11 | import binascii 12 | import multiprocessing 13 | from multiprocessing import Process, Queue 14 | from multiprocessing.pool import ThreadPool 15 | import threading 16 | import base58 17 | import ecdsa 18 | import requests 19 | 20 | # If required imports are unavailable, we will attempt to install them! 21 | 22 | except ImportError: 23 | import subprocess 24 | subprocess.check_call(["python3", '-m', 'pip', 'install', 'base58==1.0.0']) 25 | subprocess.check_call(["python3", '-m', 'pip', 'install', 'ecdsa==0.13']) 26 | subprocess.check_call(["python3", '-m', 'pip', 'install', 'requests==2.19.1']) 27 | import base58 28 | import ecdsa 29 | import requests 30 | 31 | def generate_private_key(): 32 | return binascii.hexlify(os.urandom(32)).decode('utf-8') 33 | 34 | def private_key_to_WIF(private_key): 35 | var80 = "80" + str(private_key) 36 | var = hashlib.sha256(binascii.unhexlify(hashlib.sha256(binascii.unhexlify(var80)).hexdigest())).hexdigest() 37 | return str(base58.b58encode(binascii.unhexlify(str(var80) + str(var[0:8]))), 'utf-8') 38 | 39 | def private_key_to_public_key(private_key): 40 | sign = ecdsa.SigningKey.from_string(binascii.unhexlify(private_key), curve = ecdsa.SECP256k1) 41 | return ('04' + binascii.hexlify(sign.verifying_key.to_string()).decode('utf-8')) 42 | 43 | def public_key_to_address(public_key): 44 | alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 45 | count = 0; val = 0 46 | var = hashlib.new('ripemd160') 47 | var.update(hashlib.sha256(binascii.unhexlify(public_key.encode())).digest()) 48 | doublehash = hashlib.sha256(hashlib.sha256(binascii.unhexlify(('00' + var.hexdigest()).encode())).digest()).hexdigest() 49 | address = '00' + var.hexdigest() + doublehash[0:8] 50 | for char in address: 51 | if (char != '0'): 52 | break 53 | count += 1 54 | count = count // 2 55 | n = int(address, 16) 56 | output = [] 57 | while (n > 0): 58 | n, remainder = divmod (n, 58) 59 | output.append(alphabet[remainder]) 60 | while (val < count): 61 | output.append(alphabet[0]) 62 | val += 1 63 | return ''.join(output[::-1]) 64 | 65 | def get_balance(address): 66 | time.sleep(0.2) #This is to avoid over-using the API and keep the program running indefinately. 67 | try: 68 | response = requests.get("https://sochain.com/api/v2/address/BTC/" + str(address)) 69 | return float(response.json()['data']['balance']) 70 | except: 71 | return -1 72 | 73 | def data_export(queue): 74 | while True: 75 | private_key = generate_private_key() 76 | public_key = private_key_to_public_key(private_key) 77 | address = public_key_to_address(public_key) 78 | data = (private_key, address) 79 | queue.put(data, block = False) 80 | 81 | def worker(queue): 82 | while True: 83 | if not queue.empty(): 84 | data = queue.get(block = True) 85 | balance = get_balance(data[1]) 86 | process(data, balance) 87 | 88 | def process(data, balance): 89 | private_key = data[0] 90 | address = data[1] 91 | if (balance == 0.00000000): 92 | print("{:<34}".format(str(address)) + " : " + str(balance)) 93 | if (balance > 0.00000000): 94 | file = open("found.txt","a") 95 | file.write("address: " + str(address) + "\n" + 96 | "private key: " + str(private_key) + "\n" + 97 | "WIF private key: " + str(private_key_to_WIF(private_key)) + "\n" + 98 | "public key: " + str(private_key_to_public_key(private_key)).upper() + "\n" + 99 | "balance: " + str(balance) + "\n\n") 100 | file.close() 101 | 102 | def thread(iterator): 103 | processes = [] 104 | data = Queue() 105 | data_factory = Process(target = data_export, args = (data,)) 106 | data_factory.daemon = True 107 | processes.append(data_factory) 108 | data_factory.start() 109 | work = Process(target = worker, args = (data,)) 110 | work.daemon = True 111 | processes.append(work) 112 | work.start() 113 | data_factory.join() 114 | 115 | if __name__ == '__main__': 116 | try: 117 | pool = ThreadPool(processes = multiprocessing.cpu_count()*2) 118 | pool.map(thread, range(0, 1)) # Limit to single CPU thread as we can only query 300 addresses per minute 119 | except: 120 | pool.close() 121 | exit() 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Purpyl Bitcoin Address Hacker 2 | 3 | An automated bitcoin wallet generator that generates random wallet addresses with associated private key and checks their balance in real-time. 4 | 5 | **⚠️ This program is intended to demonstrate a potential security flaw in blockchain technology. Use and distribution of this program is purely for educational purposes only and is not intended for use in any malicious ways. ⚠️** 6 | 7 | # Like This Project? Give It A Star 8 | 9 | [![](https://img.shields.io/github/stars/purpyl-media/btc-hack.svg)](https://github.com/purpyl-media/btc-hack) 10 | 11 | # Dependencies 12 | 13 | Python 3.6 or higher 14 | 15 | btc-hack.py will try to automatically install the required modules if they are not present. Should that fail, you can find the required modules listed in the requirements.txt 16 | 17 | Minimum RAM requirements 18 | 19 | # Installation 20 | 21 | ``` 22 | $ git clone https://github.com/purpyl-media/btc-hack.git btc-hack 23 | ``` 24 | 25 | # Quick Start 26 | 27 | ``` 28 | $ python3 btc-hack.py 29 | ``` 30 | 31 | # Proof Of Concept 32 | 33 | A private key is a secret number that allows Bitcoins to be spent. If a wallet has Bitcoins in it, then the private key will allow a person to control the wallet and spend whatever balance the wallet has. So this program attempts to find Bitcoin private keys that correlate to wallets with positive balances. However, because it is impossible to know which private keys control wallets with money and which private keys control empty wallets, we have to randomly look at every possible private key that exists and hope to find one that has a balance. 34 | 35 | This program is essentially a brute forcing algorithm. It continuously generates random Bitcoin private keys, converts the private keys into their respective wallet addresses, then checks the balance of the addresses. If a wallet with a balance is found, then the private key, public key and wallet address are saved to the text file `found.txt` on the user's hard drive. The ultimate goal is to randomly find a wallet with a balance out of the 2160 possible wallets in existence. 36 | 37 | # How It Works 38 | 39 | Private keys are generated randomly to create a 32 byte hexidecimal string using the cryptographically secure `os.urandom()` function. 40 | 41 | The private keys are converted into their respective public keys using the `starkbank-ecdsa` Python module. Then the public keys are converted into their Bitcoin wallet addresses using the `binascii` and `hashlib` standard libraries. 42 | 43 | The generated address is searched using an online api, and if it is found that the address has a balance, then the private key, public key and wallet address are saved to the text file `found.txt` on the user's hard drive. 44 | 45 | This program also utilizes multiprocessing through the `multiprocessing.Process()` function in order to make concurrent calculations. Sadly this is limited at present due to api restrictions of 300 queries per minute. 46 | 47 | # Efficiency 48 | 49 | It takes `0.0032457721` seconds for this progam to brute force a __single__ Bitcoin address. 50 | 51 | However, through `multiprocessing.Process()` a concurrent process is created for every CPU your computer has. So this program can brute force addresses at a speed of `0.0032457721 ÷ cpu_count()` seconds. 52 | 53 | # API FAQ 54 | 55 | Currently, the program runs and queries the sochain.com api. As this api allows for upto 300 queries per minute for free, it seemed like a really good choice allowing you to test approximately 18,000 adresses per hour, 432,000 addresses per 24 hours or 3,000,000 addresses per week! 56 | 57 | # Expected Output 58 | 59 | Every time this program checks the balance of a generated address, it will print the result to the user. If an empty wallet is found, then the wallet address will be printed to the terminal. An example is: 60 | 61 | >1Kz2CTvjzkZ3p2BQb5x5DX6GEoHX2jFS45 : 0.0 62 | 63 | However, if a wallet with a balance is found, then all necessary information about the wallet will be saved to the text file `found.txt`. An example is: 64 | 65 | >address: 1Kz2CTvjzkZ3p2BQb5x5DX6GEoHX2jFS45
66 | >private key: 5A4F3F1CAB44848B2C2C515AE74E9CC487A9982C9DD695810230EA48B1DCEADD
67 | >WIF private key: 5JW4RCAXDbocFLK9bxqw5cbQwuSn86fpbmz2HhT9nvKMTh68hjm
68 | >public key: 04393B30BC950F358326062FF28D194A5B28751C1FF2562C02CA4DFB2A864DE63280CC140D0D540EA1A5711D1E519C842684F42445C41CB501B7EA00361699C320
69 | >balance: 0.0001456
70 | 71 | # Memory Consumption 72 | 73 | This program uses approximately 2GB of RAM per CPU. Because this program can use multi-processing, some data gets shared between threads making it difficult to accurately measure RAM usage. 74 | 75 | The memory consumption stack trace was made by using mprof to monitor this program brute force 10,000 addresses on a 4 logical processor machine with 8GB of RAM. As a result, 4 child processes were created, each consuming 2100MiB of RAM (~2GB). 76 | 77 | # TODO 78 | 79 | - [X] Find a faster FREE API with greater allowances. 80 | 81 | 82 | Create an issue so I can add more stuff to improve 83 | --------------------------------------------------------------------------------