after so many articles( 1 - 2 - 3 ) about my research on this Cr1ptor ransomware finally there is a tiny way to decrypt your files.
SPOILER ALERT:
This is a very early alpha release, is destined to programmers not directly to the victims.
Calm down, this will not be quick and/or easy at all but there is only a theorical chance. Probably you'll need few months, years, your son's life of computational work to brute the key. This is not a solution.
Let's start from the beginning:
as I wrote in the last article I got chance to have a pair of valid keys to run some tests on my Raspberry PI VM.
Before to talk about the source code, I need you to focus on the encrypted files's structure:
Basically this ransomware append after encryption 0x7A bytes. This is important because of this:
This is how the Decryption routine looks initially (where I made some gusses..):
Studying the code from here is completely INSANE..........well, I did it anyway (tens hours of hard work) helped by the libsodium documentation I figured out the exact pseudo code of the decompilation routine built in the ransomware and what do it exactly do.
After a few IDA corrections here is where I landed:
Starting from here, I've ported the code into a C application to reproduce the decryption.Once figured out which kind of encryption the ransomware adopted, I've started to write a C program and from the libsodium documentation there was something interesting:
Well, this looks similar to our ransomware implementation, except for the fact that he's doing some manipulation on the top of the pseudo code, in fact this code example is not sufficient.
A sealed box implementation seems to anticipate the code we seen:
Good. We now have so many pieces of the puzzle. Its time to put them togheter.
What do we need to decrypt the files?
Take a closer look at the "crypto_box_seal_open" function.
Do you remember the encrypted structure?
CIPHERTEXT_LEN, from the bottom of file is 0x50. We have it.
recipient_pk, from the bottom of the file and is 0x20. We have it.
recipient_sk, f
The result decrypted array is then used to decrypt the rest of the file more or less as decribed on the libsodium documentation secret-key_cryptography -> "Stream encryption/file encryption" on github.
To procede with
crypto_secretstream_xchacha20poly1305_init_pull(&st, header, key) != 0)we need the header and the key. The header is actually stored into the encrypted file as the same as the example shows. So we have it.
the key....the key is the "decrypted" crypto_box_seal_open resoult! We have it.
Since the fact I had a working keypair, I had everything I need to run some tests with the good old DEV-C++ IDE.
Once set up the code, I found a very strange behaviour of libsodium which brings me to a correct decryption with 3 different private keys!(!?!?!?!?!?) O.o
Is due to a libsodium bug?! IDK!
I hope some of you knows (and tells me) the reason of such behaviour, by the way victims does not have the private key and this strange behaviour of libsodium motivated me to implement a brute force routine into the code (to MAYBE find a working decryption sk with humanly acceptable timing).
And here is the result (skipping the brute force routine)!
(and yes, the original file was filled by a junky 0xAA 😊)Here is the main source (I do not share the encryption routine to avoid a Cr1pt0r x86 porting)
#include#include #include #include #include #include #include #include #define UINT64_MAX (18446744073709551615ULL) #define CHUNK_SIZE 4096 #define crypto_stream_chacha20_ietf_KEYBYTES 32U void rvereseArray(unsigned char *arr, int start, int end) { while (start < end) { unsigned char temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } } void printArray(unsigned char arr[], int size) { int i; for (i=0; i < size; i++) printf("%02x ", arr[i]); printf("\n"); } static int decrypt(const char *target_file, const char *source_file, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { unsigned char buf_in[CHUNK_SIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; unsigned char buf_out[CHUNK_SIZE]; unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; crypto_secretstream_xchacha20poly1305_state st; FILE *fp_t, *fp_s, *fp_s1; unsigned long long out_len; size_t rlen; int eof; int ret = -1; unsigned char tag = 0x0; #define MESSAGE (const unsigned char *) "Message" #define MESSAGE_LEN 15 #define CIPHERTEXT_LEN (crypto_box_SEALBYTES + MESSAGE_LEN) unsigned char recipient_pk[crypto_box_PUBLICKEYBYTES]; //My pk //unsigned char recipient_pk[crypto_box_PUBLICKEYBYTES];={0x3D , 0x3F , 0x78 , 0x63 , 0x3E , 0xA6 , 0xA7 , 0x99 , 0xC4 , 0xDC , 0xF2 , 0x52 , 0x2D , 0x90 , 0x21 , 0xC5 , 0x10 , 0x31 , 0xDE , 0x6B , 0xA3 , 0xEB , //0xCF , 0x06 , 0x1C , 0xC5 , 0xCA , 0xF8 , 0xF8 , 0x43 , 0xC5 , 0x2F};//; /* Bob's public key */ // //recipient_sk decrypt the files also with recipient_sk[0]=0xDB (the original byte) than 0xDD and also 0xDC // unsigned char recipient_sk[crypto_box_SECRETKEYBYTES]={ 0xDB , 0xA2 , 0xD4 , 0x74 , 0xC0 , 0xB7 , 0x2B , 0x62 , 0x0E , 0xCD , 0xC8 , 0x7F , 0x43 , 0xEA , 0xAB , 0x2E , 0x24 , //0x65 , 0x00 , 0x91 , 0x74 , 0xDC , 0x03 , 0xB4 , 0x22 , 0xC8 , 0x48 , 0x30 , 0x1F , 0x19 , 0xDD , 0x78 }; //; /* Bob's secret key */ unsigned char recipient_sk[32]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; unsigned char ciphertext[80]; //var50h long filelen; fp_s1 = fopen("enc", "rb"); if(fp_s1==0){ printf("Encrypted files not found"); return 1;} fseek(fp_s1, 0xFFFFFFD6, SEEK_END); //filelen = ftell(fp_s1); fread(recipient_pk, 1, 0x20, fp_s1); fseek(fp_s1, 0xFFFFFF86, SEEK_END); fread(ciphertext, 1, 0x50, fp_s1); //because the new key is 32dec bytes long unsigned char decrypted[32]; //brute start //Bruteforce the 32byte key. Thanks to GeDaMo from IRC ##programming @ freenode // uint64_t i[4] = { 0, 0,0,0 }; // uint64_t i[4] = {0xDBA2D474C0B72B62 , 0x0ECDC87F43EAAB2E , 0x2465009174DC03B4 , 0x22C848301F19DD78 }; int exit=0; printf("DE-Cr1pt0r Tool By RE-Solver @solver_re:\r\n Bruteforcing \r\n "); do { i[1] = 0; do { i[2] = 0; do { i[3] = 0; do { memcpy(&recipient_sk[23],&i[3],sizeof(i[3])); rvereseArray(&recipient_sk[23],0,8); memcpy(&recipient_sk[15],&i[2],sizeof(i[2])); rvereseArray(&recipient_sk[15],0,8); memcpy(&recipient_sk[7],&i[1],sizeof(i[1])); rvereseArray(&recipient_sk[7],0,8); memcpy(&recipient_sk,&i[0],sizeof(i[0])); rvereseArray(recipient_sk,0,7); //printArray(recipient_sk, 32); if(crypto_box_seal_open(decrypted, ciphertext, 0x50u, recipient_pk, recipient_sk) == 0) {printf("Found: ");printArray(recipient_sk, 32);exit=1;break;} } while (i[3]++ < UINT64_MAX&&exit==0); } while (i[2]++ < UINT64_MAX&&exit==0); } while (i[1]++ < UINT64_MAX&&exit==0); } while (i[0]++ < UINT64_MAX&&exit==0); //END brute //from the decompilated program this was the original routine, because of the bruteforce is added as a comment now /* if (crypto_box_seal_open(decrypted, ciphertext, 0x50u, recipient_pk, recipient_sk) != 0) { // message corrupted or not intended for this recipient printf ("message corrupted or not intended for this recipient %s",decrypted);} */ fp_s = fopen(source_file, "rb"); fp_t = fopen(target_file, "wb"); if(fp_s==0 || fp_t==0){printf("Encrypted files not found"); goto ret;} fread(header, 1, 0x18, fp_s); if (crypto_secretstream_xchacha20poly1305_init_pull(&st, header, decrypted) != 0) { goto ret; /* incomplete header */ } do { rlen = fread(buf_in, 1, 0x1011, fp_s); eof = feof(fp_s); //tag = 0x0; int value=crypto_secretstream_xchacha20poly1305_pull(&st, buf_out, &out_len, &tag, buf_in, rlen, NULL,0); if (value != 0) { goto ret; /* corrupted chunk */ } if (tag == 3 && ! eof) { //crypto_secretstream_xchacha20poly1305_TAG_FINAL -> 3 goto ret; /* premature end (end of file reached before the end of the stream) */ } fwrite(buf_out, 1, (size_t) out_len, fp_t); } while (! eof); ret = 0; ret: fclose(fp_t); fclose(fp_s); fclose(fp_s1); return ret; } int main(void) { unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]; if (sodium_init() != 0) { return 1; } crypto_secretstream_xchacha20poly1305_keygen(key); if (decrypt("enc.outtmp", "enc.tmp", 0x0) != 0) { printf("Something goes wrong."); return 1; } printf("Decrypted! RE Solver"); return 0; }
IDE: DEV-C++
libsodium library: libsodium-1.0.17-mingw.tar.gz
Remember to link the library into the Project/Project Options
Compiled tool: https://www.sendspace.com/file/275c70
sha256: 4066fa0d402a8458f7784e89ba979929ee1d7efd761b3cabe9705784aa8af865
usage: Copy an encrypted file into the same folder of the tool and rename it as enc (with no extensions). Copy the same encrypted file and rename it as enc.tmp and strip the last 0x7A from the end of the file. If you're lucky within some weeks you'll have the key printed on the console and the encrypted.outtmp decrypted file created on the same folder dir.
Next step: create a file named privkey and write the hex key (with no spaces) into a text file and put it in the Cr1pt0r folder. From the same folder, rename the file pubkey as pubkey_backup and turn on your D-Link nas again.
Note: My GF is waiting me since days, she has been so patient. A special Thanks to her. 😊
I'm sorry but I do not support the tool usage or others kind of requests.Since the fact that code is released under GPL, everyone can compile, improve, modify the code. (And I hope it happens).
Follow me on Twitter @solver_re
Hire me! Job offers are welcome.
Cheers,
RE Solver
Thanks for this! I really appreciate the work you are doing.
RispondiEliminaI only use linux, and it looks like this is only for windows? Do you have a linux version? I'm trying to build it myself, but I'm not finding it easy :(. Are the include statements supposed to have a space in them? gcc doesn't seem to like that.
The libsodium library link doesn't work. I clicked it and it doesn't go anywhere, where can I get that?
Hi,
Eliminalink fixed in the post.
I was using the mingw precompiled ones because of DEV-C++ IDE.
You can choose the right one for you from libsodium github releases:
https://github.com/jedisct1/libsodium/releases
Bye
That's great, thanks! I've got it compiled and running now, and I'm working on getting it multi-threaded for speed, but I'm a little worried I might have made a mistake - any chance you can share your encrypted test file & key, so I can test my work without having to wait for it to run through?
Eliminaenc files here: https://www.sendspace.com/file/b8m06c
Eliminaunsigned char recipient_pk[crypto_box_PUBLICKEYBYTES]={0x3D , 0x3F , 0x78 , 0x63 , 0x3E , 0xA6 , 0xA7 , 0x99 , 0xC4 , 0xDC , 0xF2 , 0x52 , 0x2D , 0x90 , 0x21 , 0xC5 , 0x10 , 0x31 , 0xDE , 0x6B , 0xA3 , 0xEB , 0xCF , 0x06 , 0x1C , 0xC5 , 0xCA , 0xF8 , 0xF8 , 0x43 , 0xC5 , 0x2F};
unsigned char recipient_sk[crypto_box_SECRETKEYBYTES]={ 0xDB , 0xA2 , 0xD4 , 0x74 , 0xC0 , 0xB7 , 0x2B , 0x62 , 0x0E , 0xCD , 0xC8 , 0x7F , 0x43 , 0xEA , 0xAB , 0x2E , 0x24 , 0x65 , 0x00 , 0x91 , 0x74 , 0xDC , 0x03 , 0xB4 , 0x22 , 0xC8 , 0x48 , 0x30 , 0x1F , 0x19 , 0xDD , 0x78 };
Keep up the good work, kind code warrior.
RispondiElimina"and strip the last 0x7A "
RispondiEliminaWhat do you mean? Do I have to remove the "_Cr1ptT0r_" string from my file before feeding it to your software?
Thank you for your work :)
You should remove the last 0x7A bytes wich means the sum of length(_Cr1ptT0r_)+ length(pubkey)+ length(ciphertext)--> 0x0A+0x20+0x50 starting from the end of the file. See the first picture in this post.
EliminaBtw, take a look at https://resolverblog.blogspot.com/2019/03/libsodium-sealed-boxes-multiple-32.html
Starting from the fact that the private keys are only 32 allowed and practically adjacent to each other, does not contribute to reduce the brute force times significantly, therefore brute force remains a theorical approach and is not intended as solution.
You need years of runtime to get the key.
Gosh !
RispondiEliminaI prefer to pay you than the hackers if you find the tool to unencrypt my files !!!
May the force be with you !!!
May the force be with us all!
EliminaI have a lot file crypt and uncrypt and private Key used to crypt to give if this file Can help to résolved this.Small file and large file txt and vidéo and IMG.
RispondiEliminaRegards
You can upload the keys and your nas label picture (mac and serial number). ;)
EliminaHello I've found a file in the ransmonware install folders.
RispondiEliminathe folder is called 'hidden service' and the file is called 'Private_Key'.
he contain the following : (I have remove the key)
-----BEGIN RSA PRIVATE KEY-----
[...]
-----END RSA PRIVATE KEY-----
Can it be usefull for You?
Hello,
RispondiEliminawhen I realized that my files were being encrypted i shutdown my NAS so Cr1ptTor was unable to finish to encrypt all my files (i have encrypted and not encrypted file) .
By searching in Drives of my NAS i found the install folder of Cr1ptT0r and in this folder i've found a repository called "Hidden Service", in this folder there is a file called "Private_Key".
I can provide you if it can be usefull for you to create a decryptor for us !!
Thanks for your Job !
i'm using the decryptor just for try before remove everything, but it takes only 12% of cpu. Could it be powerful?
RispondiEliminaIt's a single-thread application. You should write the appropriate multicore/multithread variant from the source or better with NVIDIA CUDA support.
EliminaBTW the decryptor is not intended to be used like that unless you're available to let it run for the rest of your life :)
i'm just running to see if i'm lucky enough to find it in one week. For testing purposes only.
EliminaThanks anyway for your great job
RispondiEliminaAnd I picked up Cr1ptT0r. Ready to thank for the decryptor
Hi. thank you for your big help. I just need to know the string to write in the command consolle. Is it just DE-Cr1pt0r.exe or DE-Cr1pt0r.exe + enc (or enc.tmp)?
RispondiEliminaThank you Resolver
We are all following it, keep the awesome work!!!
RispondiEliminaHi, I am not able to get it compiled (Win7 32 bits / Dev-C++).
RispondiEliminaCould you by any chance give us your compile command?
Thanks in advance,
Benoit