├── LICENSE ├── NemucodFR.py ├── NemucodKE.py ├── NemucodPT.py ├── NemucodRE.py └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 @Antelox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NemucodFR.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Nemucod File Recovery 4 | 5 | Coded by Antelox 6 | Twitter: @Antelox 7 | UIC R.E. Academy - quequero.org 8 | Version: 0.36 - 06/22/2016 9 | 10 | Released under MIT License 11 | 12 | This script is able to recover files encrypted by the Nemucod Ransomware (the latest variants). 13 | 14 | *NOTE*: This script doesn't handle the 7-Zip variant. 15 | 16 | *NOTE*: You must use Python 2.7.* not 3+ 17 | 18 | *NOTE*: If you have problems with mapped drives you probably don't actually have that drive mapped under the elevated user. 19 | Try running with a UNC path instead. 20 | 21 | For more information about the last Nemucod variants you can take a look at: 22 | 23 | - https://glot.io/snippets/ee7hiif87k 24 | - http://www.bleepingcomputer.com/forums/t/608045/crypted-ransomware-nemucod-decrypttxt-support-and-help-topic/page-11#entry4005316 25 | - http://www.bleepingcomputer.com/forums/t/608045/crypted-ransomware-nemucod-decrypttxt-support-and-help-topic/page-12#entry4016916 26 | - https://reaqta.com/2016/06/nemucod-meets-php/ 27 | 28 | 29 | DISCLAIMER 30 | Use this script at your own risk. I'm not responsible for any data loss! 31 | Regardless, this script doesn't overwrite and/or delete any files and creates new files with the decrypted data. 32 | Please remove the .crypted files after you have verified the recovered files are correct either manually or using NemucodRE.py. 33 | 34 | """ 35 | 36 | import os 37 | import sys 38 | import ast 39 | 40 | def decrypt(file, key, hsize): 41 | try: 42 | fencrypted = open(file, 'rb') 43 | fkey = open(key,"rb") 44 | key1 = ast.literal_eval(fkey.read()) 45 | content_encrypted = fencrypted.read() 46 | filesize = len(content_encrypted) 47 | 48 | recovered = '' 49 | full_recovered = '' 50 | 51 | if filesize <= hsize: 52 | for i in range(0,filesize): 53 | recovered = recovered + chr(ord(content_encrypted[i])^key1[i%len(key1)]) 54 | full_recovered = recovered 55 | else: 56 | for i in range(0,hsize): 57 | recovered = recovered + chr(ord(content_encrypted[i])^key1[i%len(key1)]) 58 | full_recovered = recovered + content_encrypted[hsize:] 59 | 60 | open(file[:-8], 'wb').write(full_recovered) 61 | fencrypted.close() 62 | print "[+] File \"%s\" recovered succesfully!\n" % file[:-8] 63 | return 0 64 | except: 65 | print "An error has occured!" 66 | 67 | #main 68 | print "\n*Nemucod File Recovery*\n" 69 | 70 | if len(sys.argv) == 4: 71 | for subdir, dirs, files in os.walk(str(sys.argv[1])): 72 | for file in files: 73 | if file.endswith('.crypted'): 74 | full_path = os.path.join(subdir, file) 75 | decrypt(full_path, sys.argv[2], int(sys.argv[3])) 76 | else: 77 | print "*ERROR*: An incorrect number of arguments were passed to this script!\n" 78 | print "Example: python NemucodFR.py path key_file header_size\n" 79 | print "path: A path to a folder which contains files encrypted by Nemucod with .crypted extensions;\n" 80 | print "key_file: The file which contains the recovered key by NemucodKE.py script;\n" 81 | print "header_size: Size, from the beginning of the file, which was encrypted - info provided by NemucodKE.py script." 82 | -------------------------------------------------------------------------------- /NemucodKE.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Nemucod Key Extractor 4 | 5 | Coded by Antelox 6 | Twitter: @Antelox 7 | UIC R.E. Academy - quequero.org 8 | Version: 0.36 - 06/22/2016 9 | 10 | Released under MIT License 11 | 12 | This script is able to recover files encrypted by the Nemucod Ransomware (the latest variants). 13 | 14 | *NOTE*: This script doesn't handle the 7-Zip variant. 15 | 16 | *NOTE*: You must use Python 2.7.* not 3+ 17 | 18 | For more information about the last Nemucod variants you can take a look at: 19 | 20 | - https://glot.io/snippets/ee7hiif87k 21 | - http://www.bleepingcomputer.com/forums/t/608045/crypted-ransomware-nemucod-decrypttxt-support-and-help-topic/page-11#entry4005316 22 | - http://www.bleepingcomputer.com/forums/t/608045/crypted-ransomware-nemucod-decrypttxt-support-and-help-topic/page-12#entry4016916 23 | - https://reaqta.com/2016/06/nemucod-meets-php/ 24 | 25 | 26 | DISCLAIMER 27 | Use this script at your own risk. I'm not responsible for any data loss! 28 | Regardless, this script will only print the extracted key. 29 | ***NO FILE OPERATIONS WILL BE PERFORMED*** 30 | 31 | """ 32 | 33 | import sys 34 | 35 | print "\n*Nemucod Key Extractor*\n" 36 | 37 | if len(sys.argv) == 3: 38 | fencrypted = open(sys.argv[1],'rb') 39 | fplain = open(sys.argv[2],'rb') 40 | 41 | content_encrypted = fencrypted.read() 42 | content_plain = fplain.read() 43 | 44 | if content_encrypted[0]=='7' and content_encrypted[1]=='z': 45 | print "*INFO*: A 7zip variant detected! Unfortunately this script can not handle it." 46 | sys.exit() 47 | 48 | if content_encrypted[1535] == content_plain[1535] and content_encrypted[2000] == content_plain[2000]: 49 | print "*INFO*: Encrypted header size = 1024 bytes\n" 50 | print "*INFO*: Due to different Nemucod variants the key length can be as follows:\n" 51 | print "*INFO*: - 36 bytes\n" 52 | print "*INFO*: - 1024 bytes\n" 53 | print "*INFO*: - 102 bytes\n" 54 | print "*INFO*: Run this script again passing as an argument the key size.\n" 55 | print "*INFO*: For example - python NemucodKE.py encrypted original 102" 56 | else: 57 | print "*INFO*: Encrypted header size = 2048 bytes and key size = 255 bytes\n" 58 | key_size = 255 59 | 60 | key = '' 61 | 62 | for i in range(0,key_size): 63 | if i == key_size - 1: 64 | key = key + hex(ord(content_encrypted[i])^ord(content_plain[i])) 65 | else: 66 | key = key + hex(ord(content_encrypted[i])^ord(content_plain[i])) + ", " 67 | 68 | print "\n*KEY FOUND*: File \"key_2048_255.txt\" has been created." 69 | open("key_2048_255.txt", "wb").write("[" + key + "]") 70 | 71 | fencrypted.close() 72 | fplain.close() 73 | sys.exit() 74 | 75 | elif len(sys.argv) == 4: 76 | fencrypted = open(sys.argv[1],'rb') 77 | fplain = open(sys.argv[2],'rb') 78 | 79 | content_encrypted = fencrypted.read() 80 | content_plain = fplain.read() 81 | 82 | key = '' 83 | key_size = int(sys.argv[3]) 84 | for i in range(0,key_size): 85 | if i == key_size-1: 86 | key = key + hex(ord(content_encrypted[i])^ord(content_plain[i])) 87 | else: 88 | key = key + hex(ord(content_encrypted[i])^ord(content_plain[i])) + ", " 89 | 90 | print "\n*KEY FOUND*: File \"key_1024_%s.txt\" has been created." % str(sys.argv[3]) 91 | open("key_1024_" + str(sys.argv[3]) + ".txt", "wb").write("[" + key + "]") 92 | 93 | fencrypted.close() 94 | fplain.close() 95 | 96 | else: 97 | print "*ERROR*: An incorrect number of arguments were passed to this script!\n" 98 | print "Example: python NemucodKE.py encrypted original\n" 99 | print "encrypted: A file encrypted by Nemucod with .crypted extension;\n" 100 | print "original: The same file as the .crypted file pulled from a backup or email for example." 101 | -------------------------------------------------------------------------------- /NemucodPT.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Nemucod Path Tester 4 | 5 | Coded by Alex 6 | Twitter: @AlexJamesHaines 7 | Freedom I.T. Systems LTD - freedomitsolutions.co.uk 8 | Version: 0.36 - 06/22/2016 9 | 10 | Released under MIT License 11 | 12 | This script helps troubleshoot errors with paths that may break the execution of the other scripts. 13 | 14 | *NOTE*: You must use Python 2.7.* not 3+ 15 | 16 | *NOTE*: If you have problems with mapped drives you probably don't actually have that drive mapped under the elevated user. 17 | Try running with a UNC path instead. 18 | 19 | 20 | DISCLAIMER 21 | Use this script at your own risk. I'm not responsible for any data loss! 22 | Regardless, this script doesn't overwrite and/or delete any files. 23 | 24 | """ 25 | 26 | import os 27 | import sys 28 | 29 | print "\n*Nemucod Path Tester*\n" 30 | 31 | if len(sys.argv) == 2: 32 | 33 | path = sys.argv[1] 34 | dir, filen = os.path.split(path) 35 | 36 | print "Directory:", dir 37 | if os.path.exists(dir): 38 | print "Directory found - yay :)\n" 39 | else: 40 | print "Directory can not be found - boo :(\n" 41 | sys.exit() 42 | 43 | if not filen: 44 | print "No filename provided!!!" 45 | sys.exit() 46 | 47 | print "Filename:", filen 48 | if os.path.exists(path): 49 | print "Filename found - yay :)\n" 50 | else: 51 | print "Filename can not be found - boo :(\n" 52 | print "Directory Contents:" 53 | for fn in os.listdir(dir): 54 | print fn 55 | 56 | else: 57 | print "*ERROR*: An incorrect number of arguments were passed to this script!\n" 58 | print "Example: python NemucodPT.py path\n" 59 | print "path: A path to a folder which contains the target file.\n" 60 | -------------------------------------------------------------------------------- /NemucodRE.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Nemucod Remove Encrypted 4 | 5 | Coded by Alex 6 | Twitter: @AlexJamesHaines 7 | Freedom I.T. Systems LTD - freedomitsolutions.co.uk 8 | Version: 0.36 - 06/22/2016 9 | 10 | Released under MIT License 11 | 12 | This script helps remove the left over .crypted files once you are happy they have been recovered. 13 | 14 | *NOTE*: You must use Python 2.7.* not 3+ 15 | 16 | *NOTE*: If you have problems with mapped drives you probably don't actually have that drive mapped under the elevated user. 17 | Try running with a UNC path instead. 18 | 19 | REFERENCES 20 | http://code.activestate.com/recipes/577058 for the user query code. 21 | 22 | DISCLAIMER 23 | Use this script at your own risk. I'm not responsible for any data loss! 24 | 25 | """ 26 | 27 | import os 28 | import sys 29 | import ast 30 | 31 | def delete(file): 32 | try: 33 | os.remove(file) 34 | print "[-] File \"%s\" deleted succesfully!\n" % file[:-8] 35 | return 0 36 | except: 37 | print "An error has occured!" 38 | 39 | def query_yes_no(question, default="no"): 40 | valid = {"yes":"yes", "y":"yes", "ye":"yes", 41 | "no":"no", "n":"no"} 42 | if default == None: 43 | prompt = " [y/n] " 44 | elif default == "yes": 45 | prompt = " [Y/n] " 46 | elif default == "no": 47 | prompt = " [y/N] " 48 | else: 49 | raise ValueError("Invalid default answer: '%s'" % default) 50 | 51 | while 1: 52 | sys.stdout.write(question + prompt) 53 | choice = raw_input().lower() 54 | if default is not None and choice == '': 55 | return default 56 | elif choice in valid: 57 | return valid[choice] 58 | else: 59 | sys.stdout.write("Please respond with 'yes' or 'no' "\ 60 | "(or 'y' or 'n').\n") 61 | 62 | #Main 63 | print "\n*Nemucod Remove Encrypted*\n" 64 | 65 | answer = query_yes_no("Are you sure that you want to continue to remove all .crypted files from your system?") 66 | if answer == "no" : 67 | print "\nOK - Quitting now...\n" 68 | sys.exit() 69 | 70 | if len(sys.argv) == 2: 71 | for subdir, dirs, files in os.walk(str(sys.argv[1])): 72 | for file in files: 73 | if file.endswith('.crypted'): 74 | full_path = os.path.join(subdir, file) 75 | delete(full_path) 76 | else: 77 | print "*ERROR*: An incorrect number of arguments were passed to this script!\n" 78 | print "Example: python NemucodRE.py path\n" 79 | print "path: A path to a folder which contains files encrypted by Nemucod with .crypted extensions;\n" 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NemucodFR 2 | Extract the key and use it to recover files that have been excrypted by Nemucod Ransomware 3 | 4 | - **NemucodKE.py** - the python script to extract the key (*KE* means Key Extractor); 5 | - **NemucodFR.py** - the python script to recover the encrypted file (*FR* means File Recovery); 6 | - **NemucodPT.py** - the python script to test paths in case of errors (*PT* means Path Tester); 7 | - **NemucodRE.py** - the python script to remove the encrypted file (*RE* means Remove Encrypted). 8 | 9 | *NOTE*: You must use Python 2.7.* not 3+ 10 | 11 | # Changelog 12 | 13 | FIRST COMMIT: 05/01/2016 - ver. 0.1 14 | 15 | In the last variant of **Nemucod Ransomware**, discovered by *ReaQta's R&D Team* [1], the file is encrypted through a custom xorer that XORs the first *1024* bytes of each targeted file with a *36* bytes long randomly generated key. In the previous two Nemucod variants, you could see first a xor operation like the one found in this variant but with an hardcoded key (so not randomly generated) inside the executabled downloaded by the Javascript code [2]. Then Nemucod has replaced the xor encryption process with 7zip CLI version [3]. Now it's come back with the xor encryption approach. 16 | 17 | UPDATE: 05/22/2016 - ver. 0.2 18 | 19 | Found in the wild by [@demonslay335](https://twitter.com/demonslay335) a new Nemucod variant which encrypts first 2048 bytes with a key of 255 bytes. The script handles also this one. 20 | 21 | UPDATE: 06/11/2016 - ver. 0.3 22 | 23 | 2 new Nemucod variants discovered: this time the header size is 1024 bytes but key length changes and can be: 1024 bytes [4] or 102 bytes [5]. The script now handles also these ones. 24 | 25 | UPDATE: 06/15/2016 - ver. 0.35 26 | 27 | NemucodKE.py saves the text file which contains the key with different names based on the header/key length. 28 | NemucodFR.py searches recursively files and folders from the path passed as argument (open the command prompt as Administrator). 29 | 30 | UPDATE: 06/22/2016 - ver. 0.36 31 | 32 | NemucodPT.py helps troubleshoot errors with paths that may break the execution of the other scripts. 33 | NemucodRE.py searches recursively files and folders from the path passed as argument (open the command prompt as Administrator). 34 | Language tweaks all round. 35 | 36 | # References 37 | 38 | [1] https://glot.io/snippets/ee7hiif87k 39 | 40 | [2] https://blog.fortinet.com/post/nemucod-adds-ransomware-routine 41 | 42 | [3] https://reaqta.com/2016/04/nemucod-meets-7zip-to-launch-ransomware 43 | 44 | [4] http://www.bleepingcomputer.com/forums/t/608045/crypted-ransomware-nemucod-decrypttxt-support-and-help-topic/page-12#entry4016916 45 | 46 | [5] https://reaqta.com/2016/06/nemucod-meets-php/ 47 | 48 | # How to use these scripts 49 | These scripts are able to extract the key first, and recover whole encrypted data then ONLY AND ONLY IF you have one *exact* backup copy of the file that was encrypted. 50 | 51 | _Example scenario_ 52 | 53 | A friend, a girlfriend or any others sent you an email with *documents.zip* file in attachment. You download this file on your computer and extract the whole content to work on. After a couple of hours/days you get, unfortunately, infected by Nemucod Ransomware and your *documents.zip* file (as well as others files) was encrypted and renamed *documents.zip.crypted*. At this point you can use the **NemucodKE.py** python script to extract the key in this manner: 54 | 55 | Open a command prompt as Administrator. If you don't know how to do it see [here](https://technet.microsoft.com/en-us/library/cc947813(v=ws.10).aspx) 56 | 57 | Move inside the NemucodFR folder cloned from this repository and do: 58 | 59 | *python NemucodKE.py documents.zip.crypted documents.zip* 60 | 61 | Obviously you have the original *documents.zip* file copy inside the email server! 62 | 63 | As a result of the script, based on the Nemucod variant, you will get: 64 | 65 | 1) If the Nemucod variant is the one with header size of 2048 bytes and the key size of 255 bytes, the _key\_2048\_255.txt_ file which you can pass it as command line argument to the NemucodFR.py script (see below); 66 | 67 | 2) If the Nemucod variant is the one with header size of 1024 bytes then could be 3 different key sizes: 68 | - 36 bytes; 69 | - 1024 bytes; 70 | - 102 bytes. 71 | 72 | So you have to make 3 attempts, one for each key size. 73 | For example you can try: 74 | 75 | *python NemucodKE.py encrypted original 102* 76 | 77 | at this point you get the _key\_1024\_102.txt_ file and you can use it together with NemucodFR.py script (see below). If the file recovered with this key size is not equal to the orginal one then repeat the same operation but with a different key size. 78 | 79 | 80 | After that you got the right key text file, you can use the **NemucodFR.py** script in this manner: 81 | 82 | *python NemucodFR.py path key_file header_size* 83 | 84 | where: 85 | 86 | - *path* is the path to a folder which contains the .crypted files encrypted and renamed by Nemucod Ransomware. The script, starting from the path passed as argument, will iterate recursively files and sub-directories to search for .crypted files, so make sure that the key is the right one before to run this script, for example, from the C:\\ (more info [here](https://github.com/Antelox/NemucodFR#how-to-know-if-the-script-has-extracted-the-right-key)). If you want to make a try about the paths (mapped drives on your network, etc.), before that you run this script, you can use the **NemucodPT.py** script in this manner: 87 | 88 | *python NemucodPT.py path_to_test* 89 | 90 | - *key_file* the file which contains the recovered key by NemucodKE.py script. It can be the following ones: 91 | 92 | _key\_1024\_36.txt_ 93 | 94 | _key\_1024\_102.txt_ 95 | 96 | _key\_1024\_1024.txt_ 97 | 98 | _key\_2048\_255.txt_ 99 | 100 | - *header_size* is the size, from beginning of the file, which it was encrypted - provided by NemucodKE.py script. It can bethe following ones: 101 | 102 | _1024_ 103 | 104 | _2048_ 105 | 106 | As results we will get the decrypted files (I hope) :D 107 | 108 | If it's all ok and you want to delete .crypted files from your drives, you can use **NemucodRE.py** script in this manner: 109 | 110 | *python NemucodRE.py "C:\"* 111 | 112 | with this command you will delete whole .crypted files on drive C. 113 | 114 | *NOTE*: Execute this command ONLY after that you have recovered the files!!! 115 | 116 | Of course this is only an example. Any files of which you have a backup copy on external device and/or in a cloud storage service will be fine, the important is that it's the same one that was encrypted. 117 | 118 | # How to know if the script has extracted the right key 119 | It's really simple. Before you start to decrypt your data, you can create a "*test*" folder in which you can copy an encrypted file. Make your try with the file inside "*test*" folder and if you will see your original file, so it's the right key. Of course if you make a try and the file is not recovered then, before that you start to make another try, delete first the file inside "*test*" folder and copy again the encrypted file inside it. 120 | 121 | # Note 122 | I thought this script as an additional possibility that a Nemucod's victim can have in addition to other already known solutions. 123 | 124 | # Disclaimer 125 | *Use these scripts at your own risk. I'm not responsible for any data loss! 126 | Regardless, the main scripts don't overwrite and/or delete any files and create new files with the decrypted data. 127 | Only the NemucodRE.py script removes data from your devices.* 128 | 129 | # License 130 | See the LICENSE file for license rights and limitations (*MIT*). 131 | --------------------------------------------------------------------------------