├── tests ├── red │ ├── r1.out │ ├── r2.out │ ├── r3.out │ ├── r4.out │ ├── r5.out │ ├── r1.in │ ├── r2.in │ ├── r3.in │ ├── r4.in │ └── r5.in ├── green │ ├── g1.out │ ├── g2.out │ ├── g1.in │ ├── g2.in │ └── green1.txt ├── mischa │ ├── m1.out │ └── m1.in └── golden │ ├── d1.out │ ├── d2.out │ ├── d1.in │ └── d2.in ├── src ├── base58.h ├── petya.h ├── CMakeLists.txt ├── common.h ├── ec.h ├── base58.cpp ├── endian.h ├── petya.cpp └── main.cpp └── README.md /tests/red/r1.out: -------------------------------------------------------------------------------- 1 | rU95BhkeU4Eua2q3 2 | -------------------------------------------------------------------------------- /tests/red/r2.out: -------------------------------------------------------------------------------- 1 | 8fb9GLT7qkQJ5hBu 2 | -------------------------------------------------------------------------------- /tests/red/r3.out: -------------------------------------------------------------------------------- 1 | DPUjrHHNmCRbnFUL 2 | -------------------------------------------------------------------------------- /tests/red/r4.out: -------------------------------------------------------------------------------- 1 | i8kV3MUF8kiwR31j 2 | -------------------------------------------------------------------------------- /tests/red/r5.out: -------------------------------------------------------------------------------- 1 | msV7BwS7EXWNhaDb 2 | -------------------------------------------------------------------------------- /tests/green/g1.out: -------------------------------------------------------------------------------- 1 | H9UM88r4k49QWmNk 2 | -------------------------------------------------------------------------------- /tests/green/g2.out: -------------------------------------------------------------------------------- 1 | VcwwBGdPWb22ge5b 2 | -------------------------------------------------------------------------------- /tests/mischa/m1.out: -------------------------------------------------------------------------------- 1 | vW2ebtSboq7gBdUU 2 | -------------------------------------------------------------------------------- /tests/golden/d1.out: -------------------------------------------------------------------------------- 1 | f63117737990bd58de1d91a63a9156a0 2 | -------------------------------------------------------------------------------- /tests/golden/d2.out: -------------------------------------------------------------------------------- 1 | 8e7731b9c62332ce4b829380d3443804 2 | -------------------------------------------------------------------------------- /tests/red/r1.in: -------------------------------------------------------------------------------- 1 | e4Q8ffSQY1yvicQi2FERwAoYQLfRPJ9b9pukbkSQ3Pk21KJr9DXZbfqXZDFKnnKZRPJ5MXvNAC9eajUsqRCNu43hQ2 2 | -------------------------------------------------------------------------------- /tests/red/r2.in: -------------------------------------------------------------------------------- 1 | e4Pb8YnLu3LBLViHMPcSFx5A4rEW7RcuYdVjiMBqccTbQosDqZvSvrVZxnn9w1J5bGk2JyQzV39Kb47newj1LQ6Qfp 2 | -------------------------------------------------------------------------------- /tests/red/r3.in: -------------------------------------------------------------------------------- 1 | faRxDptWDNGLwLSHkmAtdNpJVnkw1WvKikasvxcsvbJ2NvfDZt276W4P1QihSPVnhAvkxNQSmWGSVXnxXo4HqubC1o 2 | -------------------------------------------------------------------------------- /tests/red/r4.in: -------------------------------------------------------------------------------- 1 | ceNQvr2pYRvJ4D1oELoK4z6DVnM11HHZiQomqY7fNXQDAvAMhkkyzvzBWw2CzCbdSM2HuJPYENrnTNDyEFSVrb64f1 2 | -------------------------------------------------------------------------------- /tests/red/r5.in: -------------------------------------------------------------------------------- 1 | 88QCVsQJ1v7zXRXhtTSxkC9ZNJi6G4gxikkpNLQZJVAHZHmLdS7LM43zdpUBooz3aRgJVgLR61wqqxvyyQRRhshPMC 2 | -------------------------------------------------------------------------------- /tests/green/g1.in: -------------------------------------------------------------------------------- 1 | a5PhwcoxzctW7vUN5B5oPLTf64cnTe4ZWJyx5tKGHhHJQvSFMfPNAHiZcazmSdz6ZWo4uaYKtmC3rQj6g67wCmrZDu918bA1 2 | -------------------------------------------------------------------------------- /tests/green/g2.in: -------------------------------------------------------------------------------- 1 | 08R8pyaitpdnhbpFAWp5DeE9EWzQ6gbvxaGUvGaXTj2ADRd9Sxse2T9GxBVsc11aE917GXSKhrALGKeSK8qi9DDUdt908fA5 2 | -------------------------------------------------------------------------------- /tests/golden/d1.in: -------------------------------------------------------------------------------- 1 | rzvj43fWvf5ef3Nq6W7Z4Zbzoehmh557m972QhMedVHQvLyju3mx7BrRb5XpkvD21JUVw1M9Cs74AvZpzGTWp4xbp9aVqsoL 2 | -------------------------------------------------------------------------------- /tests/golden/d2.in: -------------------------------------------------------------------------------- 1 | rkNK8Wp58MMCTFJ8PFVE4qucSs2fkkwKnKS5RJ9uiqpR9MqZ3ibEfHXefzAcD4CzLMh2qb5f8xMCNLHjdrr9WSwPudRJ636q 2 | -------------------------------------------------------------------------------- /tests/green/green1.txt: -------------------------------------------------------------------------------- 1 | ccP1V6kux9wFwGxGSyangz7WoAkmSgUtDpZsPE2Q2EWvVDrSKP6KaTunJDF2nsM7uxT6tF41Fyn2dzgZtgTA8eB8Yd908fA1 2 | -------------------------------------------------------------------------------- /tests/mischa/m1.in: -------------------------------------------------------------------------------- 1 | b4QjQkFw2h8ua41xCUJRDYKTNWkiXGRuozLL2dWwFtFMEzku24i6D7phY2ht7QZXgqi1xxZNY1nhiJ4YLygF2GGgiZ908FA1 2 | -------------------------------------------------------------------------------- /src/base58.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef _MSC_VER 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | 12 | size_t decode_base58(const char *str, size_t str_size, uint8_t *out_buf, size_t out_buf_size); 13 | -------------------------------------------------------------------------------- /src/petya.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define SECTOR_SIZE 0x200 7 | 8 | #define REDGREEN_ONION_SECTOR_NUM 54 9 | #define GOLDEN_ONION_SECTOR_NUM 32 10 | 11 | #define HTTP_OFFSET_RED 0x53 12 | #define HTTP_OFFSET 0x69 13 | #define VICTIM_ID_OFFSET 0xa9 14 | #define VICTIM_ID_MAXSIZE 100 15 | 16 | 17 | bool is_infected(FILE *fp); 18 | 19 | char* fetch_victim_id(FILE *fp, size_t onion_sector_num); 20 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project (petya_key) 3 | 4 | set (hdrs 5 | ec.h 6 | common.h 7 | endian.h 8 | base58.h 9 | petya.h 10 | ) 11 | 12 | set (srcs 13 | main.cpp 14 | base58.cpp 15 | petya.cpp 16 | ) 17 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 18 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lcrypto") 19 | 20 | add_executable (${PROJECT_NAME} ${hdrs} ${srcs}) 21 | 22 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | //from: https://github.com/keeshux/basic-blockchain-programming/ 2 | #ifndef __COMMON_H 3 | #define __COMMON_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include "endian.h" 9 | 10 | void bbp_print_hex(const char *label, const uint8_t *v, size_t len) { 11 | size_t i; 12 | 13 | printf("%s: ", label); 14 | for (i = 0; i < len; ++i) { 15 | printf("%02x", v[i]); 16 | } 17 | printf("\n"); 18 | } 19 | 20 | uint8_t bbp_hex2byte(const char ch) { 21 | if ((ch >= '0') && (ch <= '9')) { 22 | return ch - '0'; 23 | } 24 | if ((ch >= 'a') && (ch <= 'f')) { 25 | return ch - 'a' + 10; 26 | } 27 | return 0; 28 | } 29 | 30 | void bbp_parse_hex(uint8_t *v, const char *str) { 31 | const size_t count = strlen(str) / 2; 32 | size_t i; 33 | 34 | for (i = 0; i < count; ++i) { 35 | const char hi = bbp_hex2byte(str[i * 2]); 36 | const char lo = bbp_hex2byte(str[i * 2 + 1]); 37 | 38 | v[i] = hi * 16 + lo; 39 | } 40 | } 41 | 42 | uint8_t *bbp_alloc_hex(const char *str, size_t *len) { 43 | const size_t count = strlen(str) / 2; 44 | size_t i; 45 | 46 | uint8_t *v = (uint8_t*) malloc(count); 47 | 48 | for (i = 0; i < count; ++i) { 49 | const char hi = bbp_hex2byte(str[i * 2]); 50 | const char lo = bbp_hex2byte(str[i * 2 + 1]); 51 | 52 | v[i] = hi * 16 + lo; 53 | } 54 | 55 | *len = count; 56 | 57 | return v; 58 | } 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/ec.h: -------------------------------------------------------------------------------- 1 | //based on: https://github.com/keeshux/basic-blockchain-programming/blob/master/ec.h 2 | //modified for secp192k1 3 | 4 | #ifndef __EC_H 5 | #define __EC_H 6 | 7 | #ifdef _MSC_VER 8 | #include 9 | #else 10 | #include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define PRIV_KEY_SIZE 24 18 | 19 | EC_KEY *bbp_ec_new_keypair(const uint8_t *priv_bytes) 20 | { 21 | EC_KEY *key; 22 | BIGNUM *priv; 23 | BN_CTX *ctx; 24 | const EC_GROUP *group; 25 | EC_POINT *pub; 26 | 27 | /* init empty OpenSSL EC keypair */ 28 | 29 | key = EC_KEY_new_by_curve_name(NID_secp192k1); 30 | 31 | /* set private key through BIGNUM */ 32 | 33 | priv = BN_new(); 34 | BN_bin2bn(priv_bytes, PRIV_KEY_SIZE, priv); 35 | EC_KEY_set_private_key(key, priv); 36 | 37 | /* derive public key from private key and group */ 38 | 39 | ctx = BN_CTX_new(); 40 | BN_CTX_start(ctx); 41 | 42 | group = EC_KEY_get0_group(key); 43 | pub = EC_POINT_new(group); 44 | EC_POINT_mul(group, pub, priv, NULL, NULL, ctx); 45 | EC_KEY_set_public_key(key, pub); 46 | 47 | /* release resources */ 48 | 49 | EC_POINT_free(pub); 50 | BN_CTX_end(ctx); 51 | BN_CTX_free(ctx); 52 | BN_clear_free(priv); 53 | 54 | return key; 55 | } 56 | 57 | EC_KEY *bbp_ec_new_pubkey(const uint8_t *pub_bytes, size_t pub_len) 58 | { 59 | EC_KEY *key; 60 | const uint8_t *pub_bytes_copy; 61 | 62 | key = EC_KEY_new_by_curve_name(NID_secp192k1); 63 | pub_bytes_copy = pub_bytes; 64 | o2i_ECPublicKey(&key, &pub_bytes_copy, pub_len); 65 | 66 | return key; 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/base58.cpp: -------------------------------------------------------------------------------- 1 | #include "base58.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | static const int8_t b58_ascii_to_val[] = { 10 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 11 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 12 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 13 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,-1,-1,-1,-1, 14 | -1, 9,10,11,12,13,14,15, 16,-1,17,18,19,20,21,-1, 15 | 22,23,24,25,26,27,28,29, 30,31,32,-1,-1,-1,-1,-1, 16 | -1,33,34,35,36,37,38,39, 40,41,42,43,-1,44,45,46, 17 | 47,48,49,50,51,52,53,54, 55,56,57,-1,-1,-1,-1,-1, 18 | }; 19 | 20 | const uint8_t BASE = 58; 21 | 22 | size_t decode_base58(const char *str, size_t str_size, uint8_t *out_buf, size_t out_buf_size) 23 | { 24 | if (out_buf == NULL || str == NULL) { 25 | return 0; 26 | } 27 | BIGNUM *output = BN_new(); 28 | BIGNUM *base = BN_new(); 29 | BIGNUM *op = BN_new(); 30 | 31 | BN_CTX *ctx = BN_CTX_new(); 32 | BN_CTX_start(ctx); 33 | 34 | BN_bin2bn(&BASE, 1, base); 35 | 36 | for (size_t i = 0; i < str_size; i++) { 37 | uint8_t c = str[i]; 38 | c = b58_ascii_to_val[c]; 39 | if (c == -1) { 40 | break; //invalid character 41 | } 42 | BN_bin2bn(&c, 1, op); 43 | BN_mul(output, output, base, ctx); 44 | BN_add(output, output, op); 45 | } 46 | //check the output size: 47 | size_t output_size = BN_num_bytes(output)*sizeof(uint8_t); 48 | 49 | size_t res = -1; 50 | if (out_buf_size >= output_size) { 51 | //the buffer lenght is OK, fill the buffer: 52 | res = BN_bn2bin(output, out_buf); 53 | } 54 | //cleanup 55 | BN_CTX_end(ctx); 56 | BN_CTX_free(ctx); 57 | BN_clear_free(output); 58 | BN_clear_free(base); 59 | BN_clear_free(op); 60 | return res; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # petya_key 2 | A decoder for Petya victim keys, using the Janus' masterkey
3 | It supports: 4 | + Red Petya 5 | + Green Petya (both versions) + Mischa 6 | + Goldeneye (bootlocker + files) 7 | 8 | Read more about identifying Petya versions [here](https://blog.malwarebytes.com/cybercrime/2017/07/keeping-up-with-the-petyas-demystifying-the-malware-family/) 9 | 10 | [⏬ Download tools](https://github.com/hasherezade/petya_key/releases) 11 | 12 | --- 13 | 14 | *DISCLAIMER: Those tools are provided as is and you are using them at your own risk. I am not responsible for any damage or lost data.* 15 | 16 | --- 17 | Usage: 18 | ``` 19 | ./petya_key [victim_data] 20 | ``` 21 | where the `[victim_data]` is a file containing the 'personal decryption code' displayed by the bootlocker 22 | 23 | 1) Save your _"Personal decryption code"_ as a continuous string, without separators. Example of the valid file content: 24 | ``` 25 | e2NKAXKGX7YFYUHPUuwrcfZ6FUkkYtRUdvzqRUwacPgjMvyYr8mH5Pw4X8Wdt6XgLrK7G7m1TVVeBdVzRDayyHFWp76353A1 26 | ``` 27 | 28 | 2) Supply the saved file to the decoder: 29 | ``` 30 | ./petya_key saved_id.txt 31 | ``` 32 | 33 | Choose your version of Petya from the menu. If the given data is valid, you will get your key, i.e: 34 | ``` 35 | [+] Your key : TxgTCXnpUPSeR2U7 36 | ``` 37 | 38 | 3) **Before unlocking attempt I strongly recommend you to make a dump of the full disk.** Some versions of Petya are buggy. For example they may hang during decryption and corrupt your data. 39 | --- 40 | 41 | In order to decrypt MFT, supply the generated key to the bootlocker.
42 | In order to decrypt **files** you need supply the key to an appropriate decryption tool. 43 | 44 | + For **Mischa**: https://github.com/hasherezade/petya_key/files/7348787/mischa_decrypter.zip 45 | + For **Goldeneye**: https://github.com/hasherezade/petya_key/files/7348772/golden_decrypter.zip 46 | -------------------------------------------------------------------------------- /src/endian.h: -------------------------------------------------------------------------------- 1 | //from: https://github.com/keeshux/basic-blockchain-programming/ 2 | #ifndef __ENDIAN_H 3 | #define __ENDIAN_H 4 | 5 | #include 6 | #include 7 | #include "common.h" 8 | 9 | typedef enum { 10 | BBP_BIG, 11 | BBP_LITTLE 12 | } bbp_endian_t; 13 | 14 | bbp_endian_t bbp_host_endian() { 15 | static const union { 16 | uint16_t i; 17 | uint8_t c[2]; 18 | } test = { 0x1234 }; 19 | 20 | return ((test.c[0] == 0x34) ? BBP_LITTLE : BBP_BIG ); 21 | } 22 | 23 | uint16_t bbp_swap16(uint16_t n) { 24 | return (n >> 8) | 25 | ((n & 0xff) << 8); 26 | } 27 | 28 | uint32_t bbp_swap32(uint32_t n) { 29 | return (n >> 24) | 30 | ((n & 0xff0000) >> 8) | 31 | ((n & 0xff00) << 8) | 32 | ((n & 0xff) << 24); 33 | } 34 | 35 | uint64_t bbp_swap64(uint64_t n) { 36 | return (n >> 56) | 37 | ((n & 0xff000000000000) >> 40) | 38 | ((n & 0xff0000000000) >> 24) | 39 | ((n & 0xff00000000) >> 8) | 40 | ((n & 0xff000000) << 8) | 41 | ((n & 0xff0000) << 24) | 42 | ((n & 0xff00) << 40) | 43 | ((n & 0xff) << 56); 44 | } 45 | 46 | uint16_t bbp_eint16(bbp_endian_t e, uint16_t n) { 47 | if (bbp_host_endian() == e) { 48 | return n; 49 | } 50 | return bbp_swap16(n); 51 | } 52 | 53 | uint32_t bbp_eint32(bbp_endian_t e, uint32_t n) { 54 | if (bbp_host_endian() == e) { 55 | return n; 56 | } 57 | return bbp_swap32(n); 58 | } 59 | 60 | uint64_t bbp_eint64(bbp_endian_t e, uint64_t n) { 61 | if (bbp_host_endian() == e) { 62 | return n; 63 | } 64 | return bbp_swap64(n); 65 | } 66 | 67 | void bbp_reverse(uint8_t *dst, size_t len) { 68 | size_t i; 69 | const size_t stop = len >> 1; 70 | for (i = 0; i < stop; ++i) { 71 | uint8_t *left = dst + i; 72 | uint8_t *right = dst + len - i - 1; 73 | const uint8_t tmp = *left; 74 | *left = *right; 75 | *right = tmp; 76 | } 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/petya.cpp: -------------------------------------------------------------------------------- 1 | #include "petya.h" 2 | 3 | bool check_pattern(FILE *fp, size_t offset, const char *cmp_buf, size_t cmp_size) 4 | { 5 | char out_buf[0x400] = { 0 }; 6 | 7 | cmp_size = (cmp_size > sizeof(out_buf)) ? sizeof(out_buf) : cmp_size; 8 | 9 | fseek(fp, offset, SEEK_SET); 10 | size_t read = fread(out_buf, 1, cmp_size, fp); 11 | 12 | if (read != cmp_size) { 13 | printf("Error, read = %d\n", read); 14 | return false; 15 | } 16 | 17 | if (memcmp(out_buf, cmp_buf, cmp_size-1) == 0) { 18 | return true; 19 | } 20 | return false; 21 | } 22 | 23 | bool is_infected(FILE *fp) 24 | { 25 | char Bootloader[] = \ 26 | "\xfa\x66\x31\xc0\x8e\xd0\x8e\xc0\x8e\xd8\xbc\x00\x7c\xfb\x88\x16"; 27 | 28 | const size_t bootloader_offset = 0; 29 | bool has_bootloader = check_pattern(fp, bootloader_offset, Bootloader, sizeof(Bootloader)); 30 | if (has_bootloader) printf("[+] Petya bootloader detected!\n"); 31 | 32 | return has_bootloader; 33 | } 34 | 35 | bool check_onion_sector(FILE *fp, size_t onion_sector_num) 36 | { 37 | char http_pattern[] = "http://"; 38 | size_t pattern_len = strlen(http_pattern); 39 | size_t http_offset = onion_sector_num * SECTOR_SIZE + HTTP_OFFSET; 40 | bool has_http = check_pattern(fp, http_offset, http_pattern, pattern_len); 41 | if (has_http) { 42 | printf("[+] Petya http address detected!\n"); 43 | return has_http; 44 | } 45 | http_offset = onion_sector_num * SECTOR_SIZE + HTTP_OFFSET_RED; 46 | has_http = check_pattern(fp, http_offset, http_pattern, pattern_len); 47 | if (has_http) { 48 | printf("[+] Petya http address detected!\n"); 49 | } 50 | return has_http; 51 | } 52 | 53 | char* fetch_data(FILE *fp, const size_t offset, const size_t in_size) 54 | { 55 | char* in_buf = new char[in_size]; 56 | memset(in_buf, 0, in_size); 57 | fseek(fp, offset, SEEK_SET); 58 | size_t read = fread(in_buf, 1, in_size, fp); 59 | if (read != in_size) { 60 | printf("Error, read = %d\n", read); 61 | return NULL; 62 | } 63 | return in_buf; 64 | } 65 | 66 | char* fetch_victim_id(FILE *fp, size_t onion_sector_num) 67 | { 68 | if (!check_onion_sector(fp, onion_sector_num)) { 69 | return NULL; 70 | } 71 | size_t offset = onion_sector_num * SECTOR_SIZE + VICTIM_ID_OFFSET; 72 | char *data = fetch_data(fp, offset, VICTIM_ID_MAXSIZE); 73 | if (strlen(data) == 0) { 74 | delete []data; 75 | data = NULL; 76 | } 77 | return data; 78 | } 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /*** 2 | * Petya key decoder 3 | * CC-BY: hasherezade 4 | **/ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ec.h" 16 | #include "common.h" 17 | #include "base58.h" 18 | #include "petya.h" 19 | 20 | #define PUBLIC_KEY_LEN 49 21 | #define AES_CHUNK_LEN 16 22 | #define AES_KEY_LEN 32 23 | //#define DISK_INPUT // enable reading the victim ID directly from the disk 24 | 25 | typedef enum Petyas { PETYA_RED = 0, PETYA_GREEN = 1, PETYA_GOLDEN = 2, PETYA_UNK = -1} Petya_t; 26 | 27 | //the private key published by Janus: 28 | uint8_t priv_bytes[] = { 29 | 0x38, 0xdd, 0x46, 0x80, 0x1c, 0xe6, 0x18, 0x83, 0x43, 0x30, 0x48, 0xd6, 0xd8, 0xc6, 0xab, 30 | 0x8b, 0xe1, 0x86, 0x54, 0xa2, 0x69, 0x5b, 0x47, 0x23 31 | }; 32 | 33 | void aes_decrypt_chunk(uint8_t enc_buf[AES_CHUNK_LEN], uint8_t *key_bytes) 34 | { 35 | AES_KEY key; 36 | AES_set_decrypt_key(key_bytes, 256, &key); 37 | AES_ecb_encrypt(enc_buf, enc_buf, &key, AES_CHUNK_LEN); 38 | } 39 | 40 | void xor_buffer(uint8_t *buffer, size_t buffer_size, uint8_t *key, size_t key_size) 41 | { 42 | for (size_t i = 0; i < buffer_size && i < key_size; i++) { 43 | buffer[i] ^= key[i]; 44 | } 45 | } 46 | 47 | size_t truncated_size(const char *str) 48 | { 49 | size_t len = strlen(str); 50 | size_t out_size = len; 51 | for (size_t i = len - 1; i >= 0; i--) { 52 | if (isalnum(str[i])) break; 53 | out_size--; 54 | } 55 | return out_size; 56 | } 57 | 58 | bool load_victim_data(char* line, uint8_t victim_pub_key[PUBLIC_KEY_LEN], uint8_t enc_buf[AES_CHUNK_LEN], Petya_t &my_petya) 59 | { 60 | char* b58_str = line; 61 | if (my_petya == PETYA_RED || my_petya == PETYA_GREEN) { 62 | b58_str += 2; //ommit the first two characters 63 | } 64 | size_t b58_len = truncated_size(b58_str); 65 | if (my_petya == PETYA_GREEN) { 66 | b58_len -= 6; //ommit 6 last characters 67 | } 68 | 69 | const size_t max_decoded = 0x100; 70 | uint8_t decoded[max_decoded] = { 0 }; 71 | 72 | size_t out_len = decode_base58(b58_str, b58_len, decoded, max_decoded); 73 | #ifdef DEBUG 74 | bbp_print_hex("converted ", decoded, out_len); 75 | printf("---\n"); 76 | #endif 77 | if (out_len < PUBLIC_KEY_LEN + AES_CHUNK_LEN) { 78 | printf("Decoded content is too short!\n"); 79 | return false; 80 | } 81 | 82 | memcpy(victim_pub_key, decoded, PUBLIC_KEY_LEN); 83 | memcpy(enc_buf, decoded + PUBLIC_KEY_LEN, AES_CHUNK_LEN); 84 | return true; 85 | } 86 | 87 | void sha512(uint8_t *in_buffer, size_t in_buffer_len, uint8_t out_hash[SHA512_DIGEST_LENGTH]) 88 | { 89 | SHA512_CTX sha512; 90 | SHA512_Init(&sha512); 91 | SHA512_Update(&sha512, in_buffer, in_buffer_len); 92 | SHA512_Final(out_hash, &sha512); 93 | } 94 | 95 | 96 | const EC_KEY* load_session_key(uint8_t session_pub[PUBLIC_KEY_LEN]) 97 | { 98 | // load victim's public key: 99 | #ifdef DEBUG 100 | bbp_print_hex("victim pub: ", session_pub, PUBLIC_KEY_LEN); 101 | #endif 102 | // init empty OpenSSL EC keypair 103 | EC_KEY *session_key = EC_KEY_new_by_curve_name(NID_secp192k1); 104 | if (!session_key) { 105 | puts("Unable to create session keypair"); 106 | return NULL; 107 | } 108 | 109 | // set the public key in uncompressed form: 110 | EC_KEY_set_conv_form(session_key, POINT_CONVERSION_UNCOMPRESSED); 111 | 112 | const unsigned char* victim_pub_key = session_pub; 113 | EC_KEY *pkey = o2i_ECPublicKey(&session_key, &victim_pub_key, PUBLIC_KEY_LEN); 114 | 115 | //TEST: 116 | //try to fetch it back: 117 | uint8_t *pub = (uint8_t*)calloc(PUBLIC_KEY_LEN, sizeof(uint8_t)); 118 | 119 | // pub_copy is needed because i2o_ECPublicKey alters the input pointer 120 | uint8_t *pub_copy = pub; 121 | if (i2o_ECPublicKey(session_key, &pub_copy) != PUBLIC_KEY_LEN) { 122 | puts("Unable to decode public key"); 123 | return NULL; 124 | } 125 | return session_key; 126 | } 127 | 128 | size_t get_expanded_size(uint8_t *secret, size_t secret_len) 129 | { 130 | uint32_t first_dword = 0; 131 | memcpy(&first_dword, secret, sizeof(uint32_t)); 132 | first_dword = bbp_swap32(first_dword); 133 | 134 | uint32_t counter = 0x20; 135 | uint32_t curr = 0; 136 | size_t dif = 0; 137 | do { 138 | curr = first_dword; 139 | curr >>= (counter - 1); 140 | if (curr & 1) { 141 | break; 142 | } 143 | counter--; 144 | dif++; 145 | } while (counter); 146 | 147 | return (secret_len * 8) - dif; 148 | } 149 | 150 | uint8_t *expand_secret(uint8_t* secret, size_t out_secret_len) 151 | { 152 | const size_t secret_data_size = get_expanded_size(secret, out_secret_len); 153 | uint8_t *secret_data = (uint8_t *)OPENSSL_malloc(secret_data_size); 154 | memset(secret_data, 0, secret_data_size); 155 | #ifdef DEBUG 156 | printf("secret size: %d\n", secret_data_size); 157 | #endif 158 | size_t secret_offset = secret_data_size - out_secret_len; 159 | 160 | memcpy(secret_data + secret_offset, secret, out_secret_len); 161 | #ifdef DEBUG 162 | bbp_print_hex("secret buffer: ", secret_data, secret_data_size); 163 | #endif 164 | return secret_data; 165 | } 166 | 167 | bool derive_secret_hash(const EC_POINT *pub_key, EC_KEY *key, uint8_t out_buffer[SHA512_DIGEST_LENGTH]) 168 | { 169 | // allocate the memory for the shared secret 170 | const size_t secret_len = 0x40; 171 | uint8_t *secret = (uint8_t *)OPENSSL_malloc(secret_len); 172 | memset(secret, 0, secret_len); 173 | if (!secret) { 174 | printf("Failed to allocate memory for the secret!\n"); 175 | return false; 176 | } 177 | 178 | // derive the shared secret: 179 | size_t out_secret_len = ECDH_compute_key(secret, secret_len, pub_key, key, NULL); 180 | #ifdef DEBUG 181 | printf("Got secret len: %d = %#x\n", out_secret_len, out_secret_len); 182 | bbp_print_hex("secret: ", secret, out_secret_len); 183 | #endif 184 | // expand the secret: 185 | uint8_t *to_hash = expand_secret(secret, out_secret_len); 186 | size_t to_hash_size = get_expanded_size(secret, out_secret_len); 187 | 188 | sha512(to_hash, to_hash_size, out_buffer); 189 | #ifdef DEBUG 190 | bbp_print_hex("SHA512: ", out_buffer, SHA512_DIGEST_LENGTH); 191 | printf("---\n"); 192 | #endif 193 | OPENSSL_free(secret); 194 | OPENSSL_free(to_hash); 195 | return true; 196 | } 197 | 198 | Petya_t choose_variant() 199 | { 200 | printf("Choose one of the supported variants:\nr - Red Petya\ng - Green Petya or Mischa\nd - Goldeneye\n"); 201 | printf("[*] My petya is: "); 202 | char code = getchar(); 203 | Petya_t type = PETYA_UNK; 204 | switch (code) { 205 | case 'r': type = PETYA_RED; break; 206 | case 'g': type = PETYA_GREEN; break; 207 | case 'd': type = PETYA_GOLDEN; break; 208 | default: type = PETYA_UNK; break; 209 | }; 210 | return type; 211 | } 212 | 213 | //input variant #1: read the victim ID from file 214 | char* fetch_file_input(const char* filename, Petya_t &my_petya) 215 | { 216 | FILE *fp = fopen(filename, "rb"); 217 | if (!fp) { 218 | printf("Cannot open victim's file: %s\n", filename); 219 | return NULL; 220 | } 221 | my_petya = choose_variant(); 222 | if (my_petya == PETYA_UNK) { 223 | printf("Invalid param!\n"); 224 | return NULL; 225 | } 226 | printf("Victim file: %s\n", filename); 227 | fseek(fp, 0, SEEK_END); 228 | size_t file_size = ftell(fp); 229 | #ifdef DEBUG 230 | printf("---\n"); 231 | printf("file_size: %d = %#x\n", file_size, file_size); 232 | #endif 233 | fseek(fp, 0, SEEK_SET); 234 | 235 | const size_t max_line = 0x100; 236 | char *line = new char[max_line]; 237 | if (line) { 238 | fgets(line, max_line, fp); 239 | } 240 | fclose(fp); 241 | return line; 242 | } 243 | 244 | //input variant #2: read the victim ID from the infected disk 245 | char* fetch_disk_input(const char* filename, Petya_t &my_petya) 246 | { 247 | FILE *fp = fopen(filename, "rb"); 248 | if (fp == NULL) { 249 | printf("Cannot open file %s\n", filename); 250 | return NULL; 251 | } 252 | if (is_infected(fp)) { 253 | printf("[+] Petya FOUND on the disk!\n"); 254 | } else { 255 | printf("[-] Petya not found on the disk!\n"); 256 | return NULL; 257 | } 258 | my_petya = choose_variant(); 259 | if (my_petya == PETYA_UNK) { 260 | printf("Invalid param!\n"); 261 | return NULL; 262 | } 263 | size_t onion_sector_num = REDGREEN_ONION_SECTOR_NUM; 264 | if (my_petya == PETYA_GOLDEN) { 265 | onion_sector_num = GOLDEN_ONION_SECTOR_NUM; 266 | } 267 | char *victim_id = fetch_victim_id(fp, onion_sector_num); 268 | return victim_id; 269 | } 270 | 271 | int main(int argc, char* argv[]) 272 | { 273 | Petya_t my_petya = PETYA_UNK; 274 | if (argc < 2) { 275 | #ifdef DISK_INPUT 276 | printf("[-] Parameter missing! Supply the infected disk!\n"); 277 | #else 278 | printf("[-] Parameter missing! Supply a file containing the ID from the victim\n"); 279 | #endif 280 | 281 | #ifdef _WINDOWS 282 | system("pause"); 283 | #endif 284 | return -1; 285 | } 286 | #ifdef DISK_INPUT 287 | char* victim_id = fetch_disk_input(argv[1], my_petya); 288 | #else 289 | char* victim_id = fetch_file_input(argv[1], my_petya); 290 | #endif 291 | if (victim_id == NULL) { 292 | printf("Failed to load victim ID\n"); 293 | return -1; 294 | } 295 | printf("[+] Victim ID: %s\n", victim_id); 296 | //the private key: 297 | 298 | //create a keypair basing on the private key: 299 | EC_KEY *key = bbp_ec_new_keypair(priv_bytes); 300 | if (!key) { 301 | puts("Unable to create keypair"); 302 | #ifdef _WINDOWS 303 | system("pause"); 304 | #endif 305 | return -1; 306 | } 307 | 308 | const BIGNUM *priv_bn = EC_KEY_get0_private_key(key); 309 | if (!priv_bn) { 310 | puts("Unable to decode private key"); 311 | #ifdef _WINDOWS 312 | system("pause"); 313 | #endif 314 | return -1; 315 | } 316 | #ifdef DEBUG 317 | uint8_t priv[PRIV_KEY_SIZE]; 318 | BN_bn2bin(priv_bn, priv); 319 | bbp_print_hex("priv: ", priv, sizeof(priv)); 320 | #endif 321 | 322 | uint8_t session_pub[PUBLIC_KEY_LEN] = { 0 }; 323 | uint8_t salsa_key[AES_CHUNK_LEN] = { 0 }; 324 | if (!load_victim_data(victim_id, session_pub, salsa_key, my_petya)) { 325 | printf("Failed loading victim's data!\n"); 326 | #ifdef _WINDOWS 327 | system("pause"); 328 | #endif 329 | return -1; 330 | } 331 | 332 | const EC_KEY *session_key = load_session_key(session_pub); 333 | if (session_key == NULL) { 334 | printf("Cannot load victim's public key!\n"); 335 | #ifdef _WINDOWS 336 | system("pause"); 337 | #endif 338 | return -1; 339 | } 340 | const EC_POINT *pub_key = EC_KEY_get0_public_key(session_key); 341 | if (pub_key == NULL) { 342 | printf("Cannot fetch victim's public key!\n"); 343 | #ifdef _WINDOWS 344 | system("pause"); 345 | #endif 346 | return -1; 347 | } 348 | //----- 349 | #ifdef DEBUG 350 | bbp_print_hex("enc. Salsa: ", salsa_key, AES_CHUNK_LEN); 351 | #endif 352 | uint8_t sha512_buffer[SHA512_DIGEST_LENGTH] = {0}; 353 | if (!derive_secret_hash(pub_key, key, sha512_buffer)) { 354 | printf("Cannot derive the secret!\n"); 355 | return -1; 356 | } 357 | 358 | aes_decrypt_chunk(salsa_key, sha512_buffer); 359 | #ifdef DEBUG 360 | bbp_print_hex("de-AES Salsa: ", salsa_key, AES_CHUNK_LEN); 361 | #endif 362 | printf("---\n"); 363 | int res = -1; 364 | if (my_petya == PETYA_GOLDEN) { 365 | bbp_print_hex("[+] Your key ", salsa_key, AES_CHUNK_LEN); 366 | res = 0; //success 367 | } 368 | else if (my_petya == PETYA_RED || my_petya == PETYA_GREEN) { 369 | xor_buffer(salsa_key, AES_CHUNK_LEN, session_pub, PUBLIC_KEY_LEN); 370 | #ifdef DEBUG 371 | bbp_print_hex("de-XOR Salsa: ", salsa_key, AES_CHUNK_LEN); 372 | #endif 373 | printf("[+] Your key : %.16s\n", salsa_key); 374 | res = 0; //success 375 | } 376 | //----- 377 | //cleanup: 378 | // release keypair 379 | EC_KEY_free(key); 380 | delete []victim_id; 381 | #ifdef _WINDOWS 382 | system("pause"); 383 | #endif 384 | return res; 385 | } 386 | 387 | 388 | --------------------------------------------------------------------------------