├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── malconf ├── malwareconfig ├── __init__.py ├── common.py ├── crypto.py ├── decoders │ ├── LostDoor.py │ ├── RedLine.py │ ├── Xtreme.py │ ├── __init__.py │ ├── aar.py │ ├── adwind.py │ ├── adzok.py │ ├── alienspy.py │ ├── alina.py │ ├── arcom.py │ ├── blacknix.py │ ├── blackshades.py │ ├── bluebanana.py │ ├── bozok.py │ ├── clientmesh.py │ ├── cybergate.py │ ├── darkcomet.py │ ├── darkrat.py │ ├── hawkeye.py │ ├── hrat.py │ ├── jbifrost.py │ ├── jrat.py │ ├── luminositylink.py │ ├── luxnet.py │ ├── mirai.py │ ├── nanocore.py │ ├── netwire.py │ ├── njrat.py │ ├── plasma.py │ ├── remcos.py │ ├── saefko.py │ ├── sakula.py │ ├── spynote.py │ └── template.py ├── fileparser.py ├── modules.py ├── preprocessors │ ├── __init__.py │ └── upx.py ├── yaraRules │ ├── AAR.yar │ ├── Adzok.yar │ ├── AlienSpy.yar │ ├── Alina.yar │ ├── Ap0calypse.yar │ ├── Arcom.yar │ ├── Bandook.yar │ ├── BlackNix.yar │ ├── BlackShades.yar │ ├── BlueBanana.yar │ ├── Bozok.yar │ ├── ClientMesh.yar │ ├── CyberGate.yar │ ├── DarkComet.yar │ ├── DarkRAT.yar │ ├── Greame.yar │ ├── HawkEye.yar │ ├── Imminent3.yar │ ├── Infinity.yar │ ├── JavaDropper.yar │ ├── LostDoor.yar │ ├── LuminosityLink.yar │ ├── LuxNet.yar │ ├── NanoCore.yar │ ├── NetWire.yar │ ├── Pandora.yar │ ├── Paradox.yar │ ├── Plasma.yar │ ├── PoisonIvy.yar │ ├── PredatorPain.yar │ ├── Punisher.yar │ ├── PythoRAT.yar │ ├── QRat.yar │ ├── RedLine.yar │ ├── Sakula.yar │ ├── ShadowTech.yar │ ├── SmallNet.yar │ ├── SpyGate.yar │ ├── Sub7Nation.yar │ ├── TrickBot.yar │ ├── UPX.yar │ ├── Vertex.yar │ ├── VirusRat.yar │ ├── Xtreme.yar │ ├── adWind.yar │ ├── hrat.yar │ ├── jRat.yar │ ├── jbifrost.yar │ ├── mirai.yar │ ├── njRat.yar │ ├── remcos.yar │ ├── saefko.yar │ ├── spynote.yar │ ├── unrecom.yar │ ├── xRAT.yar │ └── yaraRules.yar └── yarascanner.py ├── requirements.txt ├── setup.py └── tests ├── __init__.py ├── test_decoders.py ├── test_preprocess.py └── test_yara.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | 64 | 65 | # PyCharms 66 | .idea/ 67 | .c9/ 68 | 69 | 70 | tests/samples -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: required 3 | dist: bionic 4 | group: edge 5 | cache: 6 | pip: true 7 | python: 8 | - 3.6 9 | - 3.8 10 | before_install: 11 | - sudo apt-get update -qq 12 | - sudo apt-get install automake libtool make gcc libmagic-dev -yqq python3-pip unzip upx 13 | - wget https://github.com/VirusTotal/yara/archive/v4.0.0.tar.gz 14 | - tar -xzvf v4.0.0.tar.gz 15 | - cd yara-4.0.0/ && ./bootstrap.sh && ./configure --enable-dotnet --enable-magic 16 | && make && sudo make install && cd ../ 17 | - git clone --recursive https://github.com/VirusTotal/yara-python 18 | - pip3 install pytest codecov pytest-cov 19 | - cd yara-python 20 | - python setup.py build --enable-magic --enable-dotnet 21 | - python setup.py install && cd ../ && rm -rf yara-python && rm -rf yara-4.0.0/ 22 | - wget https://github.com/kevthehermit/malconf-samples/archive/master.zip && unzip master.zip && mv malconf-samples-master tests/samples/ && rm master.zip 23 | install: 24 | - pip install -r requirements.txt 25 | - pip install -e . 26 | script: 27 | - malconf 28 | - pytest -v --cov=malwareconfig 29 | after_success: 30 | - python setup.py sdist 31 | - codecov 32 | deploy: 33 | provider: pypi 34 | user: "__token__" 35 | password: 36 | secure: Uy2IiRInZZngLLOZR0rAEVPmEDHhe/0xLjeROUioARra3DSAtfHnDA6WBrN4QqEelMEcsz5BKqgfN4zhagzJ8Xe3fOCenLWulsHeQCRqfhoJvtAwUhtt21cJ675qopS9DeI21ENxxlyf7KvCGcMnA8W2NlStbvWo+J8eRHjX2OBtulbpnE+AmZfqzEC8Vj32wCKSzzuECnjA+1EZPszotFwuJJiz/TUnX/4ZNh03FKnySnN9ieszpSSfc8rOp0iGaZAm4+K6lePd9ymeTg7C7JIOgoQl1cYKmec+5C4HKS7P8QQ4CZfS6jj0HwmFIa47xS7APFND1F/AczrNZUwIVY7WLvAgWmw/OX6OuJ5zQ3bwGNz27ev/PwZ55FiDU8c8iSiJUAOSvwCqS9TSlXtLjqT9vVhwfWwvjrFXSE+qCrfYzkNittrekqP1QWuowgaIcevKtPLY4i5ClznySPioDek/qhgVy4KCrGtHuV3V1s2Z8/scMLHm0gRCY34TA9AeXLfcnQuL/VPP9LnocCf48bBdqywge+s+bmJVyQoxfrEkYLPGpLXP7eu7SPKJaJzwfYQi+7ULqs/ycCi7GfxHqfqsKbExggQpx/1RVgb7Om5yQ/jrKAf/HCTMl1O8edqjBRlCw5uMx4dxI6cjVMH1dVjfi0qW7OUpK5AAxlHRhoo= 37 | on: 38 | branch: master 39 | skip_existing: true 40 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [1.0.3] - 2019-10-06 10 | ### Added 11 | - Mirai decoder 12 | 13 | ### Changed 14 | - Switched pprint to json indent 15 | 16 | 17 | ## [1.0.2] - 2019-10-06 18 | ### Changed 19 | - Removed print statement from saefko decoder 20 | - Travis badge to .org not .com 21 | - Correct extension to changelog 22 | 23 | ## [1.0.1] - 2019-10-06 24 | ### Added 25 | - This changelog 26 | 27 | ### Fixed 28 | - Travis deploy provider 29 | 30 | ### Changed 31 | - Coverage badge to the correct branch 32 | - Minor changes to readme 33 | 34 | ## [1.0.0] - 2019-10-06 35 | ### Added 36 | - New Library Format. 37 | - travis build. 38 | - unit testing. 39 | - code coverage. 40 | - support for 28 malware families. 41 | 42 | ### Removed 43 | - Standalone decoders 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Kevin Breen 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. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include malwareconfig/yaraRules *.yar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RATDecoders 2 | =========== 3 | 4 | Malconf is a python3 library that can be used to staticly analyse specific malware families and extract the Configuration data that can be used by 5 | Incident Responders during an incident. 6 | 7 | As a library it can also be installed in to automated malware analysis pipelines. 8 | 9 | 10 | ![Coverage](https://codecov.io/gh/kevthehermit/RATDecoders/branch/master/graph/badge.svg "Coverage") 11 | 12 | [![Build Status](https://travis-ci.org/kevthehermit/RATDecoders.svg?branch=master)](https://travis-ci.org/kevthehermit/RATDecoders) 13 | 14 | 15 | ## Installation 16 | 17 | #### Requirements 18 | 19 | There are some pre-reqs that are included in the pip setup and the requirements.txt 20 | 21 | - pefile 22 | - pbkdf2 23 | - javaobj-py3 24 | - pycrypto 25 | - androguard 26 | 27 | For all the decoders you will need yara and yara-python. For dealing with .NET malware you will need to install yara-python with dotnet support 28 | 29 | ##### yara-python with dotnet support 30 | 31 | ``` 32 | git clone --recursive https://github.com/VirusTotal/yara-python 33 | python3 setup.py build --enable-magic --enable-dotnet 34 | sudo python3 setup.py install 35 | ``` 36 | 37 | #### Install from pip 38 | 39 | ``` 40 | pip3 install --upgrade malwareconfig 41 | ``` 42 | 43 | #### Install from repo 44 | 45 | ``` 46 | git clone git@github.com:kevthehermit/RATDecoders.git 47 | cd RATDecoders 48 | pip3 install -r requirements.txt 49 | python3 setup.py install 50 | ``` 51 | 52 | ### Current Rats 53 | Here is a list of the currently supported RATS: 54 | 55 | - LostDoor 56 | - Xtreme 57 | - AAR 58 | - AdWind 59 | - Adzok 60 | - AlienSpy 61 | - Alina 62 | - Arcom 63 | - BlackNix 64 | - BlackShades 65 | - BlueBanana 66 | - Bozok 67 | - ClientMesh 68 | - CyberGate 69 | - DarkComet 70 | - DarkRAT 71 | - HawkEye 72 | - Hrat / hworm / WSH 73 | - Jbifrost 74 | - JRat 75 | - LuminosityLink 76 | - LuxNet 77 | - NanoCore 78 | - NetWire 79 | - njRat 80 | - Plasma 81 | - Remcos 82 | - Saefko 83 | - Sakula 84 | - SpyNote / Mobihook 85 | 86 | ### Upcoming RATS 87 | 88 | - Still migrating old ones! 89 | 90 | ### Usage 91 | 92 | Using the supplied command line tool `malconf` you can pass in a single file or a directory with the `-r` flag and it will attempt to automagically detect the family and extract any config. 93 | 94 | You can also use the `-o` option to write results out to a file. 95 | 96 | 97 | ```malconf``` 98 | 99 | ```malconf -l``` This will list all the supported rats 100 | 101 | ```malconf /path/to/sample ``` This will automagically detect the family and run the decoder 102 | 103 | ``` 104 | ⇒ malconf tests/samples/alienspy 105 | 106 | __ __ _ ____ __ 107 | | \/ | __ _| |/ ___|___ _ __ / _| 108 | | |\/| |/ _` | | | / _ \| '_ \| |_ 109 | | | | | (_| | | |__| (_) | | | | _| 110 | |_| |_|\__,_|_|\____\___/|_| |_|_| 111 | 112 | Malware Configuration Parser by @kevthehermit 113 | 114 | [+] Loading File: tests/samples/alienspy 115 | [-] Found: AlienSpy 116 | [-] Running Decoder 117 | [-] Config Output 118 | 119 | {'ConfigKey': 'fzGUoTaQH3SUW7E82IKQK2J2J2IISIS', 120 | 'NAME': 'ok', 121 | 'Version': 'B', 122 | 'connetion_time': '0', 123 | 'desktop': 'true', 124 | 'dns': '213.208.129.211', 125 | 'extensionname': 'qQJ', 126 | 'folder': 'java', 127 | 'instalar': 'true', 128 | 129 | 130 | ``` 131 | 132 | ### Library 133 | 134 | If you pip install you can also use it is a library. 135 | 136 | ``` 137 | from malwareconfig import fileparser 138 | from malwareconfig.modules import __decoders__, __preprocessors__ 139 | 140 | # Open and parse the file 141 | sample_path = '/path/to/sample.exe' 142 | file_info = fileparser.FileParser(file_path=sample_path) 143 | 144 | # Check for a valid decoder and then parse 145 | if file_info.malware_name in __decoders__: 146 | module = __decoders__[file_info.malware_name]['obj']() 147 | module.set_file(file_info) 148 | module.get_config() 149 | conf = module.config 150 | pprint(conf) 151 | 152 | ``` 153 | 154 | 155 | ### Thanks 156 | 157 | Full credit where credit is due. 158 | 159 | Malware.lu for the initial xtreme Rat Writeup - https://code.google.com/p/malware-lu/wiki/en_xtreme_RAT 160 | 161 | Fireye for their Poison Ivy and Xtreme rat WriteUps (Even though they ignored my tweets :-) ) - http://www.fireeye.com/blog/technical/2014/02/xtremerat-nuisance-or-threat.html 162 | 163 | Shawn Denbow and Jesse Herts for their paper here - http://www.matasano.com/research/PEST-CONTROL.pdf Saved me a lot of time 164 | 165 | -------------------------------------------------------------------------------- /malconf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import sys 4 | import json 5 | from optparse import OptionParser 6 | 7 | from malwareconfig import fileparser 8 | from malwareconfig.modules import __decoders__, __preprocessors__ 9 | 10 | __description__ = 'Malware Config Extractor' 11 | __author__ = '@kevthehermit, https://techanarchy.net, https://malwareconfig.com' 12 | __version__ = '1.0' 13 | __date__ = '2019/10' 14 | 15 | 16 | def banner(): 17 | banner_text = ''' 18 | __ __ _ ____ __ 19 | | \/ | __ _| |/ ___|___ _ __ / _| 20 | | |\/| |/ _` | | | / _ \| '_ \| |_ 21 | | | | | (_| | | |__| (_) | | | | _| 22 | |_| |_|\__,_|_|\____\___/|_| |_|_| 23 | 24 | Malware Configuration Parser by @kevthehermit 25 | ''' 26 | print(banner_text) 27 | 28 | 29 | def process_file(file_path, output_file): 30 | # Open and parse the file 31 | print("[+] Loading File: {0}".format(file_path)) 32 | file_info = fileparser.FileParser(file_path=file_path) 33 | 34 | print(" [-] Found: {0}".format(file_info.malware_name)) 35 | 36 | # First we preprocesss 37 | # Check for a packer we can unpack 38 | if file_info.malware_name in __preprocessors__: 39 | print(" [+] Running PreProcessor {0}".format(file_info.malware_name)) 40 | module = __preprocessors__[file_info.malware_name]['obj']() 41 | module.set_file(file_info) 42 | module.pre_process() 43 | 44 | 45 | if file_info.malware_name in __decoders__: 46 | print(" [-] Running Decoder") 47 | module = __decoders__[file_info.malware_name]['obj']() 48 | module.set_file(file_info) 49 | module.get_config() 50 | conf = module.config 51 | 52 | if output_file: 53 | print(" [+] Writing config to file: {0}".format(output_file)) 54 | with open(output_file, 'a') as out: 55 | out.write('"{0}","{1}","{2}"\n'.format(file_path, file_info.malware_name, conf)) 56 | 57 | else: 58 | print(" [-] Config Output\n") 59 | json_config = json.dumps(conf, indent=4, sort_keys=True) 60 | print(json_config) 61 | else: 62 | print("[!] No Decoder or File is Packed") 63 | 64 | 65 | if __name__ == "__main__": 66 | parser = OptionParser(usage='usage: %prog file / dir\n' + __description__, version='%prog ' + __version__) 67 | parser.add_option("-r", "--recursive", action='store_true', default=False, help="Recursive Mode") 68 | parser.add_option("-l", "--list", action="store_true", default=False, help="List Available Decoders") 69 | parser.add_option("-o", "--output", help="Output Config elements to file.") 70 | (options, args) = parser.parse_args() 71 | 72 | banner() 73 | 74 | if options.list: 75 | print("[+] Listing Decoders") 76 | for name in __decoders__.keys(): 77 | print(" [-] Loaded: {0}".format(name)) 78 | 79 | print("[+] Listing PreProcessors") 80 | for name in __preprocessors__.keys(): 81 | print(" [-] Loaded: {0}".format(name)) 82 | 83 | sys.exit() 84 | 85 | # We need at least one arg 86 | if len(args) < 1: 87 | print("[!] Not enough Arguments, Need at least file path\n") 88 | parser.print_help() 89 | sys.exit() 90 | 91 | # Check for file or dir 92 | is_file = os.path.isfile(args[0]) 93 | is_dir = os.path.isdir(args[0]) 94 | 95 | if options.output: 96 | output_file = options.output 97 | else: 98 | output_file = None 99 | 100 | 101 | if options.recursive: 102 | if not is_dir: 103 | print("[!] Recursive requires a directory not a file.\n") 104 | sys.exit() 105 | 106 | # Read all the things 107 | base_dir = args[0] 108 | for root, dirs, files in os.walk(base_dir): 109 | for file_name in files: 110 | file_path = os.path.join(root, file_name) 111 | try: 112 | process_file(file_path, output_file) 113 | except Exception as e: 114 | print("[!] Unable to process file {0}".format(file_path)) 115 | 116 | else: 117 | if not is_file: 118 | print("[!] You did not provide a valid file.\n") 119 | sys.exit() 120 | 121 | # Read in the file. 122 | process_file(args[0], output_file) 123 | -------------------------------------------------------------------------------- /malwareconfig/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevthehermit/RATDecoders/d675ba1c06e6dd8365149c9ee8a8db1a6e5e508e/malwareconfig/__init__.py -------------------------------------------------------------------------------- /malwareconfig/common.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | 4 | class Decoder(object): 5 | decoder_name = "" 6 | decoder_version = 1 7 | decoder_author = "" 8 | decoder_description = "" 9 | file_info = object 10 | 11 | def set_file(self, file_info): 12 | self.file_info = file_info 13 | 14 | def get_config(self): 15 | pass 16 | 17 | 18 | class PreProcessor(object): 19 | preprocessor_name = "" 20 | preprocessor_version = 1 21 | preprocessor_author = "" 22 | preprocessor_description = "" 23 | file_info = object 24 | 25 | def set_file(self, file_info): 26 | self.file_info = file_info 27 | 28 | def pre_process(self): 29 | pass 30 | 31 | 32 | def string_printable(line): 33 | line = str(line) 34 | new_line = '' 35 | for c in line: 36 | if c in string.printable: 37 | new_line += c 38 | else: 39 | new_line += '\\x' + c.encode("utf-8").hex() 40 | return new_line 41 | 42 | 43 | -------------------------------------------------------------------------------- /malwareconfig/crypto.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from Crypto.Cipher import ARC4, DES, DES3, AES, Blowfish, XOR 3 | from Crypto.PublicKey import RSA 4 | 5 | from pbkdf2 import PBKDF2 6 | 7 | 8 | # RSA 9 | def decrypt_rsa(key, data): 10 | rsa_key = RSA.importKey(key) 11 | return rsa_key.decrypt(data) 12 | 13 | 14 | # XOR 15 | def decrypt_xor(key, data): 16 | cipher = XOR.new(key) 17 | return cipher.decrypt(data) 18 | 19 | 20 | # RC4 21 | def decrypt_arc4(key, data): 22 | cipher = ARC4.new(key) 23 | return cipher.decrypt(data) 24 | 25 | 26 | # DES 27 | def decrypt_des_ecb(key, data, iv=None): 28 | mode = DES.MODE_ECB 29 | if iv: 30 | cipher = DES.new(key, mode, iv) 31 | else: 32 | cipher = DES.new(key, mode) 33 | return cipher.decrypt(data) 34 | 35 | 36 | def decrypt_des_cbc(key, data, iv=None): 37 | mode = DES.MODE_CBC 38 | if iv: 39 | cipher = DES.new(key, mode, iv) 40 | else: 41 | cipher = DES.new(key, mode) 42 | return cipher.decrypt(data) 43 | 44 | 45 | # DES3 46 | def decrypt_des3(key, data): 47 | cipher = DES3.new(key) 48 | return cipher.decrypt(data) 49 | 50 | 51 | # AES 52 | def decrypt_aes(key, data): 53 | cipher = AES.new(key) 54 | return cipher.decrypt(data) 55 | 56 | 57 | def decrypt_aes_cbc_iv(key, iv, data): 58 | mode = AES.MODE_CBC 59 | cipher = AES.new(key, mode, IV=iv) 60 | return cipher.decrypt(data) 61 | 62 | 63 | # Blowfish 64 | def decrypt_blowfish(key, data): 65 | cipher = Blowfish.new(key) 66 | return cipher.decrypt(data) 67 | 68 | 69 | # RC6 - Custom 70 | def decrypt_RC6(key, encrypted, P, Q, rounds): 71 | def rol(a, i): 72 | a &= 0xFFFFFFFF 73 | i &= 0x1F 74 | x = (((a << i) & 0xFFFFFFFF) | (a >> (32 - i))) & 0xFFFFFFFF 75 | return x 76 | 77 | def ror(a, i): 78 | i &= 0x1F 79 | a &= 0xFFFFFFFF 80 | return ( ((a >> i) & 0xFFFFFFFF) | (a << ( (32 - i)))) & 0xFFFFFFFF 81 | 82 | def to_int(bytestring): 83 | if isinstance(bytestring, str): 84 | bytestring = bytearray(bytestring, 'utf-8') 85 | 86 | l = [] 87 | for i in range(int(len(bytestring)/4)): 88 | l.append(struct.unpack("> (j * 8) & 0xFF) 115 | return decrypted 116 | 117 | T = 2 * rounds + 4 118 | 119 | # Expand key 120 | L = to_int(key) 121 | S = [] 122 | S = [0 for i in range(T)] 123 | S[0] = P 124 | 125 | for x in range(T-1): 126 | S[x+1] = (S[x] + Q) & 0xFFFFFFFF 127 | i = 0 128 | j = 0 129 | A = 0 130 | B = 0 131 | 132 | for x in range(3*T): 133 | A = S[i] = rol((S[i] + A + B), 3) 134 | B = L[j] = rol((L[j] + A + B), (A + B)) 135 | i = (i + 1) % T 136 | j = (j + 1) % 8 137 | 138 | # Decrypt blocks 139 | decrypted = [] 140 | while True: 141 | decrypted += decrypt_block(encrypted[:16], S) 142 | encrypted = encrypted[16:] 143 | if not encrypted: 144 | break 145 | data = bytearray(decrypted) 146 | data = data.rstrip(b"\x00") 147 | return data 148 | 149 | 150 | # Key Derivation 151 | def derive_pbkdf2(key, salt, iv_length, key_length, iterations=8): 152 | generator = PBKDF2(key, salt, iterations) 153 | derived_iv = generator.read(iv_length) 154 | derived_key = generator.read(key_length) 155 | return derived_iv, derived_key 156 | -------------------------------------------------------------------------------- /malwareconfig/decoders/LostDoor.py: -------------------------------------------------------------------------------- 1 | from malwareconfig.crypto import decrypt_arc4 2 | from malwareconfig.common import Decoder 3 | from malwareconfig.common import string_printable 4 | from binascii import unhexlify 5 | 6 | class LostDoor(Decoder): 7 | decoder_name = "LostDoor" 8 | decoder__version = 1 9 | decoder_author = "@kevthehermit" 10 | decoder_description = "LostDoor Rat" 11 | 12 | def __init__(self): 13 | self.config = {} 14 | 15 | 16 | def ver_detect(self, data): 17 | first = data.split(b"*EDIT_SERVER*") 18 | if len(first) == 2: 19 | second = first[1].split(b"\r\n") 20 | if len(second) > 14 < 30: 21 | print("[+] Found Version < 8") 22 | return self.new_decoder(second) 23 | first = data.split(b"[DATA]") 24 | if len(first) == 21: 25 | print("[+] Found Version 8") 26 | return self.ver_80(first) 27 | if len(first) == 30: 28 | print("[+] Found Version 8.01") 29 | return self.ver_801(first) 30 | return None 31 | 32 | def new_decoder(self, split_list): 33 | raw_dict = {} 34 | for line in split_list: 35 | try: 36 | line = line.decode('UTF-8') 37 | k,v = line.split(' = ') 38 | raw_dict[k.strip('*')] = v.strip('%') 39 | except Exception as e: 40 | continue 41 | return self.config_cleaner(raw_dict) 42 | 43 | def config_cleaner(self, raw_dict): 44 | clean_dict = {} 45 | for k,v in raw_dict.items(): 46 | if k == 'ip': 47 | clean_dict['Domain'] = decrypt_arc4('oussamio', unhexlify(v)) 48 | if k == 'fire': 49 | clean_dict['Firewall Bypass'] = v 50 | if k == 'foder': 51 | clean_dict['InstallPath'] = v 52 | if k == 'mlt': 53 | clean_dict['Melt'] = v 54 | if k == 'msns': 55 | clean_dict['MSN Spread'] = v 56 | if k == 'name': 57 | clean_dict['Reg Key'] = v 58 | if k == 'path': 59 | clean_dict['Reg value'] = v 60 | if k == 'port': 61 | clean_dict['Port'] = v 62 | if k == 'ppp': 63 | clean_dict['P2PSpread'] = v 64 | if k == 'reg': 65 | clean_dict['Registry Startup'] = v 66 | if k == 'usb': 67 | clean_dict['USB Spread'] = v 68 | if k == 'usbn': 69 | clean_dict['USB Name'] = v 70 | if k == 'victimo': 71 | clean_dict['Campaign'] = v 72 | return clean_dict 73 | 74 | @staticmethod 75 | def ver_80(conf): 76 | conf_dict = {} 77 | conf_dict['Domain'] = decrypt_arc4('UniQue OussamiO', unhexlify(conf[1])) 78 | conf_dict['Campaign'] = conf[2] 79 | conf_dict['Enable Startup'] = conf[3] 80 | conf_dict['StartupName'] = conf[4] 81 | conf_dict['FolderName'] = conf[5] 82 | if conf[6] == "D": 83 | conf_dict['Path'] = 'App Data Folder' 84 | elif conf[6] == "W": 85 | conf_dict['Path'] = 'Windows Folder' 86 | if conf[6] == "s": 87 | conf_dict['Path'] = 'System Folder' 88 | conf_dict['Enable Error Message'] = conf[7] 89 | conf_dict['Error Message'] = conf[8] 90 | conf_dict['Disable Firewall'] = conf[9] 91 | #conf_dict[''] = conf[10] 92 | #conf_dict[''] = conf[11] 93 | conf_dict['USB Spread'] = conf[12] 94 | conf_dict['MSN Spread'] = conf[13] 95 | conf_dict['P2P Spread'] = conf[14] 96 | conf_dict['Melt'] = conf[15] 97 | conf_dict['Get Default User Name'] = conf[16] 98 | conf_dict['Connection Delay'] = conf[17] 99 | conf_dict['Set Hidden'] = conf[18] 100 | conf_dict['Protect Process'] = conf[19] 101 | #conf_dict[''] = conf[20] 102 | 103 | return conf_dict 104 | 105 | @staticmethod 106 | def ver_801(conf): 107 | conf_dict = {} 108 | conf_dict['Domain'] = decrypt_arc4('UniQue OussamiO', conf[1]) 109 | conf_dict['Campaign'] = conf[2] 110 | conf_dict['Enable Startup'] = conf[3] 111 | conf_dict['StartupName'] = conf[4] 112 | conf_dict['FolderName'] = conf[5] 113 | if conf[6] == 'D': 114 | conf_dict['Path'] = 'App Data Folder' 115 | elif conf[6] == 'W': 116 | conf_dict['Path'] = 'Windows Folder' 117 | if conf[6] == 's': 118 | conf_dict['Path'] = 'System Folder' 119 | conf_dict['Enable Error Message'] = conf[7] 120 | conf_dict['Error Message'] = conf[8] 121 | conf_dict['Disable Firewall'] = conf[9] 122 | conf_dict['USB Spread'] = conf[12] 123 | conf_dict['MSN Spread'] = conf[13] 124 | conf_dict['P2P Spread'] = conf[14] 125 | conf_dict['Melt'] = conf[15] 126 | conf_dict['Get Default User Name'] = conf[16] 127 | conf_dict['Connection Delay'] = conf[17] 128 | conf_dict['Set Hidden'] = conf[18] 129 | conf_dict['Protect Process'] = conf[19] 130 | conf_dict['Name To Spread'] = conf[20] 131 | conf_dict['Enable Active X'] = conf[21] 132 | conf_dict['Active X Key'] = conf[22] 133 | conf_dict['Enable Mutex'] = conf[23] 134 | conf_dict['Mutex'] = conf[24] 135 | conf_dict['Persistant Server'] = conf[25] 136 | conf_dict['Offline Keylogger'] = conf[26] 137 | conf_dict['Disable Task Manager'] = conf[27] 138 | conf_dict['Disable RegEdit'] = conf[28] 139 | return conf_dict 140 | 141 | 142 | 143 | 144 | def get_config(self): 145 | ''' 146 | This is the main entry 147 | :return: 148 | ''' 149 | 150 | 151 | file_data = self.file_info.file_data 152 | 153 | config_dict = self.ver_detect(file_data) 154 | 155 | # Set the config to the class for use 156 | self.config = config_dict 157 | -------------------------------------------------------------------------------- /malwareconfig/decoders/RedLine.py: -------------------------------------------------------------------------------- 1 | from malwareconfig.common import Decoder 2 | from malwareconfig.common import string_printable 3 | import base64 4 | 5 | 6 | 7 | 8 | 9 | 10 | class RedLine(Decoder): 11 | decoder_name = "RedLine" 12 | decoder__version = 1 13 | decoder_author = "@Gi7w0rm" 14 | decoder_description = "RedLine RAT Decoder" 15 | 16 | def __init__(self): 17 | self.config = {} 18 | 19 | 20 | def get_config(self): 21 | ''' 22 | This is the main entry 23 | :return: 24 | ''' 25 | config_dict = {} 26 | 27 | user_strings = self.file_info.dotnet_user_strings() 28 | base_location = user_strings.index("Yandex\\YaAddon") 29 | enc_domain = user_strings[base_location+1] 30 | enc_ID = user_strings[base_location+2] 31 | enc_key = user_strings[base_location+3] 32 | config_dict['Config_Key'] = enc_key 33 | config_dict['C2_Proxy'] = self.decrypt(enc_domain, enc_key) 34 | config_dict['Campaign_ID'] = self.decrypt(enc_ID, enc_key) 35 | 36 | # Set the config to the class for use 37 | self.config = config_dict 38 | 39 | def decrypt(self, str_to_dec, Key): 40 | dec_xor = "" 41 | first_dec = base64.b64decode(str_to_dec) 42 | 43 | len_first_dec = len(first_dec) 44 | 45 | 46 | for i in range(len_first_dec): 47 | Key = Key + str(Key[i % len(Key)]) 48 | 49 | a_list = [chr(ord(chr(a)) ^ ord(b)) for a,b in zip(first_dec, Key)] 50 | dec_xor = "".join(a_list) 51 | third_dec = base64.b64decode(dec_xor) 52 | tocut = str(third_dec) 53 | cut = tocut[2:-1] 54 | return cut 55 | 56 | -------------------------------------------------------------------------------- /malwareconfig/decoders/Xtreme.py: -------------------------------------------------------------------------------- 1 | from malwareconfig import crypto 2 | from malwareconfig.common import Decoder 3 | from malwareconfig.common import string_printable 4 | 5 | from binascii import hexlify, unhexlify 6 | 7 | # temp imports 8 | import re 9 | from struct import unpack 10 | 11 | class Xtreme(Decoder): 12 | decoder_name = "Xtreme" 13 | decoder__version = 1 14 | decoder_author = "@kevthehermit" 15 | decoder_description = "Xtreme decoder for 2.9, 3.1, 3.2, 3.5" 16 | 17 | def __init__(self): 18 | self.config = {} 19 | 20 | def get_unicode_string(self, buf, pos): 21 | out = '' 22 | for i in range(len(buf[pos:])): 23 | if buf[pos+i] == 0 and buf[pos+i+1] == 0: 24 | out += '\x00' 25 | break 26 | out += chr(buf[pos+i]) 27 | if out == '': 28 | return None 29 | else: 30 | return out.replace('\x00','') 31 | 32 | 33 | def get_config(self): 34 | ''' 35 | This is the main entry 36 | :return: 37 | ''' 38 | 39 | raw_config = {} 40 | file_data = self.file_info.file_data 41 | 42 | # Get Resource 43 | res_data = self.file_info.pe_resource_id(b'X\x00T\x00R\x00E\x00M\x00E\x00') 44 | 45 | 46 | # Return no resource 47 | if not res_data: 48 | print(" [-] No Config Resource Found") 49 | return # Check what we do for a negative result 50 | 51 | 52 | key = 'C\x00O\x00N\x00' 53 | decrypted_config = crypto.decrypt_arc4(key, res_data) 54 | 55 | # 1.3.x - Not implemented yet. 56 | if len(decrypted_config) == 0xe10: 57 | config_data = None 58 | # 2.9.x - Not a stable extract. 59 | elif len(decrypted_config) == 0x1390 or len(decrypted_config) == 0x1392: 60 | config_data = self.v29(decrypted_config) 61 | # 3.1 & 3.2 62 | elif len(decrypted_config) == 0x5Cc: 63 | config_data = self.v32(decrypted_config) 64 | # 3.5 65 | elif len(decrypted_config) == 0x7f0: 66 | config_data = self.v35(decrypted_config) 67 | else: 68 | config_data = None 69 | 70 | 71 | def v29(self, rawConfig): 72 | config_data = {} 73 | config_data["ID"] = self.get_unicode_string(rawConfig, 0x9e0) 74 | config_data["Group"] = self.get_unicode_string(rawConfig, 0xa5a) 75 | config_data["Version"] = self.get_unicode_string(rawConfig, 0xf2e) 76 | config_data["Mutex"] = self.get_unicode_string(rawConfig, 0xfaa) 77 | config_data["Install Dir"] = self.get_unicode_string(rawConfig, 0xb50) 78 | config_data["Install Name"] = self.get_unicode_string(rawConfig, 0xad6) 79 | config_data["HKLM"] = self.get_unicode_string(rawConfig, 0xc4f) 80 | config_data["HKCU"] = self.get_unicode_string(rawConfig, 0xcc8) 81 | config_data["Custom Reg Key"] = self.get_unicode_string(rawConfig, 0xdc0) 82 | config_data["Custom Reg Name"] = self.get_unicode_string(rawConfig, 0xe3a) 83 | config_data["Custom Reg Value"] = self.get_unicode_string(rawConfig, 0xa82) 84 | config_data["ActiveX Key"] = self.get_unicode_string(rawConfig, 0xd42) 85 | config_data["Injection"] = self.get_unicode_string(rawConfig, 0xbd2) 86 | config_data["FTP Server"] = self.get_unicode_string(rawConfig, 0x111c) 87 | config_data["FTP UserName"] = self.get_unicode_string(rawConfig, 0x1210) 88 | config_data["FTP Password"] = self.get_unicode_string(rawConfig, 0x128a) 89 | config_data["FTP Folder"] = self.get_unicode_string(rawConfig, 0x1196) 90 | config_data["Domain1"] = str(self.get_unicode_string(rawConfig, 0x50)+":"+str(unpack("(.*?)(.*?) 3: 35 | config_dict["Domain"] = parts[0] 36 | config_dict["Port"] = parts[1] 37 | config_dict["Install Path"] = parts[2] 38 | config_dict["Install Name"] = parts[3] 39 | config_dict["Startup Key"] = parts[4] 40 | config_dict["Campaign ID"] = parts[5] 41 | config_dict["Mutex Main"] = parts[6] 42 | config_dict["Mutex Per"] = parts[7] 43 | config_dict["YPER"] = parts[8] 44 | config_dict["YGRB"] = parts[9] 45 | config_dict["Mutex Grabber"] = parts[10] 46 | config_dict["Screen Rec Link"] = parts[11] 47 | config_dict["Mutex 4"] = parts[12] 48 | config_dict["YVID"] = parts[13] 49 | config_dict["YIM"] = parts[14] 50 | config_dict["NO"] = parts[15] 51 | config_dict["Smart Broadcast"] = parts[16] 52 | config_dict["YES"] = parts[17] 53 | config_dict["Plugins"] = parts[18] 54 | config_dict["Flag1"] = parts[19] 55 | config_dict["Flag2"] = parts[20] 56 | config_dict["Flag3"] = parts[21] 57 | config_dict["Flag4"] = parts[22] 58 | config_dict["WebPanel"] = parts[23] 59 | config_dict["Remote Delay"] = parts[24] 60 | 61 | # Set the config to the class for use 62 | self.config = config_dict 63 | -------------------------------------------------------------------------------- /malwareconfig/decoders/blacknix.py: -------------------------------------------------------------------------------- 1 | from malwareconfig import crypto 2 | from malwareconfig.common import Decoder 3 | from malwareconfig.common import string_printable 4 | 5 | 6 | class BlackNix(Decoder): 7 | decoder_name = "BlackNix" 8 | decoder__version = 1 9 | decoder_author = "@kevthehermit" 10 | decoder_description = "BlackNix RAT" 11 | 12 | def __init__(self): 13 | self.config = {} 14 | 15 | 16 | def get_config(self): 17 | ''' 18 | This is the main entry 19 | :return: 20 | ''' 21 | config_data = self.file_info.pe_resource_by_name("SETTINGS") 22 | 23 | # Decode String 24 | config_line = '' 25 | for c in config_data: 26 | config_line += chr(c-1) 27 | 28 | # Reverse the string 29 | config_line = config_line[::-1] 30 | 31 | # Get Fields 32 | fields = config_line.split('|') 33 | 34 | config_dict = { 35 | 'Mutex': fields[1], 36 | 'Anti Sandboxie': fields[2], 37 | 'Max Folder Size': fields[3], 38 | 'Delay Time': fields[4], 39 | 'Password': fields[5], 40 | 'Kernel Mode Unhooking': fields[6], 41 | 'User More Unhooking': fields[7], 42 | 'Melt Server': fields[8], 43 | 'Offline Screen Capture': fields[9], 44 | 'Offline Keylogger': fields[10], 45 | 'Copy To ADS': fields[11], 46 | 'Domain': fields[12], 47 | 'Persistence Thread': fields[13], 48 | 'Active X Key': fields[14], 49 | 'Registry Key': fields[15], 50 | 'Active X Run': fields[16], 51 | 'Registry Run': fields[17], 52 | 'Safe Mode Startup': fields[18], 53 | 'Inject winlogon.exe': fields[19], 54 | 'Install Name': fields[20], 55 | 'Install Path': fields[21], 56 | 'Campaign Name': fields[22], 57 | 'Campaign Group': fields[23] 58 | } 59 | 60 | # Set the config to the class for use 61 | self.config = config_dict 62 | -------------------------------------------------------------------------------- /malwareconfig/decoders/blackshades.py: -------------------------------------------------------------------------------- 1 | import re 2 | import binascii 3 | 4 | from malwareconfig import crypto 5 | from malwareconfig.common import Decoder 6 | from malwareconfig.common import string_printable 7 | 8 | prng_seed = 0 9 | 10 | class BlackShades(Decoder): 11 | decoder_name = "BlackShades" 12 | decoder__version = 1 13 | decoder_author = "@botnet_hunter, @kevthehermit" 14 | decoder_description = "BlackShades Decoder" 15 | 16 | def __init__(self): 17 | self.config = {} 18 | 19 | @staticmethod 20 | def is_valid_config(config): 21 | if config[:3] != "\x0c\x0c\x0c": 22 | return False 23 | if config.count("\x0C\x0C\x0C") < 15: 24 | return False 25 | return True 26 | 27 | @staticmethod 28 | def get_next_rng_value(): 29 | global prng_seed 30 | prng_seed = ((prng_seed * 1140671485 + 12820163) & 0xffffff) 31 | return int(prng_seed / 65536) 32 | 33 | @staticmethod 34 | def decrypt_configuration(hex): 35 | global prng_seed 36 | 37 | hex = hex.decode('utf-8') 38 | ascii = binascii.unhexlify(hex) 39 | tail = ascii[0x20:] 40 | 41 | pre_check = [] 42 | for x in range(3): 43 | pre_check.append(tail[x] ^ 0x0c) 44 | 45 | for x in range(0xffffff): 46 | prng_seed = x 47 | if BlackShades.get_next_rng_value() != pre_check[0] or BlackShades.get_next_rng_value() != pre_check[1] or BlackShades.get_next_rng_value() != pre_check[2]: 48 | continue 49 | prng_seed = x 50 | 51 | config_bytes = [] 52 | 53 | for c in tail: 54 | config_bytes.append(chr(c ^ int(BlackShades.get_next_rng_value()))) 55 | 56 | config = "".join(config_bytes) 57 | 58 | if BlackShades.is_valid_config(config): 59 | return config.split("\x0c\x0c\x0c") 60 | return None 61 | 62 | def extract_config(self): 63 | file_data = self.file_info.file_data 64 | config_pattern = re.findall(b'[0-9a-fA-F]{154,}', file_data) 65 | for s in config_pattern: 66 | if (len(s) % 2) == 1: 67 | s = s[:-1] 68 | return s 69 | 70 | @staticmethod 71 | def config_parser(config): 72 | config_dict = {} 73 | config_dict['Domain'] = config[1] 74 | config_dict['Client Control Port'] = config[2] 75 | config_dict['Client Transfer Port'] = config[3] 76 | config_dict['Campaign ID'] = config[4] 77 | config_dict['File Name'] = config[5] 78 | config_dict['Install Path'] = config[6] 79 | config_dict['Registry Key'] = config[7] 80 | config_dict['ActiveX Key'] = config[8] 81 | config_dict['Install Flag'] = config[9] 82 | config_dict['Hide File'] = config[10] 83 | config_dict['Melt File'] = config[11] 84 | config_dict['Delay'] = config[12] 85 | config_dict['USB Spread'] = config[13] 86 | config_dict['Mutex'] = config[14] 87 | config_dict['Log File'] = config[15] 88 | config_dict['Folder Name'] = config[16] 89 | config_dict['Smart DNS'] = config[17] 90 | config_dict['Protect Process'] = config[18] 91 | return config_dict 92 | 93 | def get_config(self): 94 | """ 95 | This is the main entry 96 | :return: 97 | """ 98 | 99 | # Extract Config 100 | config_data = self.extract_config() 101 | 102 | # Decrypt The Config 103 | clear_config = self.decrypt_configuration(config_data) 104 | 105 | # Parse Config 106 | config_dict = BlackShades.config_parser(clear_config) 107 | 108 | # Set the config to the class for use 109 | self.config = config_dict 110 | -------------------------------------------------------------------------------- /malwareconfig/decoders/bluebanana.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | from malwareconfig import crypto 3 | from malwareconfig.common import Decoder 4 | from malwareconfig.common import string_printable 5 | 6 | 7 | class BlueBanana(Decoder): 8 | decoder_name = "BlueBanana" 9 | decoder__version = 1 10 | decoder_author = "@kevthehermit" 11 | decoder_description = "Decoder for Blue Banana" 12 | 13 | def __init__(self): 14 | self.config = {} 15 | 16 | 17 | def get_config(self): 18 | ''' 19 | This is the main entry 20 | :return: 21 | ''' 22 | key1 = '15af8sd4s1c5s511' 23 | key2 = '4e3f5a4c592b243f' 24 | 25 | crypted_config = self.file_info.file_from_zip('config.txt') 26 | 27 | first_round = crypto.decrypt_aes(key1, binascii.unhexlify(crypted_config)) 28 | clear_config = crypto.decrypt_aes(key2, binascii.unhexlify(first_round[:-16])) 29 | 30 | fields = clear_config.decode('utf-8').split("") 31 | 32 | config_dict = {'Domain': fields[0], 33 | 'Password': fields[1], 34 | 'Port1': fields[2], 35 | 'Port2': fields[3] 36 | } 37 | if len(fields) > 4: 38 | config_dict['InstallName'] = fields[4] 39 | config_dict['JarName'] = fields[5] 40 | 41 | # Set the config to the class for use 42 | self.config = config_dict 43 | -------------------------------------------------------------------------------- /malwareconfig/decoders/bozok.py: -------------------------------------------------------------------------------- 1 | from malwareconfig import crypto 2 | from malwareconfig.common import Decoder 3 | from malwareconfig.common import string_printable 4 | 5 | 6 | class Bozok(Decoder): 7 | decoder_name = "Bozok" 8 | decoder__version = 1 9 | decoder_author = "@kevthehermit" 10 | decoder_description = "Bozok Decoder" 11 | 12 | def __init__(self): 13 | self.config = {} 14 | 15 | 16 | def get_config(self): 17 | """ 18 | This is the main entry 19 | :return: 20 | """ 21 | 22 | config_data = self.file_info.pe_resource_by_name('CFG') 23 | 24 | config_data = config_data.decode('utf-8').replace('\x00', '') 25 | config_fields = config_data.split('|') 26 | 27 | config_dict = {'ServerID': config_fields[0], 28 | 'InstallName': config_fields[2], 29 | 'StartupName': config_fields[3], 30 | 'Extension': config_fields[4], 31 | 'Password': config_fields[5], 32 | 'Install Flag': config_fields[6], 33 | 'Startup Flag': config_fields[7], 34 | 'Visible Flag': config_fields[8], 35 | 'Unknown Flag1': config_fields[9], 36 | 'Unknown Flag2': config_fields[10], 37 | 'Port': config_fields[11], 38 | 'Domain': config_fields[12], 39 | 'Unknown Flag3': config_fields[13] 40 | } 41 | 42 | # Set the config to the class for use 43 | self.config = config_dict 44 | -------------------------------------------------------------------------------- /malwareconfig/decoders/clientmesh.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode 2 | from malwareconfig.common import Decoder 3 | from malwareconfig.common import string_printable 4 | 5 | 6 | class ClientMesh(Decoder): 7 | decoder_name = "ClientMesh" 8 | decoder__version = 1 9 | decoder_author = "@kevthehermit" 10 | decoder_description = "ClientMesh Decoder" 11 | 12 | def __init__(self): 13 | self.config = {} 14 | 15 | def get_config(self): 16 | ''' 17 | This is the main entry 18 | :return: 19 | ''' 20 | 21 | file_data = self.file_info.file_data 22 | splits = file_data.split(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7e') 23 | 24 | config_data = b64decode(splits[-1]).decode('utf-8') 25 | 26 | fields = config_data.split('``') 27 | 28 | config_dict = { 29 | 'Domain': fields[0], 30 | 'Port': fields[1], 31 | 'Password': fields[2], 32 | 'CampaignID': fields[3], 33 | 'MsgBoxFlag': fields[4], 34 | 'MsgBoxTitle': fields[5], 35 | 'MsgBoxText': fields[6], 36 | 'Startup': fields[7], 37 | 'RegistryKey': fields[8], 38 | 'RegistryPersistance': fields[9], 39 | 'LocalKeyLogger': fields[10], 40 | 'VisibleFlag': fields[11], 41 | 'Unknown': fields[12] 42 | } 43 | 44 | # Set the config to the class for use 45 | self.config = config_dict 46 | -------------------------------------------------------------------------------- /malwareconfig/decoders/cybergate.py: -------------------------------------------------------------------------------- 1 | from malwareconfig import crypto 2 | from malwareconfig.common import Decoder 3 | from malwareconfig.common import string_printable 4 | 5 | 6 | class CyberGate(Decoder): 7 | decoder_name = "CyberGate" 8 | decoder__version = 1 9 | decoder_author = "@kevthehermit" 10 | decoder_description = "CyberGate RAT" 11 | 12 | def __init__(self): 13 | self.config = {} 14 | 15 | @staticmethod 16 | def xor(data): 17 | decoded = '' 18 | key = 0xBC 19 | for c in data: 20 | decoded += chr(key^c) 21 | return decoded 22 | 23 | def get_config(self): 24 | ''' 25 | This is the main entry 26 | :return: 27 | ''' 28 | 29 | key = 0xBC 30 | 31 | resource_list = self.file_info.pe_resource_names() 32 | 33 | if 'XX-XX-XX-XX' in resource_list: 34 | config_data = self.file_info.pe_resource_by_name('XX-XX-XX-XX') 35 | elif 'CG-CG-CG-CG' in resource_list: 36 | config_data = self.file_info.pe_resource_by_name('CG-CG-CG-CG') 37 | else: 38 | print("Unable to find config data") 39 | return 40 | 41 | fields = config_data.split(b'####@####') 42 | config_dict = {} 43 | # fields 0 - 19 contain domains and ports 44 | for i in range(19): 45 | decrypted = CyberGate.xor(fields[i]) 46 | if decrypted == '\x9c': 47 | continue 48 | config_dict['Domain{0}'.format(i)] = decrypted 49 | 50 | config_dict['CampaignID'] = CyberGate.xor(fields[20]) 51 | config_dict['Password'] = CyberGate.xor(fields[21]) 52 | config_dict['InstallFlag'] = CyberGate.xor(fields[22]) 53 | config_dict['InstallDir'] = CyberGate.xor(fields[25]) 54 | config_dict['InstallFileName'] = CyberGate.xor(fields[26]) 55 | config_dict['ActiveXStartup'] = CyberGate.xor(fields[27]) 56 | config_dict['REGKeyHKLM'] = CyberGate.xor(fields[28]) 57 | config_dict['REGKeyHKCU'] = CyberGate.xor(fields[29]) 58 | config_dict['EnableMessageBox'] = CyberGate.xor(fields[30]) 59 | config_dict['MessageBoxIcon'] = CyberGate.xor(fields[31]) 60 | config_dict['MessageBoxButton'] = CyberGate.xor(fields[32]) 61 | config_dict['InstallMessageTitle'] = CyberGate.xor(fields[33]) 62 | config_dict['InstallMessageBox'] = CyberGate.xor(fields[34]) 63 | config_dict['ActivateKeylogger'] = CyberGate.xor(fields[35]) 64 | config_dict['KeyloggerBackspace'] = CyberGate.xor(fields[36]) 65 | config_dict['KeyloggerEnableFTP'] = CyberGate.xor(fields[37]) 66 | config_dict['FTPAddress'] = CyberGate.xor(fields[38]) 67 | config_dict['FTPDirectory'] = CyberGate.xor(fields[39]) 68 | config_dict['FTPUserName'] = CyberGate.xor(fields[41]) 69 | config_dict['FTPPassword'] = CyberGate.xor(fields[42]) 70 | config_dict['FTPPort'] = CyberGate.xor(fields[43]) 71 | config_dict['FTPInterval'] = CyberGate.xor(fields[44]) 72 | config_dict['Persistance'] = CyberGate.xor(fields[59]) 73 | config_dict['HideFile'] = CyberGate.xor(fields[60]) 74 | config_dict['ChangeCreationDate'] = CyberGate.xor(fields[61]) 75 | config_dict['Mutex'] = CyberGate.xor(fields[62]) 76 | config_dict['MeltFile'] = CyberGate.xor(fields[63]) 77 | config_dict['CyberGateVersion'] = CyberGate.xor(fields[67]) 78 | config_dict['StartupPolicies'] = CyberGate.xor(fields[69]) 79 | config_dict['USBSpread'] = CyberGate.xor(fields[70]) 80 | 81 | process_inject = CyberGate.xor(fields[57]) 82 | if process_inject == 0 or process_inject == None: 83 | config_dict['ProcessInjection'] = 'Disabled' 84 | elif process_inject == 1: 85 | config_dict['ProcessInjection'] = 'Default Browser' 86 | elif process_inject == 2: 87 | config_dict['ProcessInjection'] = CyberGate.xor(fields[58]) 88 | 89 | # Set the config to the class for use 90 | self.config = config_dict 91 | -------------------------------------------------------------------------------- /malwareconfig/decoders/darkcomet.py: -------------------------------------------------------------------------------- 1 | from malwareconfig import crypto 2 | from malwareconfig.common import Decoder 3 | from malwareconfig.common import string_printable 4 | 5 | 6 | class DarkComet(Decoder): 7 | decoder_name = "DarkComet" 8 | decoder__version = 1 9 | decoder_author = "@kevthehermit" 10 | decoder_description = "DarkComet decoder for all known versions" 11 | 12 | def __init__(self): 13 | self.config = {} 14 | 15 | @staticmethod 16 | def get_version(raw_data): 17 | if b'#KCMDDC2#' in raw_data: 18 | return '#KCMDDC2#-890' 19 | elif b'#KCMDDC4#' in raw_data: 20 | return '#KCMDDC4#-890' 21 | elif b'#KCMDDC42#' in raw_data: 22 | return '#KCMDDC42#-890' 23 | elif b'#KCMDDC42F#' in raw_data: 24 | return '#KCMDDC42F#-890' 25 | elif b'#KCMDDC5#' in raw_data: 26 | return '#KCMDDC5#-890' 27 | elif b'#KCMDDC51#' in raw_data: 28 | return '#KCMDDC51#-890' 29 | else: 30 | return None 31 | 32 | @staticmethod 33 | def parse_v5(file_info, dc_version): 34 | config = {} 35 | 36 | # Get the config section 37 | config_data = file_info.pe_resource_by_name("DCDATA") 38 | 39 | # Decrypt the config using the key 40 | crypted_config = bytes.fromhex(config_data.decode()) 41 | clear_config = crypto.decrypt_arc4(dc_version, crypted_config) 42 | 43 | # Parse the config entries to get a json object 44 | config_list = clear_config.split(b'\r\n') 45 | for entries in config_list[1:-1]: 46 | key, value = entries.split(b'=') 47 | key = key.strip().decode('utf-8') 48 | value = value.rstrip()[1:-1] 49 | clean_value = string_printable(value.decode('utf-8')) 50 | config[key] = clean_value 51 | config['Version'] = dc_version 52 | 53 | # return the json 54 | return config 55 | 56 | def get_config(self): 57 | ''' 58 | This is the main entry 59 | :return: 60 | ''' 61 | 62 | file_data = self.file_info.file_data 63 | 64 | # Version Check 65 | dc_version = self.get_version(file_data) 66 | 67 | # The version dictates how the resources are saved in the binary 68 | 69 | if '5' in dc_version: 70 | raw_config = self.parse_v5(self.file_info, dc_version) 71 | 72 | self.config = raw_config 73 | -------------------------------------------------------------------------------- /malwareconfig/decoders/darkrat.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode 2 | 3 | from malwareconfig import crypto 4 | from malwareconfig.common import Decoder 5 | from malwareconfig.common import string_printable 6 | 7 | 8 | class DarkRAT(Decoder): 9 | decoder_name = "DarkRAT" 10 | decoder__version = 1 11 | decoder_author = "@kevthehermit" 12 | decoder_description = "darkrat RAT Decoder" 13 | 14 | def __init__(self): 15 | self.config = {} 16 | 17 | 18 | def get_config(self): 19 | ''' 20 | This is the main entry 21 | :return: 22 | ''' 23 | 24 | file_data = self.file_info.file_data 25 | config_dict = {} 26 | raw_config = file_data.split(b'@1906dark1996coder@') 27 | if len(raw_config) > 3: 28 | config_dict['Domain'] = raw_config[1][7:-1] 29 | config_dict['AutoRun'] = raw_config[2] 30 | config_dict['USB Spread'] = raw_config[3] 31 | config_dict['Hide Form'] = raw_config[4] 32 | config_dict['Msg Box Title'] = raw_config[6] 33 | config_dict['Msg Box Text'] = raw_config[7] 34 | config_dict['Timer Interval'] = raw_config[8] 35 | if raw_config[5] == 4: 36 | config_dict['Msg Box Type'] = 'Information' 37 | elif raw_config[5] == 2: 38 | config_dict['Msg Box Type'] = 'Question' 39 | elif raw_config[5] == 3: 40 | config_dict['Msg Box Type'] = 'Exclamation' 41 | elif raw_config[5] == 1: 42 | config_dict['Msg Box Type'] = 'Critical' 43 | else: 44 | config_dict['Msg Box Type'] = 'None' 45 | 46 | # Set the config to the class for use 47 | self.config = config_dict 48 | -------------------------------------------------------------------------------- /malwareconfig/decoders/hawkeye.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode 2 | from binascii import unhexlify, hexlify 3 | 4 | from malwareconfig import crypto 5 | from malwareconfig.common import Decoder 6 | 7 | 8 | class HawkEye(Decoder): 9 | decoder_name = "HawkEye" 10 | decoder__version = 1 11 | decoder_author = "@kevthehermit" 12 | decoder_description = "Decoder For Hawkeye Cred Stealer" 13 | 14 | def __init__(self): 15 | self.config = {} 16 | 17 | def get_config(self): 18 | ''' 19 | This is the main entry 20 | :return: 21 | ''' 22 | 23 | # Get the user string list 24 | user_strings = self.file_info.dotnet_user_strings() 25 | 26 | # Version Check 27 | 28 | key = 'HawkEyeKeylogger' 29 | salt = '0\x009\x009\x00u\x007\x008\x007\x009\x007\x008\x007\x008\x006\x00' 30 | 31 | # Older Version 32 | if 'InvisibleSoft' in user_strings: 33 | key = 'InvisibleSoft' 34 | 35 | # Derive Key 36 | d_iv, d_key = crypto.derive_pbkdf2(key, salt, 16, 32, iterations=1000) 37 | 38 | # base key = HawkEyeKeylogger 39 | # iv = 68884763241455010e730a43249fdf87 40 | # Key = 20eec1151ad79610fa5def0ef4fe701c87f1699c163d7e4dc2be0da50f661b53 41 | 42 | # Grab values and decrypt the encoded one. 43 | 44 | raw_config = {} 45 | for i in range(3,40): 46 | try: 47 | crypted_string = b64decode(user_strings[i]) 48 | result = crypto.decrypt_aes_cbc_iv(d_key, d_iv, crypted_string) 49 | result = result.decode('utf-8') 50 | 51 | # There are some padding chars 52 | for rep in ['\x00', '\n', '\x08', '\x04', '\x10']: 53 | result = result.replace(rep, '') 54 | 55 | raw_config['Key{0}'.format(i)] = result 56 | 57 | except Exception as e: 58 | raw_config['Key{0}'.format(i)] = user_strings[i] 59 | 60 | # Set the config to the class for use 61 | self.config = raw_config 62 | -------------------------------------------------------------------------------- /malwareconfig/decoders/hrat.py: -------------------------------------------------------------------------------- 1 | import re 2 | from malwareconfig.common import Decoder 3 | from malwareconfig.common import string_printable 4 | 5 | 6 | class Hrat(Decoder): 7 | decoder_name = "Hrat" 8 | decoder__version = 1 9 | decoder_author = "@kevthehermit" 10 | decoder_description = "Houdini and WSH RAT Decoder" 11 | 12 | def __init__(self): 13 | self.config = {} 14 | 15 | 16 | def get_config(self): 17 | ''' 18 | This is the main entry 19 | :return: 20 | ''' 21 | config_dict = {} 22 | 23 | #Initial settings are appended to the end of the file 24 | file_data = self.file_info.file_data 25 | 26 | # Lets try to decode some common types 27 | # 28 | 29 | 30 | host_search = re.search(b'host = "(.*)"', file_data) 31 | port_search = re.search(b'port = ([0-9]{1,5})', file_data) 32 | install_search = re.search(b'installdir = "(.*)"', file_data) 33 | runasadmin_search = re.search(b'runAsAdmin = (true|false)', file_data) 34 | lnkfile_search = re.search(b'lnkfile = (true|false)', file_data) 35 | lnkfolder_search = re.search(b'lnkfolder = (true|false)', file_data) 36 | 37 | config_dict["host"] = host_search.group(1).decode('utf-8') 38 | config_dict["port"] = port_search.group(1).decode('utf-8') 39 | config_dict["Install Dir"] = install_search.group(1).decode('utf-8') 40 | config_dict["Run as admin"] = runasadmin_search.group(1).decode('utf-8') 41 | config_dict["lnk_file"] = lnkfile_search.group(1).decode('utf-8') 42 | config_dict['lnk_dir'] = lnkfolder_search.group(1).decode('utf-8') 43 | 44 | # Set the config to the class for use 45 | self.config = config_dict 46 | -------------------------------------------------------------------------------- /malwareconfig/decoders/jbifrost.py: -------------------------------------------------------------------------------- 1 | import javaobj # sudo pip3 install javaobj-py3 2 | from xml.etree import ElementTree as ET 3 | from malwareconfig import fileparser 4 | from io import BytesIO 5 | from malwareconfig import crypto 6 | from malwareconfig.common import Decoder 7 | from malwareconfig.common import string_printable 8 | 9 | 10 | class Jbifrost(Decoder): 11 | decoder_name = "Jbifrost" 12 | decoder__version = 1 13 | decoder_author = ["@kevthehermit", "Chris"] 14 | decoder_description = "Jbifrost Java RAT" 15 | 16 | def __init__(self): 17 | self.config = {} 18 | 19 | 20 | @staticmethod 21 | def parse_xml(xml_string): 22 | # Convert to XML 23 | xml = ET.fromstring(xml_string) 24 | 25 | # Read the XML to a config dict 26 | properties_dict = {} 27 | 28 | for child in xml: 29 | if child.attrib: 30 | properties_dict[child.attrib['key']] = child.text 31 | 32 | return properties_dict 33 | 34 | @staticmethod 35 | def extract_rsa_key(java_serialized_data): 36 | deserialized_data = javaobj.loads(java_serialized_data) 37 | rsa_key_bytes = deserialized_data.encoded 38 | rsa_key_string = '' 39 | for b in rsa_key_bytes: 40 | # convert to unsigned int 41 | rsa_key_string += chr(b & 0xff) 42 | return rsa_key_string 43 | 44 | 45 | def extract_from_droppper(self): 46 | file_info = self.file_info 47 | 48 | try: 49 | # Read Files 50 | encrypted_aes_key = file_info.file_from_zip('mega.download') 51 | rsa_serialized_key = file_info.file_from_zip('sky.drive') 52 | aes_encrypted_config = file_info.file_from_zip('drop.box') 53 | 54 | # Deserailize and retrieve the RSA Key 55 | rsa_key_string = self.extract_rsa_key(rsa_serialized_key) 56 | 57 | # Decrypt the AES Key using the RSA Key 58 | aes_key = crypto.decrypt_rsa(rsa_key_string, encrypted_aes_key) 59 | 60 | except: 61 | # Read in serialized RSA KeyRep file. 62 | aes_encrypted_config = file_info.file_from_zip('lp/sq/d.png') 63 | aes_key = 'lks03oeldkfirowl' 64 | 65 | # Use the AES Key to decrpyt the config 66 | aes_decrypted_xml = crypto.decrypt_aes(aes_key[-16:], aes_encrypted_config) 67 | parsed_xml = self.parse_xml(aes_decrypted_xml) 68 | 69 | # Extract the implant using the xml properties 70 | rsa_serialized_key_path = parsed_xml['PRIVATE_PASSWORD'].lstrip('/') 71 | aes_path = parsed_xml['PASSWORD_CRYPTED'].lstrip('/') 72 | implant_path = parsed_xml['SERVER_PATH'].lstrip('/') 73 | 74 | rsa_serialized_key = file_info.file_from_zip(rsa_serialized_key_path) 75 | encrypted_aes_key = file_info.file_from_zip(aes_path) 76 | aes_encrypted_jar = file_info.file_from_zip(implant_path) 77 | 78 | # Another round of extraction 79 | rsa_key_string = Jbifrost.extract_rsa_key(rsa_serialized_key) 80 | aes_key = crypto.decrypt_rsa(rsa_key_string, encrypted_aes_key) 81 | 82 | implant_jar = crypto.decrypt_aes(aes_key[-16:], aes_encrypted_jar) 83 | 84 | # Update for new file 85 | new_info = fileparser.FileParser(rawdata=implant_jar) 86 | self.file_info = new_info 87 | 88 | # Run config again 89 | self.get_config() 90 | 91 | def get_config(self): 92 | ''' 93 | This is the main entry 94 | :return: 95 | ''' 96 | 97 | 98 | file_info = self.file_info 99 | 100 | file_list = file_info.zip_namelist() 101 | 102 | # Look for Dropper 103 | if 'sky.drive' in file_list and 'mega.download' in file_list and 'drop.box' in file_list: 104 | self.extract_from_droppper() 105 | 106 | # Look for implant 107 | elif 'server/resources/config.json' in file_list and 'server/resources/Key1.json' in file_list and 'server/resources/Key2.json' in file_list: 108 | rsa_serialized_key = file_info.file_from_zip('server/resources/Key1.json') 109 | encrypted_aes_key = file_info.file_from_zip('server/resources/Key2.json') 110 | aes_encrypted_config = file_info.file_from_zip('server/resources/config.json') 111 | 112 | # Another round of extraction 113 | rsa_key_string = Jbifrost.extract_rsa_key(rsa_serialized_key) 114 | aes_key = crypto.decrypt_rsa(rsa_key_string, encrypted_aes_key) 115 | implant_config = crypto.decrypt_aes(aes_key[-16:], aes_encrypted_config) 116 | 117 | implant_config = implant_config.decode('utf-8').rstrip('\x0e') 118 | 119 | self.config = implant_config 120 | 121 | elif 'lp/sq/d.png' in file_info: 122 | self.extract_from_droppper() 123 | else: 124 | print 125 | '\n[+] Failed to find jBiFrost malware...' -------------------------------------------------------------------------------- /malwareconfig/decoders/jrat.py: -------------------------------------------------------------------------------- 1 | from binascii import unhexlify 2 | from base64 import b64decode 3 | from malwareconfig import crypto 4 | from malwareconfig.common import Decoder 5 | from malwareconfig import fileparser 6 | 7 | 8 | class JRat(Decoder): 9 | decoder_name = "JRat" 10 | decoder__version = 1 11 | decoder_author = "@kevthehermit" 12 | decoder_description = "Java Based RAT" 13 | 14 | def __init__(self): 15 | self.config = {} 16 | 17 | 18 | def get_config(self): 19 | ''' 20 | This is the main entry 21 | :return: 22 | ''' 23 | 24 | file_info = self.file_info 25 | file_list = file_info.zip_namelist() 26 | 27 | internal_jar = None 28 | key_file = None 29 | config_data = None 30 | 31 | config_dict = {} 32 | 33 | for name in file_list: 34 | if name == 'key.dat': 35 | key_file = file_info.file_from_zip(name) 36 | if name == 'enc.dat': 37 | internal_jar = file_info.file_from_zip(name) 38 | if name == 'config.dat': 39 | config_data = file_info.file_from_zip(name) 40 | 41 | # If there is a wrapper around the jrat 42 | if internal_jar: 43 | drop_conf = key_file 44 | new_key = drop_conf[:16] 45 | drop_conf_fields = key_file[16:].split(b',') 46 | config_dict['dropper'] = [] 47 | for field in drop_conf_fields: 48 | try: 49 | decoded_field = b64decode(field).decode('utf-8') 50 | config_dict['dropper'].append(unhexlify(decoded_field)) 51 | except: 52 | pass 53 | 54 | new_jar_data = crypto.decrypt_aes(new_key, internal_jar) 55 | new_jar = fileparser.FileParser(rawdata=new_jar_data) 56 | 57 | # replace the keyfile and jar file with the new dropper 58 | for name in new_jar.zip_namelist(): 59 | if name == 'key.dat': 60 | key_file = new_jar.file_from_zip(name) 61 | if name == 'config.dat': 62 | config_data = new_jar.file_from_zip(name) 63 | 64 | 65 | # With the Key and Config Decrypt 66 | if len(key_file) == 16: 67 | # AES 68 | decrypted = crypto.decrypt_aes(key_file, config_data) 69 | 70 | if len(key_file) in [24, 32]: 71 | decrypted = crypto.decrypt_des3(key_file, config_data) 72 | 73 | 74 | # Process the decrypted Config 75 | 76 | decrypted = decrypted.decode('utf-8') 77 | 78 | # Clean it up a little 79 | for c in ['\x01', '\x03', '\x04', '\x06', '\x08']: 80 | decrypted = decrypted.replace(c, '') 81 | 82 | fields = decrypted.split('SPLIT') 83 | 84 | 85 | 86 | for field in fields: 87 | if '=' not in field: 88 | continue 89 | key, value = field.split('=') 90 | 91 | if key == 'ip': 92 | config_dict['Domain'] = value 93 | if key == 'addresses': 94 | dom_list = value.split(',') 95 | dom_count = 0 96 | for dom in dom_list: 97 | if dom == '': 98 | continue 99 | config_dict['Domain {0}'.format(dom_count)] = value.split(':')[0] 100 | config_dict['Port {0}'.format(dom_count)] = value.split(':')[1] 101 | dom_count += 1 102 | if key == 'port': 103 | config_dict['Port'] = value 104 | if key == 'os': 105 | config_dict['OS'] = value 106 | if key == 'mport': 107 | config_dict['MPort'] = value 108 | if key == 'perms': 109 | config_dict['Perms'] = value 110 | if key == 'error': 111 | config_dict['Error'] = value 112 | if key == 'reconsec': 113 | config_dict['RetryInterval'] = value 114 | if key == 'ti': 115 | config_dict['TI'] = value 116 | if key == 'pass': 117 | config_dict['Password'] = value 118 | if key == 'id': 119 | config_dict['CampaignID'] = value 120 | if key == 'mutex': 121 | config_dict['Mutex'] = value 122 | if key == 'toms': 123 | config_dict['TimeOut'] = value 124 | if key == 'per': 125 | config_dict['Persistance'] = value 126 | if key == 'name': 127 | config_dict['InstallName'] = value 128 | if key == 'tiemout': 129 | config_dict['TimeOutFlag'] = value 130 | if key == 'debugmsg': 131 | config_dict['DebugMsg'] = value 132 | config_dict["EncryptionKey"] = key_file 133 | 134 | 135 | 136 | 137 | 138 | 139 | # Set the config to the class for use 140 | self.config = config_dict 141 | -------------------------------------------------------------------------------- /malwareconfig/decoders/luminositylink.py: -------------------------------------------------------------------------------- 1 | import re 2 | import binascii 3 | import hashlib 4 | 5 | from base64 import b64decode 6 | 7 | from malwareconfig import crypto 8 | from malwareconfig.common import Decoder 9 | from malwareconfig.common import string_printable 10 | 11 | 12 | class LuminosityLink(Decoder): 13 | decoder_name = "LuminosityLink" 14 | decoder__version = 1 15 | decoder_author = "@kevthehermit" 16 | decoder_description = "LuminosityLink decoder" 17 | 18 | def __init__(self): 19 | self.config = {} 20 | 21 | def get_config(self): 22 | ''' 23 | This is the main entry 24 | :return: 25 | ''' 26 | 27 | # Getting resource is tough so instead we grab the base64 string from the raw 28 | file_data = self.file_info.file_data 29 | re_pattern = b'[a-zA-Z0-9+/]{60,}={0,2}' 30 | conf_string = re.findall(re_pattern, file_data)[0] 31 | # b64decode 32 | conf_string = b64decode(conf_string) 33 | 34 | # Derive Key 35 | key_hash = hashlib.md5('Specify a Password'.encode('utf-8')).hexdigest() 36 | aes_key = key_hash[:30] + key_hash + '00' 37 | 38 | # Decrypt 39 | decrypted = crypto.decrypt_aes(binascii.unhexlify(aes_key), conf_string) 40 | 41 | string_list = decrypted.decode('utf-8').split('|') 42 | 43 | config_dict = {} 44 | config_dict["Domain"] = string_list[0] 45 | config_dict["Port"] = string_list[1] 46 | config_dict["BackUp Domain"] = string_list[2] 47 | config_dict["Install Name"] = string_list[3] 48 | config_dict["Startup Name"] = string_list[4] 49 | config_dict["Campaign ID"] = string_list[5] 50 | 51 | # Set the config to the class for use 52 | self.config = config_dict 53 | -------------------------------------------------------------------------------- /malwareconfig/decoders/luxnet.py: -------------------------------------------------------------------------------- 1 | from malwareconfig.common import Decoder 2 | from malwareconfig.common import string_printable 3 | 4 | 5 | class LuxNet(Decoder): 6 | decoder_name = "LuxNet" 7 | decoder__version = 1 8 | decoder_author = "@kevthehermit" 9 | decoder_description = "Luxnet RAT Decoder" 10 | 11 | def __init__(self): 12 | self.config = {} 13 | 14 | 15 | def get_config(self): 16 | ''' 17 | This is the main entry 18 | :return: 19 | ''' 20 | config_dict = {} 21 | 22 | user_strings = self.file_info.dotnet_user_strings() 23 | base_location = user_strings.index("SocketException") 24 | config_dict['domain'] = user_strings[base_location-2] 25 | config_dict['port'] = user_strings[base_location-1] 26 | 27 | 28 | # Set the config to the class for use 29 | self.config = config_dict 30 | -------------------------------------------------------------------------------- /malwareconfig/decoders/mirai.py: -------------------------------------------------------------------------------- 1 | import re 2 | from malwareconfig import crypto 3 | from malwareconfig.common import Decoder 4 | from malwareconfig.common import string_printable 5 | 6 | from binascii import hexlify, unhexlify 7 | 8 | # temp imports 9 | import re 10 | import zlib 11 | import uuid 12 | from struct import unpack 13 | 14 | class Mirai(Decoder): 15 | decoder_name = "Mirai" 16 | decoder__version = 1 17 | decoder_author = "@kevthehermit" 18 | decoder_description = "Mirai decoder with varients" 19 | 20 | def __init__(self): 21 | self.config = {} 22 | 23 | def get_config(self): 24 | ''' 25 | This is the main entry 26 | :return: 27 | ''' 28 | config_dict = {} 29 | 30 | file_data = self.file_info.file_data 31 | 32 | # Xor everything and look for some key words 33 | # 34 | # Known keys 35 | # 0xDEADBEEF 0x22 36 | 37 | # Do we try to be clever or just bruteforce? 38 | 39 | regex_domain = re.compile(rb"(?!:\/\/)[a-zA-Z0-9-_]+\.*[a-zA-Z0-9][a-zA-Z0-9-_]+\.[a-zA-Z]{2,11}", re.IGNORECASE) 40 | regex_ip = re.compile(rb'(?:[0-9]{1,3}\.){3}[0-9]{1,3}', re.IGNORECASE) 41 | 42 | 43 | match_words = [b'iptables', b'busybox', b'Mozilla'] 44 | false_postive_c2 = [b'resolv.conf', b'schemas.xmlsoap.org', b'tftp.sh'] 45 | c2_list = [] 46 | 47 | for i in range(1,255): 48 | xor_data = crypto.decrypt_xor(bytes([i]), file_data) 49 | if any(word in xor_data for word in match_words): 50 | xor_key = i 51 | domain_results = re.findall(regex_domain, xor_data) 52 | ip_results = re.findall(regex_ip, xor_data) 53 | 54 | for c2 in domain_results: 55 | # Sometimes some xor keys can be a bit annoying. 56 | if c2 not in false_postive_c2: 57 | c2_list.append(c2.decode('utf-8')) 58 | for c2 in ip_results: 59 | if c2 not in false_postive_c2: 60 | c2_list.append(c2.decode('utf-8')) 61 | 62 | config_dict['Commment'] = "The C2 extraction uses a best effort xor decryption. There may be issues with some xor keys like 0x78" 63 | config_dict['C2'] = c2_list 64 | config_dict['xor'] = hex(xor_key) 65 | 66 | self.config = config_dict 67 | 68 | # no match 69 | # wicked, satori-v4, satori-v2, mirai-ssh, jenx 70 | 71 | -------------------------------------------------------------------------------- /malwareconfig/decoders/nanocore.py: -------------------------------------------------------------------------------- 1 | from malwareconfig import crypto 2 | from malwareconfig.common import Decoder 3 | from malwareconfig.common import string_printable 4 | 5 | from binascii import hexlify, unhexlify 6 | 7 | # temp imports 8 | import re 9 | import zlib 10 | import uuid 11 | from struct import unpack 12 | 13 | class NanoCore(Decoder): 14 | decoder_name = "NanoCore" 15 | decoder__version = 1 16 | decoder_author = "@kevthehermit" 17 | decoder_description = "NanoCore decoder for early versions" 18 | 19 | def __init__(self): 20 | self.config = {} 21 | 22 | def get_config(self): 23 | ''' 24 | This is the main entry 25 | :return: 26 | ''' 27 | 28 | dotnet_res_names = self.file_info.dotnet_resource_names() 29 | pe_res_names = self.file_info.pe_resource_names() 30 | 31 | # Get Resource 32 | res_data = False 33 | if b'Data.bin' in dotnet_res_names: 34 | res_data = self.file_info.dotnet_resource_by_name(b'Data.bin') 35 | elif len(pe_res_names) == 1: 36 | res_data = self.file_info.pe_resource_id(pe_res_names[0]) 37 | else: 38 | res_data = self.file_info.pe_resource_id(1) 39 | 40 | 41 | # Return no resource 42 | if not res_data: 43 | print(" [-] No Config Resource Found") 44 | return # Check what we do for a negative result 45 | 46 | 47 | # What Version are we on 48 | 49 | if res_data[0:4] == b'\x08\x00\x00\x00': 50 | conf_dict = self.decrypt_v2(res_data) 51 | 52 | elif res_data[0:4] == b'\x10\x00\x00\x00': 53 | # we need to derive a key from teh assembly guid 54 | guid = self.file_info.dotnet_guids()[1] 55 | #guid = re.search(b'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', file_data).group() 56 | #print(guid) 57 | guid = uuid.UUID(guid).bytes_le 58 | encrypted_key = res_data[4:20] 59 | # rfc2898 derive bytes 60 | #derived_key = derive_key(guid, encrypted_key) 61 | div, dkey = crypto.derive_pbkdf2(guid, guid, 16, 16, iterations=8) 62 | final_key = crypto.decrypt_aes_cbc_iv(dkey, div, encrypted_key) 63 | 64 | conf_dict = self.decrypt_v3(res_data, final_key) 65 | else: 66 | conf_dict = self.decrypt_v1(res_data) 67 | return conf_dict 68 | 69 | 70 | def decrypt_v3(self, coded_config, key): 71 | data = coded_config[24:] 72 | decrypt_key = key[:8] 73 | raw_config = crypto.decrypt_des_cbc(decrypt_key, data, iv=decrypt_key) 74 | # if the config is over a certain size it is compressed. Indicated by a non Null byte 75 | 76 | if raw_config[1] == 0: 77 | return self.parse_config(raw_config, '3') 78 | else: 79 | # remove the string lengths and deflate the remainder of the stream 80 | deflate_config = self.deflate_contents(raw_config) 81 | return self.parse_config(deflate_config, '3') 82 | 83 | def decrypt_v2(self, coded_config): 84 | key = coded_config[4:12] 85 | data = coded_config[16:] 86 | raw_config = crypto.decrypt_des_cbc(key, data, iv=key) 87 | # if the config is over a certain size it is compressed. Indicated by a non Null byte 88 | if raw_config[1] == 0: 89 | return self.parse_config(raw_config, '2') 90 | else: 91 | # remove the string lengths and deflate the remainder of the stream 92 | deflate_config = self.deflate_contents(raw_config) 93 | return self.parse_config(deflate_config, '2') 94 | 95 | def decrypt_v1(self, coded_config): 96 | key = '\x01\x03\x05\x08\x0d\x15\x22\x37' 97 | data = coded_config[1:] 98 | new_data = crypto.decrypt_des_cbc(key, data, iv=key) 99 | if new_data[0] != 0: 100 | deflate_config = self.deflate_contents(new_data) 101 | return self.parse_config(deflate_config, 'old') 102 | 103 | def deflate_contents(self, data): 104 | new_data = data[5:] 105 | return zlib.decompress(new_data, -15) 106 | 107 | # returns pretty config 108 | def parse_config(self, raw_config, ver): 109 | config_dict = {} 110 | 111 | # Some plugins drop in here as exe files. 112 | if b'This program cannot be run' in raw_config: 113 | if b'BuildTime' in raw_config: 114 | raw_config = raw_config.split(b'BuildTime')[1] 115 | else: 116 | raw_config = raw_config.split(b'INSTALL_TITLE')[1] 117 | 118 | 119 | if ver == '2': 120 | #config_dict['BuildTime'] = unpack(">Q", re.search(b'BuildTime(.*?)\x0c', raw_config).group()[10:-1])[0] 121 | config_dict['Version'] = re.search(b'Version\x0c(.*?)\x0c', raw_config).group()[8:-1] 122 | config_dict['Mutex'] = re.search(b'Mutex(.*?)\x0c', raw_config).group()[6:-1] 123 | config_dict['Group'] = re.search(b'DefaultGroup\x0c(.*?)\x0c', raw_config).group()[14:-1] 124 | config_dict['Domain1'] = re.search(b'PrimaryConnectionHost\x0c(.*?)Back', raw_config, re.DOTALL).group()[23:-6] 125 | config_dict['Domain2'] = re.search(b'BackupConnectionHost\x0c(.*?)\x0c', raw_config).group()[22:-1] 126 | config_dict['Port'] = unpack("H', config[8][:2].rstrip(b'\x88'))[0] 32 | counter += 1 33 | return config_dict 34 | 35 | @staticmethod 36 | def config_v2(config_list): 37 | print("Found Version > 1.2") 38 | config_dict = {} 39 | counter = 1 40 | for config in config_list: 41 | config_dict['{}_Domain'.format(counter)] = config[0].rstrip(b'V') 42 | config_dict['{}_URI GET1 Folder'.format(counter)] = config[1].rstrip(b'V') 43 | config_dict['{}_URI GET3 File'.format(counter)] = config[2].rstrip(b'V') 44 | config_dict['{}_URI GET2 File'.format(counter)] = config[3].rstrip(b'V') 45 | config_dict['{}_URI GET3 Arg'.format(counter)] = config[4].rstrip(b'V') 46 | config_dict['{}_Copy File Name'.format(counter)] = config[5].rstrip(b'V') 47 | config_dict['{}_AutoRun Key'.format(counter)] = config[6].rstrip(b'V') 48 | config_dict['{}_Copy File Path'.format(counter)] = config[7].rstrip(b'V') 49 | config_dict['{}_Campaign ID'.format(counter)] = config[8].rstrip(b'V') 50 | config_dict['{}_Waiting Time'.format(counter)] = unpack(' 0: 35 | self.malware_name = scanner.rule_list[0] 36 | 37 | def file_hash(self, hash_type="sha256"): 38 | filehash = '' 39 | if hash_type == "sha256": 40 | filehash = hashlib.sha256(self.file_data).hexdigest() 41 | if hash_type == 'md5': 42 | filehash = hashlib.md5(self.file_data).hexdigest() 43 | return filehash 44 | 45 | def pe_resource_id(self, res_id): 46 | """ 47 | Read resource by its ID. Useful where normal pe file fails. 48 | :return: list 49 | """ 50 | try: 51 | rules = yara.compile(source='import "pe" rule a { condition: false }') 52 | except yara.SyntaxError: 53 | print("Error using Yara DotNet did you enable it?") 54 | resource_list = [] 55 | 56 | def modules_callback(data): 57 | for i, resource in enumerate(data.get('resources', [])): 58 | if 'id' in resource: 59 | if resource['id'] == res_id: 60 | offset = resource['offset'] 61 | length = resource['length'] 62 | self.res_data = self.file_data[offset:offset + length] 63 | elif 'name_string' in resource: 64 | # Remove null bytes for a better comparison 65 | res_name = resource['name_string'].decode('UTF-8').replace('\x00', '') 66 | # Check both unicode and plain str versions of name 67 | 68 | if res_name == res_id or resource['name_string'] == res_id: 69 | offset = resource['offset'] 70 | length = resource['length'] 71 | self.res_data = self.file_data[offset:offset + length] 72 | return yara.CALLBACK_CONTINUE 73 | 74 | rules.match(data=self.file_data, modules_callback=modules_callback) 75 | return self.res_data 76 | 77 | 78 | def pe_resource_names(self): 79 | """ 80 | Read PE Resources and return a list of resource names 81 | :return: list 82 | """ 83 | resource_names = [] 84 | pe = pefile.PE(data=self.file_data) 85 | for rsrc in pe.DIRECTORY_ENTRY_RESOURCE.entries: 86 | for entry in rsrc.directory.entries: 87 | if entry.name is not None: 88 | resource_names.append(entry.name.decode('utf-8')) 89 | return resource_names 90 | 91 | def pe_resource_by_name(self, resource_name): 92 | """ 93 | Extract a PE Resource from a binary by name 94 | :param resource_name: str 95 | :return: byte array 96 | """ 97 | offset = 0x00 98 | size = 0x00 99 | 100 | pe = pefile.PE(data=self.file_data) 101 | for rsrc in pe.DIRECTORY_ENTRY_RESOURCE.entries: 102 | for entry in rsrc.directory.entries: 103 | if entry.name is not None: 104 | if entry.name.__str__() == resource_name: 105 | offset = entry.directory.entries[0].data.struct.OffsetToData 106 | size = entry.directory.entries[0].data.struct.Size 107 | 108 | return pe.get_memory_mapped_image()[offset:offset + size] 109 | 110 | 111 | def dotnet_resource_names(self): 112 | """ 113 | Read .NET Resources and return a list of resource names 114 | :return: list 115 | """ 116 | try: 117 | rules = yara.compile(source='import "dotnet" rule a { condition: false }') 118 | except yara.SyntaxError: 119 | print("Error using Yara DotNet did you enable it?") 120 | resource_list = [] 121 | 122 | def modules_callback(data): 123 | for i, resource in enumerate(data.get('resources', [])): 124 | resource_list.append(resource['name']) 125 | return yara.CALLBACK_CONTINUE 126 | 127 | rules.match(data=self.file_data, modules_callback=modules_callback) 128 | return resource_list 129 | 130 | def dotnet_resource_by_name(self, resource_name): 131 | """ 132 | Extract a .NET Resource by name 133 | :param resource_name: 134 | :return: 135 | """ 136 | try: 137 | rules = yara.compile(source='import "dotnet" rule a { condition: false }') 138 | except yara.SyntaxError: 139 | print("Error using Yara DotNet did you enable it?") 140 | 141 | def modules_callback(data): 142 | for i, resource in enumerate(data.get('resources', [])): 143 | if resource['name'] == resource_name: 144 | offset = resource['offset'] 145 | length = resource['length'] 146 | self.res_data = self.file_data[offset:offset + length] 147 | 148 | 149 | return yara.CALLBACK_CONTINUE 150 | 151 | rules.match(data=self.file_data, modules_callback=modules_callback) 152 | return self.res_data 153 | 154 | def dotnet_guids(self): 155 | """ 156 | Exrtract GUIDS from a .NET Binary 157 | :return: list of guids 158 | """ 159 | try: 160 | rules = yara.compile(source='import "dotnet" rule a { condition: false }') 161 | except yara.SyntaxError: 162 | print("Error using Yara DotNet did you enable it?") 163 | guid_list = [] 164 | 165 | def modules_callback(data): 166 | 167 | for i, guid in enumerate(data.get('guids', [])): 168 | guid_list.append(guid.decode('utf-8')) 169 | # Type lib is also valid as a GUID for nanocore so lets add that. 170 | guid_list.append(data.get('typelib').decode('utf-8')) 171 | return yara.CALLBACK_CONTINUE 172 | 173 | rules.match(data=self.file_data, modules_callback=modules_callback) 174 | return guid_list 175 | 176 | def dotnet_user_strings(self): 177 | """ 178 | Parse a list of User Strings from a .NET Binary file 179 | :return: list of strings 180 | """ 181 | try: 182 | rules = yara.compile(source='import "dotnet" rule a { condition: false }') 183 | except yara.SyntaxError: 184 | print("Error using Yara DotNet did you enable it?") 185 | user_strings = [] 186 | 187 | def modules_callback(data): 188 | for i, userstring in enumerate(data.get('user_strings', [])): 189 | # Remove null bytes 190 | userstring = userstring.replace(b'\x00', b'') 191 | 192 | # Add string to list 193 | try: 194 | user_strings.append(userstring.decode('utf-8')) 195 | except UnicodeDecodeError: 196 | pass 197 | 198 | return yara.CALLBACK_CONTINUE 199 | 200 | rules.match(data=self.file_data, modules_callback=modules_callback) 201 | 202 | return user_strings 203 | 204 | 205 | def ascii_strings(self, min_len=4): 206 | """ 207 | parse a list of ascii strings from a binary file 208 | :return: 209 | """ 210 | string_list = [] 211 | chars = b" !\"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}\\\~\t" 212 | regexp = b'[%s]{%d,}' % (chars, min_len) 213 | pattern = re.compile(regexp) 214 | for s in pattern.finditer(self.file_data): 215 | string_list.append(s.group()) 216 | return string_list 217 | 218 | def unicode_strings(self, min_len=4): 219 | string_list = [] 220 | chars = r" !\"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}\\\~\t" 221 | regexp = b'((?:[%s]\x00){%d,})' % (chars, min_len) 222 | pattern = re.compile(regexp) 223 | for s in pattern.finditer(self.file_data): 224 | string_list.append(s.group()) 225 | 226 | def file_from_zip(self, filename): 227 | new_zip = io.BytesIO(self.file_data) 228 | with ZipFile(new_zip, 'r') as open_zip: 229 | for name in open_zip.namelist(): 230 | if name == filename: 231 | zip_data = open_zip.read(name) 232 | return zip_data 233 | 234 | def zip_namelist(self): 235 | new_zip = io.BytesIO(self.file_data) 236 | filelist = [] 237 | with ZipFile(new_zip, 'r') as open_zip: 238 | for name in open_zip.namelist(): 239 | filelist.append(name) 240 | return filelist 241 | 242 | 243 | def parse_apk(self): 244 | a, d, dx = misc.AnalyzeAPK(self.file_data, raw=True) 245 | return a,d,dx 246 | 247 | 248 | def elf_list_sections(self): 249 | """ 250 | Read a list of sections from an elf binary 251 | :return: list of section names 252 | """ 253 | try: 254 | rules = yara.compile(source='import "elf" rule a { condition: false }') 255 | except yara.SyntaxError: 256 | print("Error using Yara ELF did you enable it?") 257 | section_names = [] 258 | 259 | def modules_callback(data): 260 | for i, section in enumerate(data.get('sections', [])): 261 | section_names.append(section['name'].decode('utf-8')) 262 | return yara.CALLBACK_CONTINUE 263 | 264 | rules.match(data=self.file_data, modules_callback=modules_callback) 265 | 266 | return section_names 267 | 268 | 269 | def elf_section_by_name(self, resource_name): 270 | """ 271 | Extract an elf section by name 272 | :param resource_name: 273 | :return: 274 | """ 275 | try: 276 | rules = yara.compile(source='import "elf" rule a { condition: false }') 277 | except yara.SyntaxError: 278 | print("Error using Yara ELF did you enable it?") 279 | 280 | def modules_callback(data): 281 | for i, section in enumerate(data.get('sections', [])): 282 | if section['name'].decode('utf-8') == resource_name: 283 | offset = section['offset'] 284 | length = section['size'] 285 | self.res_data = self.file_data[offset:offset + length] 286 | return yara.CALLBACK_CONTINUE 287 | 288 | rules.match(data=self.file_data, modules_callback=modules_callback) 289 | return self.res_data -------------------------------------------------------------------------------- /malwareconfig/modules.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import inspect 3 | import pkgutil 4 | 5 | from malwareconfig.common import Decoder, PreProcessor 6 | from malwareconfig import decoders 7 | from malwareconfig import preprocessors 8 | 9 | def load_decoders(): 10 | 11 | dec_modules = dict() 12 | 13 | # Walk recursively through all modules and packages. 14 | for loader, module_name, ispkg in pkgutil.walk_packages(decoders.__path__, decoders.__name__ + '.'): 15 | # If current item is a package, skip. 16 | if ispkg: 17 | continue 18 | # Try to import the module, otherwise skip. 19 | try: 20 | module = importlib.import_module(module_name) 21 | except ImportError as e: 22 | print("Unable to import Module {0}: {1}".format(module_name, e)) 23 | continue 24 | 25 | for mod_name, mod_object in inspect.getmembers(module): 26 | if inspect.isclass(mod_object): 27 | if issubclass(mod_object, Decoder) and mod_object is not Decoder: 28 | dec_modules[mod_object.decoder_name] = dict(obj=mod_object, 29 | decoder_name=mod_object.decoder_name, 30 | decoder_description=mod_object.decoder_description, 31 | decoder_version=mod_object.decoder_version, 32 | decoder_author=mod_object.decoder_author 33 | ) 34 | return dec_modules 35 | 36 | 37 | def load_preprocessors(): 38 | 39 | pre_modules = dict() 40 | 41 | # Walk recursively through all modules and packages. 42 | for loader, module_name, ispkg in pkgutil.walk_packages(preprocessors.__path__, preprocessors.__name__ + '.'): 43 | # If current item is a package, skip. 44 | if ispkg: 45 | continue 46 | # Try to import the module, otherwise skip. 47 | try: 48 | module = importlib.import_module(module_name) 49 | except ImportError as e: 50 | print("Unable to import Module") 51 | continue 52 | 53 | for mod_name, mod_object in inspect.getmembers(module): 54 | if inspect.isclass(mod_object): 55 | if issubclass(mod_object, PreProcessor) and mod_object is not PreProcessor: 56 | pre_modules[mod_object.preprocessor_name] = dict(obj=mod_object, 57 | preprocessor_name=mod_object.preprocessor_name, 58 | preprocessor_description=mod_object.preprocessor_description, 59 | preprocessor_version=mod_object.preprocessor_version, 60 | preprocessor_author=mod_object.preprocessor_author 61 | ) 62 | return pre_modules 63 | 64 | 65 | __decoders__ = load_decoders() 66 | __preprocessors__ = load_preprocessors() 67 | -------------------------------------------------------------------------------- /malwareconfig/preprocessors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevthehermit/RATDecoders/d675ba1c06e6dd8365149c9ee8a8db1a6e5e508e/malwareconfig/preprocessors/__init__.py -------------------------------------------------------------------------------- /malwareconfig/preprocessors/upx.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | from malwareconfig.yarascanner import YaraScanner 5 | from malwareconfig.common import PreProcessor 6 | from tempfile import NamedTemporaryFile 7 | 8 | 9 | class UPX(PreProcessor): 10 | preprocessor_name = "UPX" 11 | preprocessor__version = 1 12 | preprocessor_author = "@kevthehermit" 13 | preprocessor_description = "Unpack UPX Samples" 14 | 15 | def __init__(self): 16 | self.config = {} 17 | 18 | def pre_process(self): 19 | ''' 20 | This is the main entry 21 | :return: 22 | ''' 23 | 24 | file_data = self.file_info.file_data 25 | 26 | f = NamedTemporaryFile(delete=False) 27 | f.write(file_data) 28 | f.close() 29 | 30 | try: 31 | # Run UPX to unpack on the command line 32 | with open(os.devnull, 'w') as devnull: 33 | # this will also redirect stderr to /dev/null as well 34 | subprocess.run(['upx', '-d', f.name], stdout=devnull, stderr=devnull) 35 | 36 | # Read the new file 37 | new_data = open(f.name, 'rb').read() 38 | 39 | # Remove the UPX temp file 40 | os.unlink(f.name) 41 | 42 | # Set the file data to the new data 43 | self.file_info.file_data = new_data 44 | 45 | # Scan with yara 46 | print(" [-] Rescanning File") 47 | scanner = YaraScanner() 48 | scanner.yara_scan(self.file_info.file_data) 49 | self.file_info.malware_name = scanner.rule_list[0] 50 | print(" [-] Detected: {0}".format(self.file_info.malware_name)) 51 | 52 | except Exception as e: 53 | print("Failed to Unpack sample: {0}".format(e)) -------------------------------------------------------------------------------- /malwareconfig/yaraRules/AAR.yar: -------------------------------------------------------------------------------- 1 | rule AAR 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/AAR" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "Hashtable" 12 | $b = "get_IsDisposed" 13 | $c = "TripleDES" 14 | $d = "testmemory.FRMMain.resources" 15 | $e = "$this.Icon" wide 16 | $f = "{11111-22222-20001-00001}" wide 17 | $g = "@@@@@" 18 | 19 | condition: 20 | all of them 21 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Adzok.yar: -------------------------------------------------------------------------------- 1 | rule Adzok 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | Description = "Adzok Rat" 6 | Versions = "Free 1.0.0.3," 7 | date = "2015/05" 8 | ref = "http://malwareconfig.com/stats/Adzok" 9 | maltype = "Remote Access Trojan" 10 | filetype = "jar" 11 | 12 | strings: 13 | $a1 = "config.xmlPK" 14 | $a2 = "key.classPK" 15 | $a3 = "svd$1.classPK" 16 | $a4 = "svd$2.classPK" 17 | $a5 = "Mensaje.classPK" 18 | $a6 = "inic$ShutdownHook.class" 19 | $a7 = "Uninstall.jarPK" 20 | $a8 = "resources/icono.pngPK" 21 | 22 | condition: 23 | 7 of ($a*) 24 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/AlienSpy.yar: -------------------------------------------------------------------------------- 1 | rule AlienSpy 2 | { 3 | meta: 4 | author = "Kevin Breen" 5 | ref = "http://malwareconfig.com/stats/AlienSpy" 6 | maltype = "Remote Access Trojan" 7 | filetype = "jar" 8 | 9 | strings: 10 | $PK = "PK" 11 | $MF = "META-INF/MANIFEST.MF" 12 | 13 | $a1 = "a.txt" 14 | $a2 = "b.txt" 15 | $a3 = "Main.class" 16 | 17 | $b1 = "ID" 18 | $b2 = "Main.class" 19 | $b3 = "plugins/Server.class" 20 | 21 | $c1 = "resource/password.txt" 22 | $c2 = "resource/server.dll" 23 | 24 | $d1 = "java/stubcito.opp" 25 | $d2 = "java/textito.isn" 26 | 27 | $e1 = "java/textito.text" 28 | $e2 = "java/resources.xsx" 29 | 30 | $f1 = "amarillo/asdasd.asd" 31 | $f2 = "amarillo/adqwdqwd.asdwf" 32 | 33 | $g1 = "config/config.perl" 34 | $g2 = "main/Start.class" 35 | 36 | $o1 = "config/config.ini" 37 | $o2 = "windows/windows.ini" 38 | $o3 = "components/linux.plsk" 39 | $o4 = "components/manifest.ini" 40 | $o5 = "components/mac.hwid" 41 | 42 | 43 | condition: 44 | $PK at 0 and $MF and 45 | (all of ($a*) or all of ($b*) or all of ($c*) or all of ($d*) or all of ($e*) or all of ($f*) or all of ($g*) or any of ($o*)) 46 | } 47 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Alina.yar: -------------------------------------------------------------------------------- 1 | rule Alina { 2 | meta: 3 | author = "Brian Wallace @botnet_hunter" 4 | author_email = "bwall@ballastsecurity.net" 5 | date = "2014-08-09" 6 | description = "Identify Alina" 7 | strings: 8 | $s1 = "Alina v1.0" 9 | $s2 = "POST" 10 | $s3 = "1[0-2])[0-9]" 11 | condition: 12 | all of them 13 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Ap0calypse.yar: -------------------------------------------------------------------------------- 1 | rule Ap0calypse 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Ap0calypse" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "Ap0calypse" 12 | $b = "Sifre" 13 | $c = "MsgGoster" 14 | $d = "Baslik" 15 | $e = "Dosyalars" 16 | $f = "Injecsiyon" 17 | 18 | condition: 19 | all of them 20 | } 21 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Arcom.yar: -------------------------------------------------------------------------------- 1 | rule Arcom 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Arcom" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a1 = "CVu3388fnek3W(3ij3fkp0930di" 12 | $a2 = "ZINGAWI2" 13 | $a3 = "clWebLightGoldenrodYellow" 14 | $a4 = "Ancestor for '%s' not found" wide 15 | $a5 = "Control-C hit" wide 16 | $a6 = {A3 24 25 21} 17 | 18 | condition: 19 | all of them 20 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Bandook.yar: -------------------------------------------------------------------------------- 1 | rule Bandook 2 | { 3 | 4 | meta: 5 | author = " Kevin Breen " 6 | date = "2014/04" 7 | ref = "http://malwareconfig.com/stats/bandook" 8 | maltype = "Remote Access Trojan" 9 | filetype = "exe" 10 | 11 | strings: 12 | $a = "aaaaaa1|" 13 | $b = "aaaaaa2|" 14 | $c = "aaaaaa3|" 15 | $d = "aaaaaa4|" 16 | $e = "aaaaaa5|" 17 | $f = "%s%d.exe" 18 | $g = "astalavista" 19 | $h = "givemecache" 20 | $i = "%s\\system32\\drivers\\blogs\\*" 21 | $j = "bndk13me" 22 | 23 | 24 | 25 | condition: 26 | all of them 27 | } 28 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/BlackNix.yar: -------------------------------------------------------------------------------- 1 | rule BlackNix 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/BlackNix" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a1 = "SETTINGS" wide 12 | $a2 = "Mark Adler" 13 | $a3 = "Random-Number-Here" 14 | $a4 = "RemoteShell" 15 | $a5 = "SystemInfo" 16 | 17 | 18 | condition: 19 | all of them 20 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/BlackShades.yar: -------------------------------------------------------------------------------- 1 | rule BlackShades 2 | { 3 | meta: 4 | author = "Brian Wallace (@botnet_hunter)" 5 | date = "2014/04" 6 | ref = "http://blog.cylance.com/a-study-in-bots-blackshades-net" 7 | family = "blackshades" 8 | 9 | strings: 10 | $string1 = "bss_server" 11 | $string2 = "txtChat" 12 | $string3 = "UDPFlood" 13 | condition: 14 | all of them 15 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/BlueBanana.yar: -------------------------------------------------------------------------------- 1 | rule BlueBanana 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/BlueBanana" 7 | maltype = "Remote Access Trojan" 8 | filetype = "Java" 9 | 10 | strings: 11 | $meta = "META-INF" 12 | $conf = "config.txt" 13 | $a = "a/a/a/a/f.class" 14 | $b = "a/a/a/a/l.class" 15 | $c = "a/a/a/b/q.class" 16 | $d = "a/a/a/b/v.class" 17 | 18 | 19 | condition: 20 | all of them 21 | } 22 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Bozok.yar: -------------------------------------------------------------------------------- 1 | rule Bozok 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Bozok" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "getVer" nocase 12 | $b = "StartVNC" nocase 13 | $c = "SendCamList" nocase 14 | $d = "untPlugin" nocase 15 | $e = "gethostbyname" nocase 16 | 17 | condition: 18 | all of them 19 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/ClientMesh.yar: -------------------------------------------------------------------------------- 1 | rule ClientMesh 2 | { 3 | meta: 4 | author = "Kevin Breen " 5 | date = "2014/06" 6 | ref = "http://malwareconfig.com/stats/ClientMesh" 7 | family = "torct" 8 | 9 | strings: 10 | $string1 = "machinedetails" 11 | $string2 = "MySettings" 12 | $string3 = "sendftppasswords" 13 | $string4 = "sendbrowserpasswords" 14 | $string5 = "arma2keyMass" 15 | $string6 = "keylogger" 16 | $conf = {00 00 00 00 00 00 00 00 00 7E} 17 | 18 | condition: 19 | all of them 20 | } 21 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/CyberGate.yar: -------------------------------------------------------------------------------- 1 | rule CyberGate 2 | { 3 | 4 | meta: 5 | author = " Kevin Breen " 6 | date = "2014/04" 7 | ref = "http://malwareconfig.com/stats/CyberGate" 8 | maltype = "Remote Access Trojan" 9 | filetype = "exe" 10 | 11 | strings: 12 | $string1 = {23 23 23 23 40 23 23 23 23 E8 EE E9 F9 23 23 23 23 40 23 23 23 23} 13 | $string2 = {23 23 23 23 40 23 23 23 23 FA FD F0 EF F9 23 23 23 23 40 23 23 23 23} 14 | $string3 = "EditSvr" 15 | $string4 = "TLoader" 16 | $string5 = "Stroks" 17 | $string6 = "####@####" 18 | $res1 = "XX-XX-XX-XX" 19 | $res2 = "CG-CG-CG-CG" 20 | 21 | condition: 22 | all of ($string*) and any of ($res*) 23 | } 24 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/DarkComet.yar: -------------------------------------------------------------------------------- 1 | rule DarkComet 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/DarkComet" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | // Versions 2x 12 | $a1 = "#BOT#URLUpdate" 13 | $a2 = "Command successfully executed!" 14 | $a3 = "MUTEXNAME" wide 15 | $a4 = "NETDATA" wide 16 | // Versions 3x & 4x & 5x 17 | $b1 = "FastMM Borland Edition" 18 | $b2 = "%s, ClassID: %s" 19 | $b3 = "I wasn't able to open the hosts file" 20 | $b4 = "#BOT#VisitUrl" 21 | $b5 = "#KCMDDC" 22 | 23 | 24 | 25 | condition: 26 | all of ($a*) or all of ($b*) 27 | } 28 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/DarkRAT.yar: -------------------------------------------------------------------------------- 1 | rule DarkRAT 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/DarkRAT" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "@1906dark1996coder@" 12 | $b = "SHEmptyRecycleBinA" 13 | $c = "mciSendStringA" 14 | $d = "add_Shutdown" 15 | $e = "get_SaveMySettingsOnExit" 16 | $f = "get_SpecialDirectories" 17 | $g = "Client.My" 18 | 19 | condition: 20 | all of them 21 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Greame.yar: -------------------------------------------------------------------------------- 1 | rule Greame 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Greame" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = {23 23 23 23 40 23 23 23 23 E8 EE E9 F9 23 23 23 23 40 23 23 23 23} 12 | $b = {23 23 23 23 40 23 23 23 23 FA FD F0 EF F9 23 23 23 23 40 23 23 23 23} 13 | $c = "EditSvr" 14 | $d = "TLoader" 15 | $e = "Stroks" 16 | $f = "Avenger by NhT" 17 | $g = "####@####" 18 | $h = "GREAME" 19 | 20 | 21 | 22 | condition: 23 | all of them 24 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/HawkEye.yar: -------------------------------------------------------------------------------- 1 | rule HawkEye 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2015/06" 6 | ref = "http://malwareconfig.com/stats/HawkEye" 7 | maltype = "KeyLogger" 8 | filetype = "exe" 9 | 10 | strings: 11 | $key = "HawkEyeKeylogger" wide 12 | $salt = "099u787978786" wide 13 | $string1 = "HawkEye_Keylogger" wide 14 | $string2 = "holdermail.txt" wide 15 | $string3 = "wallet.dat" wide 16 | $string4 = "Keylog Records" wide 17 | $string5 = "" wide 18 | $string6 = "\\pidloc.txt" wide 19 | $string7 = "BSPLIT" wide 20 | 21 | 22 | condition: 23 | $key and $salt and all of ($string*) 24 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Imminent3.yar: -------------------------------------------------------------------------------- 1 | rule Imminent 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Imminent" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $v1a = "DecodeProductKey" 12 | $v1b = "StartHTTPFlood" 13 | $v1c = "CodeKey" 14 | $v1d = "MESSAGEBOX" 15 | $v1e = "GetFilezillaPasswords" 16 | $v1f = "DataIn" 17 | $v1g = "UDPzSockets" 18 | $v1h = {52 00 54 00 5F 00 52 00 43 00 44 00 41 00 54 00 41} 19 | 20 | $v2a = "k__BackingField" 21 | $v2b = "k__BackingField" 22 | $v2c = "DownloadAndExecute" 23 | $v2d = "-CHECK & PING -n 2 127.0.0.1 & EXIT" wide 24 | $v2e = "england.png" wide 25 | $v2f = "Showed Messagebox" wide 26 | condition: 27 | all of ($v1*) or all of ($v2*) 28 | } 29 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Infinity.yar: -------------------------------------------------------------------------------- 1 | rule Infinity 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Infinity" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "CRYPTPROTECT_PROMPTSTRUCT" 12 | $b = "discomouse" 13 | $c = "GetDeepInfo" 14 | $d = "AES_Encrypt" 15 | $e = "StartUDPFlood" 16 | $f = "BATScripting" wide 17 | $g = "FBqINhRdpgnqATxJ.html" wide 18 | $i = "magic_key" wide 19 | 20 | condition: 21 | all of them 22 | } 23 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/JavaDropper.yar: -------------------------------------------------------------------------------- 1 | rule JavaDropper 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2015/10" 6 | ref = "http://malwareconfig.com/stats/AlienSpy" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $jar = "META-INF/MANIFEST.MF" 12 | 13 | $a1 = "ePK" 14 | $a2 = "kPK" 15 | 16 | $b1 = "config.ini" 17 | $b2 = "password.ini" 18 | 19 | $c1 = "stub/stub.dll" 20 | 21 | $d1 = "c.dat" 22 | 23 | condition: 24 | $jar and (all of ($a*) or all of ($b*) or all of ($c*) or all of ($d*)) 25 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/LostDoor.yar: -------------------------------------------------------------------------------- 1 | rule LostDoor 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/LostDoor" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a0 = {0D 0A 2A 45 44 49 54 5F 53 45 52 56 45 52 2A 0D 0A} 12 | $a1 = "*mlt* = %" 13 | $a2 = "*ip* = %" 14 | $a3 = "*victimo* = %" 15 | $a4 = "*name* = %" 16 | $b5 = "[START]" 17 | $b6 = "[DATA]" 18 | $b7 = "We Control Your Digital World" wide ascii 19 | $b8 = "RC4Initialize" wide ascii 20 | $b9 = "RC4Decrypt" wide ascii 21 | 22 | condition: 23 | all of ($a*) or all of ($b*) 24 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/LuminosityLink.yar: -------------------------------------------------------------------------------- 1 | rule LuminosityLink 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/LuminosityLink" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "SMARTLOGS" wide 12 | $b = "RUNPE" wide 13 | $c = "b.Resources" wide 14 | $d = "CLIENTINFO*" wide 15 | $e = "Invalid Webcam Driver Download URL, or Failed to Download File!" wide 16 | $f = "Proactive Anti-Malware has been manually activated!" wide 17 | $g = "REMOVEGUARD" wide 18 | $h = "C0n1f8" wide 19 | $i = "Luminosity" wide 20 | $j = "LuminosityCryptoMiner" wide 21 | $k = "MANAGER*CLIENTDETAILS*" wide 22 | 23 | condition: 24 | all of them 25 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/LuxNet.yar: -------------------------------------------------------------------------------- 1 | rule LuxNet 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/LuxNet" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "GetHashCode" 12 | $b = "Activator" 13 | $c = "WebClient" 14 | $d = "op_Equality" 15 | $e = "dickcursor.cur" wide 16 | $f = "{0}|{1}|{2}" wide 17 | 18 | condition: 19 | all of them 20 | } 21 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/NanoCore.yar: -------------------------------------------------------------------------------- 1 | rule NanoCore 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/NanoCore" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "NanoCore" 12 | $b = "ClientPlugin" 13 | $c = "ProjectData" 14 | $d = "DESCrypto" 15 | $e = "KeepAlive" 16 | $f = "IPNETROW" 17 | $g = "LogClientMessage" 18 | $h = "|ClientHost" 19 | $i = "get_Connected" 20 | $j = "#=q" 21 | $key = {43 6f 24 cb 95 30 38 39} 22 | 23 | 24 | condition: 25 | 6 of them 26 | } 27 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/NetWire.yar: -------------------------------------------------------------------------------- 1 | rule NetWire 2 | { 3 | meta: 4 | author = " Kevin Breen & David Cannings" 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/NetWire" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | 12 | $exe1 = "%.2d-%.2d-%.4d" 13 | $exe2 = "%s%.2d-%.2d-%.4d" 14 | $exe3 = "[%s] - [%.2d/%.2d/%d %.2d:%.2d:%.2d]" 15 | $exe4 = "wcnwClass" 16 | $exe5 = "[Ctrl+%c]" 17 | $exe6 = "SYSTEM\\CurrentControlSet\\Control\\ProductOptions" 18 | $exe7 = "%s\\.purple\\accounts.xml" 19 | 20 | condition: 21 | all of them 22 | } 23 | 24 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Pandora.yar: -------------------------------------------------------------------------------- 1 | rule Pandora 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Pandora" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "Can't get the Windows version" 12 | $b = "=M=Q=U=Y=]=a=e=i=m=q=u=y=}=" 13 | $c = "JPEG error #%d" wide 14 | $d = "Cannot assign a %s to a %s" wide 15 | $g = "%s, ProgID:" 16 | $h = "clave" 17 | $i = "Shell_TrayWnd" 18 | $j = "melt.bat" 19 | $k = "\\StubPath" 20 | $l = "\\logs.dat" 21 | $m = "1027|Operation has been canceled!" 22 | $n = "466|You need to plug-in! Double click to install... |" 23 | $0 = "33|[Keylogger Not Activated!]" 24 | 25 | condition: 26 | all of them 27 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Paradox.yar: -------------------------------------------------------------------------------- 1 | rule Paradox 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Paradox" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "ParadoxRAT" 12 | $b = "Form1" 13 | $c = "StartRMCam" 14 | $d = "Flooders" 15 | $e = "SlowLaris" 16 | $f = "SHITEMID" 17 | $g = "set_Remote_Chat" 18 | 19 | condition: 20 | all of them 21 | } 22 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Plasma.yar: -------------------------------------------------------------------------------- 1 | rule Plasma 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Plasma" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "Miner: Failed to Inject." wide 12 | $b = "Started GPU Mining on:" wide 13 | $c = "BK: Hard Bot Killer Ran Successfully!" wide 14 | $d = "Uploaded Keylogs Successfully!" wide 15 | $e = "No Slowloris Attack is Running!" wide 16 | $f = "An ARME Attack is Already Running on" wide 17 | $g = "Proactive Bot Killer Enabled!" wide 18 | $h = "PlasmaRAT" wide ascii 19 | $i = "AntiEverything" wide ascii 20 | 21 | condition: 22 | all of them 23 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/PoisonIvy.yar: -------------------------------------------------------------------------------- 1 | rule PoisonIvy 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/PoisonIvy" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $stub = {04 08 00 53 74 75 62 50 61 74 68 18 04} 12 | $string1 = "CONNECT %s:%i HTTP/1.0" 13 | $string2 = "ws2_32" 14 | $string3 = "cks=u" 15 | $string4 = "thj@h" 16 | $string5 = "advpack" 17 | condition: 18 | $stub at 0x1620 and all of ($string*) or (all of them) 19 | } 20 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/PredatorPain.yar: -------------------------------------------------------------------------------- 1 | rule PredatorPain 2 | { 3 | 4 | meta: 5 | author = " Kevin Breen " 6 | date = "2014/04" 7 | ref = "http://malwareconfig.com/stats/PredatorPain" 8 | maltype = "Remote Access Trojan" 9 | filetype = "exe" 10 | 11 | strings: 12 | $string1 = "holderwb.txt" wide 13 | $string3 = "There is a file attached to this email" wide 14 | $string4 = "screens\\screenshot" wide 15 | $string5 = "Disablelogger" wide 16 | $string6 = "\\pidloc.txt" wide 17 | $string7 = "clearie" wide 18 | $string8 = "clearff" wide 19 | $string9 = "emails should be sent to you shortly" wide 20 | $string10 = "jagex_cache\\regPin" wide 21 | $string11 = "open=Sys.exe" wide 22 | $ver1 = "PredatorLogger" wide 23 | $ver2 = "EncryptedCredentials" wide 24 | $ver3 = "Predator Pain" wide 25 | 26 | condition: 27 | 7 of ($string*) and any of ($ver*) 28 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Punisher.yar: -------------------------------------------------------------------------------- 1 | rule Punisher 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Punisher" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "abccba" 12 | $b = {5C 00 68 00 66 00 68 00 2E 00 76 00 62 00 73} 13 | $c = {5C 00 73 00 63 00 2E 00 76 00 62 00 73} 14 | $d = "SpyTheSpy" wide ascii 15 | $e = "wireshark" wide 16 | $f = "apateDNS" wide 17 | $g = "abccbaDanabccb" 18 | 19 | condition: 20 | all of them 21 | } 22 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/PythoRAT.yar: -------------------------------------------------------------------------------- 1 | rule PythoRAT 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/PythoRAT" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "TKeylogger" 12 | $b = "uFileTransfer" 13 | $c = "TTDownload" 14 | $d = "SETTINGS" 15 | $e = "Unknown" wide 16 | $f = "#@#@#" 17 | $g = "PluginData" 18 | $i = "OnPluginMessage" 19 | 20 | condition: 21 | all of them 22 | } 23 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/QRat.yar: -------------------------------------------------------------------------------- 1 | rule QRat 2 | { 3 | meta: 4 | author = "Kevin Breen @KevTheHermit" 5 | date = "2015/08" 6 | ref = "http://malwareconfig.com" 7 | maltype = "Remote Access Trojan" 8 | filetype = "jar" 9 | 10 | strings: 11 | $a0 = "e-data" 12 | $a1 = "quaverse/crypter" 13 | $a2 = "Qrypt.class" 14 | $a3 = "Jarizer.class" 15 | $a4 = "URLConnection.class" 16 | 17 | 18 | condition: 19 | 4 of them 20 | 21 | 22 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/RedLine.yar: -------------------------------------------------------------------------------- 1 | rule RedLine 2 | { 3 | meta: 4 | description = "Identifies RedLine stealer." 5 | author = "@bartblaze" 6 | date = "2021-06" 7 | tlp = "White" 8 | 9 | strings: 10 | $ = "Account" ascii wide 11 | $ = "AllWalletsRule" ascii wide 12 | $ = "ArmoryRule" ascii wide 13 | $ = "AtomicRule" ascii wide 14 | $ = "Autofill" ascii wide 15 | $ = "BrowserExtensionsRule" ascii wide 16 | $ = "BrowserVersion" ascii wide 17 | $ = "Chrome" ascii wide 18 | $ = "CoinomiRule" ascii wide 19 | $ = "CommandLineUpdate" ascii wide 20 | $ = "CryptoHelper" ascii wide 21 | $ = "CryptoProvider" ascii wide 22 | $ = "DataBaseConnection" ascii wide 23 | $ = "DesktopMessangerRule" ascii wide 24 | $ = "DiscordRule" ascii wide 25 | $ = "DisplayHelper" ascii wide 26 | $ = "DownloadAndExecuteUpdate" ascii wide 27 | $ = "DownloadUpdate" ascii wide 28 | $ = "ElectrumRule" ascii wide 29 | $ = "EndpointConnection" ascii wide 30 | $ = "EthRule" ascii wide 31 | $ = "ExodusRule" ascii wide 32 | $ = "Extensions" ascii wide 33 | $ = "FileCopier" ascii wide 34 | $ = "FileScanner" ascii wide 35 | $ = "FileScannerArg" ascii wide 36 | $ = "FileScannerRule" ascii wide 37 | $ = "FileZilla" ascii wide 38 | $ = "GameLauncherRule" ascii wide 39 | $ = "Gecko" ascii wide 40 | $ = "GeoHelper" ascii wide 41 | $ = "GeoInfo" ascii wide 42 | $ = "GeoPlugin" ascii wide 43 | $ = "GuardaRule" ascii wide 44 | $ = "HardwareType" ascii wide 45 | $ = "IpSb" ascii wide 46 | $ = "IRemoteEndpoint" ascii wide 47 | $ = "ITaskProcessor" ascii wide 48 | $ = "JaxxRule" ascii wide 49 | $ = "NordApp" ascii wide 50 | $ = "OpenUpdate" ascii wide 51 | $ = "OpenVPNRule" ascii wide 52 | $ = "OsCrypt" ascii wide 53 | $ = "Program" ascii wide 54 | $ = "ProgramMain" ascii wide 55 | $ = "ProtonVPNRule" ascii wide 56 | $ = "RecordHeaderField" ascii wide 57 | $ = "RecoursiveFileGrabber" ascii wide 58 | $ = "ResultFactory" ascii wide 59 | $ = "ScanDetails" ascii wide 60 | $ = "ScannedBrowser" ascii wide 61 | $ = "ScannedCookie" ascii wide 62 | $ = "ScannedFile" ascii wide 63 | $ = "ScanningArgs" ascii wide 64 | $ = "ScanResult" ascii wide 65 | $ = "SqliteMasterEntry" ascii wide 66 | $ = "StringDecrypt" ascii wide 67 | $ = "SystemHardware" ascii wide 68 | $ = "SystemInfoHelper" ascii wide 69 | $ = "TableEntry" ascii wide 70 | $ = "TaskResolver" ascii wide 71 | $ = "UpdateAction" ascii wide 72 | $ = "UpdateTask" ascii wide 73 | $ = "XMRRule" ascii wide 74 | 75 | condition: 76 | 45 of them 77 | } 78 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Sakula.yar: -------------------------------------------------------------------------------- 1 | rule Sakula 2 | { 3 | meta: 4 | description = "Sakula v1.0" 5 | date = "2015-10-13" 6 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou / NCC Group David Cannings" 7 | strings: 8 | $s1 = "%d_of_%d_for_%s_on_%s" 9 | $s2 = "/c ping 127.0.0.1 & del /q \"%s\"" 10 | $s3 = "=%s&type=%d" 11 | $s4 = "?photoid=" 12 | $s5 = "iexplorer" 13 | $s6 = "net start \"%s\"" 14 | $s7 = "cmd.exe /c rundll32 \"%s\"" 15 | 16 | $v1_1 = "MicroPlayerUpdate.exe" 17 | $v1_2 = "CCPUpdate" 18 | $v1_3 = { 81 3E 78 03 00 00 75 57 8D 54 24 14 52 68 0C 05 41 00 68 01 00 00 80 FF 15 00 F0 40 00 85 C0 74 10 8B 44 24 14 68 2C 31 41 00 50 FF 15 10 F0 40 00 8B 4C 24 14 51 FF 15 24 F0 40 00 E8 0F 09 00 } 19 | $v1_4 = { 50 E8 CD FC FF FF 83 C4 04 68 E8 03 00 00 FF D7 56 E8 54 12 00 00 E9 AE FE FF FF E8 13 F5 FF FF } 20 | 21 | //$serial01 = { 31 06 2e 48 3e 01 06 b1 8c 98 2f 00 53 18 5c 36 } 22 | $serial02 = { 01 a5 d9 59 95 19 b1 ba fc fa d0 e8 0b 6d 67 35 } 23 | $serial03 = { 47 d5 d5 37 2b cb 15 62 b4 c9 f4 c2 bd f1 35 87 } 24 | $serial04 = { 3a c1 0e 68 f1 ce 51 9e 84 dd cd 28 b1 1f a5 42 } 25 | 26 | $opcodes1 = { 89 FF 55 89 E5 83 EC 20 A1 ?? ?? ?? 00 83 F8 00 } 27 | $opcodes2 = { 31 C0 8A 04 0B 3C 00 74 09 38 D0 74 05 30 D0 88 04 0B } 28 | $opcodes3 = { 8B 45 08 8D 0C 02 8A 01 84 C0 74 08 3C ?? 74 04 34 ?? 88 01 } 29 | $opcodes4 = { 30 14 38 8D 0C 38 40 FE C2 3B C6 } 30 | $opcodes5 = { 30 14 39 8D 04 39 41 FE C2 3B CE } 31 | 32 | $MZ = "MZ" 33 | condition: 34 | ($MZ at 0 and (3 of ($s*) and any of ($v1_*))) or (any of ($serial0*)) or (any of ($opcodes*)) 35 | } 36 | 37 | rule sakula_v2_0 38 | { 39 | meta: 40 | description = "Sakula v2.0 - The bytes string matchs a specific decryption routine (xor 0x33) (VirtualAlloc + memcpy + loop)" 41 | date = "2015-10-13" 42 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 43 | strings: 44 | $m = { 8B 75 DC 2B F3 6A 40 68 00 10 00 00 56 6A 00 FF 15 04 20 40 00 8B F8 85 FF 74 4A 56 8B 4D E0 03 CB 51 57 E8 3C 02 00 00 83 C4 0C C7 45 FC 00 00 00 00 B3 33 33 D2 89 55 D8 88 5D E7 3B D6 73 11 0F B6 CB 0F B6 04 3A 33 C8 88 0C 3A FE C3 42 EB E5 FF D7 EB } 45 | 46 | $MZ = "MZ" 47 | condition: 48 | $MZ at 0 and all of them 49 | } 50 | 51 | rule sakula_packed_v2_0 52 | { 53 | meta: 54 | description = "Sakula packer v2.0 - The bytes string matchs 2 concatenated functions. The first function returns the offset of the second function, and the second function returns the payload offset (hardcoded)" 55 | date = "2015-10-13" 56 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 57 | strings: 58 | $m = { 55 8B EC 51 53 56 57 E8 00 00 00 00 58 05 13 00 00 00 89 45 FC 8B 45 FC 5F 5E 5B 8B E5 5D C3 4D } 59 | 60 | $MZ = "MZ" 61 | condition: 62 | all of them 63 | } 64 | 65 | rule sakula_v2_1 66 | { 67 | meta: 68 | description = "Sakula v2.1" 69 | date = "2015-10-13" 70 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 71 | strings: 72 | $m1 = "Sakula" 73 | $m2 = "%d_of_%d_for_%s_on_%s" 74 | $m3 = "Create Child Cmd.exe Process Succeed!" 75 | $v2_1 = "\\drivers\\etc\\hosts" 76 | 77 | $MZ = "MZ" 78 | condition: 79 | $MZ at 0 and all of them 80 | } 81 | 82 | rule sakula_packed_v2_1 83 | { 84 | meta: 85 | description = "Sakula packer v2.1 - The bytes string matchs a specific decryption routine. It starts by xoring the payload many times (an even number) with 0x32. It is cryptographically useless, but it simulates a Sleep. Then, it decrypts the payload with a xor 0x33" 86 | date = "2015-10-13" 87 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 88 | strings: 89 | $m = { 33 C0 B2 32 85 F6 74 0D 30 14 38 8D 0C 38 40 FE C2 3B C6 72 F3 81 FB FF FF 01 00 74 0B 43 81 FB 00 00 00 01 7C DA EB 15 33 C9 B2 33 85 F6 74 0D 30 14 39 8D 04 39 41 FE C2 3B CE 72 F3 83 EC 0C} 90 | 91 | condition: 92 | all of them 93 | } 94 | 95 | rule sakula_v2_2 96 | { 97 | meta: 98 | description = "Sakula v2.2" 99 | date = "2015-10-13" 100 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 101 | strings: 102 | $m1 = "Sakula" 103 | $m2 = "%d_of_%d_for_%s_on_%s" 104 | $m3 = "Create Child Cmd.exe Process Succeed!" 105 | $v2_1 = "\\drivers\\etc\\hosts" 106 | 107 | $MZ = "MZ" 108 | condition: 109 | $MZ at 0 and all of ($m*) and not $v2_1 110 | } 111 | 112 | rule sakula_packed_v2_2 113 | { 114 | meta: 115 | description = "Sakula packer v2.2" 116 | date = "2015-10-13" 117 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 118 | strings: 119 | $m = "goldsunfucker" 120 | 121 | condition: 122 | all of them 123 | } 124 | 125 | rule sakula_v3_0 126 | { 127 | meta: 128 | description = "Sakula v3.0" 129 | date = "2015-10-13" 130 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 131 | strings: 132 | $m1 = "Mozilla/4.0+(compatible;+MSIE+8.0;+Windows+NT+5.1;+SV1)" 133 | $m2 = "ry.db" 134 | $m3 = "cmd.exe /c reg add %s\\Software\\Microsoft\\Windows\\CurrentVersion\\Run /v \"%s\" /t REG_SZ /d \"%s\"" 135 | $m4 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run" 136 | 137 | $MZ = "MZ" 138 | condition: 139 | $MZ at 0 and all of them 140 | } 141 | 142 | rule sakula_v3_1 143 | { 144 | meta: 145 | description = "Sakula v3.1" 146 | date = "2015-10-13" 147 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 148 | strings: 149 | $m1 = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)" 150 | $m2 = ".NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)" 151 | $m3 = "Self Process Id:" 152 | $m4 = "msi.dll" 153 | $m5 = "setup.msi" 154 | $m6 = "%WINDIR%\\system32\\svchost.exe" 155 | 156 | $MZ = "MZ" 157 | condition: 158 | $MZ at 0 and all of them 159 | } 160 | 161 | rule sakula_dropper_v3_1 162 | { 163 | meta: 164 | description = "Sakula v3.1 dropper" 165 | date = "2015-10-13" 166 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 167 | strings: 168 | $m1 = "/c ping 127.0.0.1 & del /q \"%s\"" 169 | $m2 = "%TEMP%\\" 170 | $m3 = "s.exe" 171 | $m4 = "setup.msi" 172 | $m5 = "msi.dll" 173 | condition: 174 | all of them 175 | } 176 | 177 | rule sakula_packed_v3_1 178 | { 179 | meta: 180 | description = "Sakula v3.1 packed shellcode" 181 | date = "2015-10-13" 182 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 183 | strings: 184 | $m1 = "AAAA" 185 | $m2 = "BBBB" 186 | $m3 = "CCCC" 187 | 188 | $MZ = "MZ" 189 | condition: 190 | all of ($m*) and @m1 < @m2 and @m2 < @m3 and $MZ at @m3+4 191 | } 192 | 193 | rule sakula_v3_2 { 194 | meta: 195 | description = "Sakula v3.2" 196 | date = "2015-10-13" 197 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 198 | strings: 199 | $m1 = "/c ping 127.0.0.1 & del /q \"%s\"" 200 | $m2 = "%TEMP%\\" 201 | $m3 = "Emabout.dll" 202 | $m4 = "Thumbs.db" 203 | $m5 = "shutil.dll" 204 | $m6 = "CloseAbout" 205 | $m7 = "rundll32.exe" 206 | 207 | condition: 208 | all of them 209 | } 210 | 211 | rule vx_protector { 212 | meta: 213 | description = "vx protector (used as a protection layer by Sakula) - The bytes string match a specific layer of protection inserted manually before the real code. It decrypts the real code and jumps on it." 214 | date = "2015-10-13" 215 | author = "Airbus Defence and Space Cybersecurity CSIRT - Yoann Francou" 216 | strings: 217 | $m1 = { 89 FF 55 89 E5 83 EC 20 A1 08 30 40 00 83 F8 00 75 0F A1 0C 30 40 00 83 F8 00 75 05 E9 95 00 00 00 E8 FA 60 00 00 89 45 FC 68 88 13 00 00 E8 F3 60 00 00 E8 C8 5F 00 00 83 F8 00 74 E4 89 45 EC E8 DB 60 00 00 2B 45 FC 3D 88 13 00 00 7C D2 8D 45 F8 50 E8 AE 5F 00 00 83 F8 00 74 C4 A1 08 30 40 00 83 F8 00 74 2C 68 E8 03 00 00 E8 B5 60 00 00 8D 45 F0 50 E8 8C 5F 00 00 83 F8 00 74 E8 8B 45 FC 8B 5D F4 39 D8 74 98 8B 45 F8 8B 5D F0 39 D8 74 D4 A1 0C 30 40 00 83 F8 00 74 19 68 88 13 00 00 E8 7F 60 00 00 E8 54 5F 00 00 83 F8 00 74 E2 3B 45 EC 90 90 FF 35 00 30 40 00 B8 1A 30 40 00 BB 71 32 40 00 29 C3 53 68 1A 30 40 00 E8 25 1B 00 00 8D 45 FC 50 6A 40 B8 2A 11 40 00 BB F8 2B 40 00 29 C3 53 68 2A 11 40 00 E8 3C 60 00 00 FF 35 04 30 40 00 B8 2A 11 40 00 BB F8 2B 40 00 29 C3 53 68 2A 11 40 00 E8 EB 1A 00 00 8D 45 FC 50 FF 30 B8 2A 11 40 00 BB F8 2B 40 00 29 C3 53 68 2A 11 40 00 E8 02 60 00 00 EC EC EC EC EC EC} 218 | 219 | condition: 220 | all of them 221 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/ShadowTech.yar: -------------------------------------------------------------------------------- 1 | rule ShadowTech 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/ShadowTech" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "ShadowTech" nocase 12 | $b = "DownloadContainer" 13 | $c = "MySettings" 14 | $d = "System.Configuration" 15 | $newline = "#-@NewLine@-#" wide 16 | $split = "pSIL" wide 17 | $key = "ESIL" wide 18 | 19 | condition: 20 | 4 of them 21 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/SmallNet.yar: -------------------------------------------------------------------------------- 1 | rule SmallNet 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/SmallNet" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $split1 = "!!<3SAFIA<3!!" 12 | $split2 = "!!ElMattadorDz!!" 13 | $a1 = "stub_2.Properties" 14 | $a2 = "stub.exe" wide 15 | $a3 = "get_CurrentDomain" 16 | 17 | condition: 18 | ($split1 or $split2) and (all of ($a*)) 19 | } 20 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/SpyGate.yar: -------------------------------------------------------------------------------- 1 | rule SpyGate 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/SpyGate" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $split = "abccba" 12 | $a1 = "abccbaSpyGateRATabccba" //$a = Version 0.2.6 13 | $a2 = "StubX.pdb" 14 | $a3 = "abccbaDanabccb" 15 | $b1 = "monikerString" nocase //$b = Version 2.0 16 | $b2 = "virustotal1" 17 | $b3 = "get_CurrentDomain" 18 | $c1 = "shutdowncomputer" wide //$c = Version 2.9 19 | $c2 = "shutdown -r -t 00" wide 20 | $c3 = "set cdaudio door closed" wide 21 | $c4 = "FileManagerSplit" wide 22 | $c5 = "Chating With >> [~Hacker~]" wide 23 | 24 | condition: 25 | (all of ($a*) and #split > 40) or (all of ($b*) and #split > 10) or (all of ($c*)) 26 | } 27 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Sub7Nation.yar: -------------------------------------------------------------------------------- 1 | rule Sub7Nation 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Sub7Nation" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a = "EnableLUA /t REG_DWORD /d 0 /f" 12 | $b = "*A01*" 13 | $c = "*A02*" 14 | $d = "*A03*" 15 | $e = "*A04*" 16 | $f = "*A05*" 17 | $g = "*A06*" 18 | $h = "#@#@#" 19 | $i = "HostSettings" 20 | $verSpecific1 = "sevane.tmp" 21 | $verSpecific2 = "cmd_.bat" 22 | $verSpecific3 = "a2b7c3d7e4" 23 | $verSpecific4 = "cmd.dll" 24 | 25 | 26 | condition: 27 | all of them 28 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/TrickBot.yar: -------------------------------------------------------------------------------- 1 | rule TrickBot 2 | { 3 | strings: 4 | $ua1 = "TrickLoader" ascii wide 5 | $ua2 = "TrickBot" ascii wide 6 | $ua3 = "BotLoader" ascii wide 7 | $str1 = "*" ascii wide 8 | $str2 = "group_tag" ascii wide 9 | $str3 = "client_id" ascii wide 10 | condition: 11 | any of ($ua*) or all of ($str*) 12 | } 13 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/UPX.yar: -------------------------------------------------------------------------------- 1 | rule UPX 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | 7 | strings: 8 | $a = "UPX0" 9 | $b = "UPX1" 10 | $c = "UPX!" 11 | 12 | condition: 13 | all of them 14 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Vertex.yar: -------------------------------------------------------------------------------- 1 | rule Vertex 2 | { 3 | 4 | meta: 5 | author = " Kevin Breen " 6 | date = "2014/04" 7 | ref = "http://malwareconfig.com/stats/Vertex" 8 | maltype = "Remote Access Trojan" 9 | filetype = "exe" 10 | 11 | strings: 12 | $string1 = "DEFPATH" 13 | $string2 = "HKNAME" 14 | $string3 = "HPORT" 15 | $string4 = "INSTALL" 16 | $string5 = "IPATH" 17 | $string6 = "MUTEX" 18 | $res1 = "PANELPATH" 19 | $res2 = "ROOTURL" 20 | 21 | condition: 22 | all of them 23 | } 24 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/VirusRat.yar: -------------------------------------------------------------------------------- 1 | rule VirusRat 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/VirusRat" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $string0 = "virustotal" 12 | $string1 = "virusscan" 13 | $string2 = "abccba" 14 | $string3 = "pronoip" 15 | $string4 = "streamWebcam" 16 | $string5 = "DOMAIN_PASSWORD" 17 | $string6 = "Stub.Form1.resources" 18 | $string7 = "ftp://{0}@{1}" wide 19 | $string8 = "SELECT * FROM moz_logins" wide 20 | $string9 = "SELECT * FROM moz_disabledHosts" wide 21 | $string10 = "DynDNS\\Updater\\config.dyndns" wide 22 | $string11 = "|BawaneH|" wide 23 | 24 | condition: 25 | all of them 26 | } 27 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/Xtreme.yar: -------------------------------------------------------------------------------- 1 | rule Xtreme 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/Xtreme" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | ver = "2.9, 3.1, 3.2, 3.5" 10 | 11 | strings: 12 | $a = "XTREME" wide 13 | $b = "ServerStarted" wide 14 | $c = "XtremeKeylogger" wide 15 | $d = "x.html" wide 16 | $e = "Xtreme RAT" wide 17 | 18 | condition: 19 | all of them 20 | } 21 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/adWind.yar: -------------------------------------------------------------------------------- 1 | rule AdWind 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/AAR" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $meta = "META-INF" 12 | $conf = "config.xml" 13 | $a = "Adwind.class" 14 | $b = "Principal.adwind" 15 | 16 | condition: 17 | all of them 18 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/hrat.yar: -------------------------------------------------------------------------------- 1 | rule Hrat 2 | { 3 | meta: 4 | author = "@kevthehermit" 5 | description = "houdini RAT and WSH Rat varients" 6 | strings: 7 | $a1 = "-= config =-" wide ascii nocase 8 | $a2 = "installdir" wide ascii nocase 9 | $a3 = "runAsAdmin" wide ascii nocase 10 | $a4 = "get-pass" wide ascii nocase 11 | $a5 = "get-pass-offline" wide ascii nocase 12 | $a6 = "install-sdk" wide ascii nocase 13 | 14 | $wsh1 = "getKeyLogger()" 15 | $wsh2 = "getRDP()" 16 | $wsh3 = "getReverseProxy()" 17 | $wsh4 = "bin.base64" 18 | 19 | condition: 20 | 3 of ($a*) or 3 of ($wsh*) 21 | } 22 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/jRat.yar: -------------------------------------------------------------------------------- 1 | rule JRat 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/jRat" 7 | maltype = "Remote Access Trojan" 8 | filetype = "Java" 9 | 10 | strings: 11 | $meta = "META-INF" 12 | $key = "key.dat" 13 | $conf = "config.dat" 14 | $jra1 = "enc.dat" 15 | $jra2 = "a.class" 16 | $jra3 = "b.class" 17 | $jra4 = "c.class" 18 | $reClass1 = /[a-z]\.class/ 19 | $reClass2 = /[a-z][a-f]\.class/ 20 | 21 | condition: 22 | ($meta and $key and $conf and #reClass1 > 10 and #reClass2 > 10) or ($meta and $key and all of ($jra*)) 23 | } 24 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/jbifrost.yar: -------------------------------------------------------------------------------- 1 | rule Jbifrost 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2018/02" 6 | maltype = "Remote Access Trojan" 7 | filetype = "Java" 8 | 9 | strings: 10 | $meta = "META-INF" 11 | $a1 = "config.json" 12 | $a2 = "Key1.json" 13 | $a3 = "Key2.json" 14 | $b1 = "sky.drive" 15 | $b2 = "mega.download" 16 | $b3 = "drop.box" 17 | $c = "sq/d.png" 18 | 19 | condition: 20 | $meta and (all of ($a*) or all of ($b*) or $c) 21 | } 22 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/mirai.yar: -------------------------------------------------------------------------------- 1 | rule Mirai 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/AAR" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $a1 = "/proc/cpuinfo" 12 | $a2 = "NewPortMappingDescription" 13 | $a3 = "[DREAMBOX]" 14 | $a4 = "Stale NFS file handle" 15 | $a5 = "Not a XENIX named type file" 16 | $a6 = "/bin/busybox" 17 | $a7 = "dreambox" 18 | $a8 = "GET /bins/" 19 | $a9 = "\x45\x43\x48\x4f\x44\x4f\x4e\x45" 20 | $a10 = "/dev/misc/watchdog" 21 | $a11 = "resolv.conf" 22 | $a12 = "/var/Challenge" 23 | $a13 = "7547" 24 | $a14 = "Too many users" 25 | $a15 = "Connection refused" 26 | $a16 = "Remote I/O error" 27 | $a17 = "POST /cdn-cgi/" 28 | $a19 = "/dev/null" 29 | $a20 = "mdebug.abi" 30 | $a21 = "data.rel.ro" 31 | $a22 = "PMMV" 32 | $a23 = "FGDCWNV" 33 | $a24 = "OMVJGP" 34 | 35 | 36 | $a25 = "exploit_stage=3" 37 | $a26 = "ECHOBOT" 38 | $a27 = "protocol.csp" 39 | 40 | $a28 = "250-Proxy" 41 | $a29 = "/etc/passwd" 42 | $a30 = "Source Address" 43 | $a31 = "connect back IP:PORT" 44 | 45 | $a32 = "185.244.25.217" 46 | 47 | condition: 48 | 2 of them 49 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/njRat.yar: -------------------------------------------------------------------------------- 1 | rule njRat 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/njRat" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | 12 | $s1 = {7C 00 27 00 7C 00 27 00 7C} // |'|'| 13 | $s2 = "netsh firewall add allowedprogram" wide 14 | $s3 = "Software\\Microsoft\\Windows\\CurrentVersion\\Run" wide 15 | $s4 = "yy-MM-dd" wide 16 | 17 | $v1 = "cmd.exe /k ping 0 & del" wide 18 | $v2 = "cmd.exe /c ping 127.0.0.1 & del" wide 19 | $v3 = "cmd.exe /c ping 0 -n 2 & del" wide 20 | 21 | 22 | condition: 23 | all of ($s*) and any of ($v*) 24 | } 25 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/remcos.yar: -------------------------------------------------------------------------------- 1 | rule Remcos 2 | { 3 | meta: 4 | author = "@neonprimetime" 5 | description = "Remcos RAT" 6 | strings: 7 | $a1 = "Software\\Remcos" 8 | $a2 = "\\remcos\\" 9 | $a3 = "REMCOS v" 10 | $b1 = "Keylogger Started" 11 | $b2 = "Connected to C&C" 12 | $b3 = "Screenshots" 13 | $b4 = "OpenCamera" 14 | $b5 = "Uploading file to C&C" 15 | $b6 = "Initializing connection to C&C" 16 | $b7 = "cleared!]" 17 | $b8 = "EnableLUA /t REG_DWORD /d 0" 18 | 19 | $b9 = "Uploading file to C&C" 20 | $b10 = "%02i:%02i:%02i:%03i" 21 | $b11 = "[Firefox StoredLogins Cleared!]" 22 | $b12 = "licence_code.txt" 23 | 24 | condition: 25 | 1 of ($a*) or 3 of ($b*) 26 | } 27 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/saefko.yar: -------------------------------------------------------------------------------- 1 | rule Saefko 2 | { 3 | meta: 4 | author = "@neonprimetime" 5 | description = "Remcos RAT" 6 | strings: 7 | $a1 = "SeafkoAgent" wide ascii nocase 8 | $a2 = "HTTPClinet" wide ascii nocase 9 | $a3 = "INFECTIONS" wide ascii nocase 10 | $a4 = "FileManagerMsgs" wide ascii nocase 11 | $a5 = "ProcessManagerMsg" wide ascii nocase 12 | $a6 = "SAEFKO" wide ascii nocase 13 | 14 | $b1 = "" 15 | $b2 = "" 16 | $b3 = "get_server_address" 17 | $b4 = "irc_clinte" 18 | 19 | condition: 20 | 3 of ($a*) or 3 of ($b*) 21 | } 22 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/spynote.yar: -------------------------------------------------------------------------------- 1 | rule SpyNote 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | maltype = "Remote Access Trojan" 6 | filetype = "apk" 7 | notes = "This needs to be a lot better; this is just for testing" 8 | 9 | strings: 10 | $a = "resources.arscPK" 11 | $b = "res/xml/device_admin.xmlPK" 12 | $c = "AndroidManifest.xmlPK" 13 | $d = "res/drawable/abc_btn_default_mtrl_shape.xmlPK" 14 | 15 | condition: 16 | all of them 17 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/unrecom.yar: -------------------------------------------------------------------------------- 1 | rule unrecom 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/AAR" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $meta = "META-INF" 12 | $conf = "load/ID" 13 | $a = "load/JarMain.class" 14 | $b = "load/MANIFEST.MF" 15 | $c = "plugins/UnrecomServer.class" 16 | 17 | condition: 18 | all of them 19 | } 20 | -------------------------------------------------------------------------------- /malwareconfig/yaraRules/xRAT.yar: -------------------------------------------------------------------------------- 1 | rule xRAT 2 | { 3 | meta: 4 | author = " Kevin Breen " 5 | date = "2014/04" 6 | ref = "http://malwareconfig.com/stats/xRat" 7 | maltype = "Remote Access Trojan" 8 | filetype = "exe" 9 | 10 | strings: 11 | $v1a = "DecodeProductKey" 12 | $v1b = "StartHTTPFlood" 13 | $v1c = "CodeKey" 14 | $v1d = "MESSAGEBOX" 15 | $v1e = "GetFilezillaPasswords" 16 | $v1f = "DataIn" 17 | $v1g = "UDPzSockets" 18 | $v1h = {52 00 54 00 5F 00 52 00 43 00 44 00 41 00 54 00 41} 19 | 20 | $v2a = "k__BackingField" 21 | $v2b = "k__BackingField" 22 | $v2c = "DownloadAndExecute" 23 | $v2d = "-CHECK & PING -n 2 127.0.0.1 & EXIT" wide 24 | $v2e = "england.png" wide 25 | $v2f = "Showed Messagebox" wide 26 | condition: 27 | all of ($v1*) or all of ($v2*) 28 | } -------------------------------------------------------------------------------- /malwareconfig/yaraRules/yaraRules.yar: -------------------------------------------------------------------------------- 1 | include "jbifrost.yar" 2 | include "Alina.yar" 3 | include "Adzok.yar" 4 | include "LostDoor.yar" 5 | include "ShadowTech.yar" 6 | include "xRAT.yar" 7 | include "jRat.yar" 8 | include "UPX.yar" 9 | include "DarkRAT.yar" 10 | include "PoisonIvy.yar" 11 | include "AAR.yar" 12 | include "PythoRAT.yar" 13 | include "VirusRat.yar" 14 | include "unrecom.yar" 15 | include "Imminent3.yar" 16 | include "ClientMesh.yar" 17 | include "Punisher.yar" 18 | include "BlackShades.yar" 19 | include "Xtreme.yar" 20 | include "Sub7Nation.yar" 21 | include "BlackNix.yar" 22 | include "Ap0calypse.yar" 23 | include "Bozok.yar" 24 | include "NetWire.yar" 25 | include "Pandora.yar" 26 | include "Bandook.yar" 27 | include "LuxNet.yar" 28 | include "Vertex.yar" 29 | include "Infinity.yar" 30 | include "Arcom.yar" 31 | include "adWind.yar" 32 | include "Paradox.yar" 33 | include "njRat.yar" 34 | include "SmallNet.yar" 35 | include "Greame.yar" 36 | include "DarkComet.yar" 37 | include "SpyGate.yar" 38 | include "NanoCore.yar" 39 | include "CyberGate.yar" 40 | include "BlueBanana.yar" 41 | include "HawkEye.yar" 42 | include "PredatorPain.yar" 43 | include "Plasma.yar" 44 | include "LuminosityLink.yar" 45 | include "QRat.yar" 46 | include "AlienSpy.yar" 47 | include "JavaDropper.yar" 48 | include "Sakula.yar" 49 | include "remcos.yar" 50 | include "saefko.yar" 51 | include "hrat.yar" 52 | include "spynote.yar" 53 | include "mirai.yar" 54 | include "RedLine.yar" 55 | -------------------------------------------------------------------------------- /malwareconfig/yarascanner.py: -------------------------------------------------------------------------------- 1 | import os 2 | import yara 3 | 4 | class YaraScanner: 5 | def __init__(self): 6 | # Create the list of rules 7 | try: 8 | yara_path = os.path.join(os.path.dirname(__file__), 'yaraRules') 9 | self.yara_rules = os.listdir(yara_path) 10 | self.rule_file = os.path.join(yara_path, 'yaraRules.yar') 11 | self.compiled_rules = yara.compile(self.rule_file) 12 | self.rule_list = [] 13 | except yara.SyntaxError as e: 14 | print("Unable to compile rules. Do you have dotnet enabled") 15 | 16 | # Yara Scanner Returns the Rule Name 17 | def yara_scan(self, raw_data): 18 | matches = self.compiled_rules.match(data=raw_data) 19 | rule_list = [] 20 | 21 | for match in matches: 22 | rule_list.append(match.rule) 23 | self.rule_list = rule_list 24 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pefile 2 | pbkdf2 3 | javaobj-py3 4 | pycrypto 5 | androguard -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from setuptools import setup, find_packages 3 | 4 | with open("README.md", encoding='utf8') as fh: 5 | long_description = fh.read() 6 | 7 | setup( 8 | name='malwareconfig', 9 | version='1.0.4', 10 | author='Kevin Breen', 11 | author_email='thehermit@malwareconfig.com', 12 | description="Malware Config Extraction", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | url='https://malwareconfig.com', 16 | license='GNU V3', 17 | zip_safe=False, 18 | packages=find_packages(), 19 | include_package_data=True, 20 | install_requires=[ 21 | 'pefile', 22 | 'pbkdf2', 23 | 'javaobj-py3', 24 | 'pycrypto', 25 | 'androguard' 26 | ], 27 | scripts=['malconf'], 28 | package_data={'': ['*.yar', 'README.md, LICENSE']} 29 | ) 30 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevthehermit/RATDecoders/d675ba1c06e6dd8365149c9ee8a8db1a6e5e508e/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_decoders.py: -------------------------------------------------------------------------------- 1 | import os 2 | from malwareconfig import fileparser 3 | from malwareconfig.modules import __decoders__ 4 | 5 | def test_decoders_import(): 6 | assert 'AAR' in __decoders__.keys() 7 | assert 'AdWind' in __decoders__.keys() 8 | assert 'Adzok' in __decoders__.keys() 9 | assert 'AlienSpy' in __decoders__.keys() 10 | assert 'Alina' in __decoders__.keys() 11 | assert 'Arcom' in __decoders__.keys() 12 | assert 'BlackNix' in __decoders__.keys() 13 | assert 'BlackShades' in __decoders__.keys() 14 | assert 'BlueBanana' in __decoders__.keys() 15 | assert 'Bozok' in __decoders__.keys() 16 | assert 'ClientMesh' in __decoders__.keys() 17 | assert 'CyberGate' in __decoders__.keys() 18 | assert 'DarkComet' in __decoders__.keys() 19 | assert 'HawkEye' in __decoders__.keys() 20 | assert 'Jbifrost' in __decoders__.keys() 21 | assert 'JRat' in __decoders__.keys() 22 | assert 'LostDoor' in __decoders__.keys() 23 | assert 'LuminosityLink' in __decoders__.keys() 24 | assert 'NanoCore' in __decoders__.keys() 25 | assert 'njRat' in __decoders__.keys() 26 | assert 'Sakula' in __decoders__.keys() 27 | assert 'Xtreme' in __decoders__.keys() 28 | 29 | 30 | def decode_sample(sample_path): 31 | file_info = fileparser.FileParser(file_path=sample_path) 32 | if file_info.malware_name in __decoders__: 33 | module = __decoders__[file_info.malware_name]['obj']() 34 | module.set_file(file_info) 35 | module.get_config() 36 | conf = module.config 37 | return conf 38 | 39 | def test_aar(): 40 | sample_path = "tests/samples/aar" 41 | results = decode_sample(sample_path) 42 | assert results['Version'] == '4.x' 43 | 44 | def test_adwind(): 45 | sample_path = "tests/samples/adwind" 46 | results = decode_sample(sample_path) 47 | assert results['Version'] == 'Adwind RAT v2.0' 48 | 49 | def test_adzok(): 50 | sample_path = "tests/samples/adzok" 51 | results = decode_sample(sample_path) 52 | assert results['Registry Key'] == 'Winhttpsvc' 53 | 54 | def test_alienspy(): 55 | sample_path = "tests/samples/alienspy" 56 | results = decode_sample(sample_path) 57 | assert results['pluginfoldername'] == 'ryfne6pMMZ' 58 | 59 | #def test_alina(): 60 | # sample_path = "tests/samples/alina" 61 | # results = decode_sample(sample_path) 62 | # assert results['pluginfoldername'] == 'ryfne6pMMZ' 63 | 64 | def test_arcom(): 65 | sample_path = "tests/samples/arcom" 66 | results = decode_sample(sample_path) 67 | assert results['Install Name'] == 'vlc.exe' 68 | 69 | def test_blackshades(): 70 | sample_path = "tests/samples/blackshades" 71 | results = decode_sample(sample_path) 72 | assert results['Client Control Port'] == '5555' 73 | 74 | def test_bluebanana(): 75 | sample_path = "tests/samples/bluebanana" 76 | results = decode_sample(sample_path) 77 | assert results['Password'] == '1111' 78 | 79 | def test_blacknix(): 80 | sample_path = "tests/samples/arcom" 81 | results = decode_sample(sample_path) 82 | assert results['Install Name'] == 'vlc.exe' 83 | 84 | def test_bozok(): 85 | sample_path = "tests/samples/bozok" 86 | results = decode_sample(sample_path) 87 | assert results['InstallName'] == 'wmiserver.exe' 88 | 89 | def test_clientmesh(): 90 | sample_path = "tests/samples/clientmesh" 91 | results = decode_sample(sample_path) 92 | assert results['RegistryKey'] == 'Windows Def' 93 | 94 | def test_cybergate(): 95 | sample_path = "tests/samples/cybergate" 96 | results = decode_sample(sample_path) 97 | assert results['CampaignID'] == 'cyber' 98 | 99 | def test_darkcomet(): 100 | sample_path = "tests/samples/darkcomet" 101 | results = decode_sample(sample_path) 102 | assert results['MUTEX'] == 'DC_MUTEX-SEJ8D2Y' 103 | 104 | def test_darkrat(): 105 | sample_path = "tests/samples/darkrat" 106 | results = decode_sample(sample_path) 107 | assert results['Timer Interval'] == b'1000' 108 | 109 | def test_hawkeye(): 110 | sample_path = "tests/samples/hawkeye" 111 | results = decode_sample(sample_path) 112 | assert results['Key6'] == '587' 113 | 114 | def test_hworm(): 115 | sample_path = "tests/samples/hworm/wsh-vbs" 116 | results = decode_sample(sample_path) 117 | assert results['host'] == 'domainname.com' 118 | 119 | #def test_jbifrost(): 120 | # sample_path = "tests/samples/jbifrost" 121 | # results = decode_sample(sample_path) 122 | # assert results['Key6'] == '587' 123 | 124 | 125 | def test_jrat(): 126 | sample_path = "tests/samples/jrat1" 127 | results = decode_sample(sample_path) 128 | assert results['Persistance'] == 'false' 129 | 130 | 131 | def test_lostdoor(): 132 | sample_path = "tests/samples/lostdoor" 133 | results = decode_sample(sample_path) 134 | assert results['Campaign'] == 'My Host' 135 | 136 | def test_luminositylink(): 137 | sample_path = "tests/samples/luminositylink" 138 | results = decode_sample(sample_path) 139 | assert results['Install Name'] == 'sysmon.exe' 140 | 141 | def test_luxnet(): 142 | sample_path = "tests/samples/luxnet" 143 | results = decode_sample(sample_path) 144 | assert results['domain'] == '192.168.50.102' 145 | 146 | def test_nanocore(): 147 | nanocore_tests = { 148 | "1": "Group", 149 | "2": "Kids", 150 | "3": "Group" 151 | } 152 | for filename, groupname in nanocore_tests.items(): 153 | sample_path = os.path.join("tests/samples/nanocore/", filename) 154 | results = decode_sample(sample_path) 155 | assert results['Group'].decode('utf-8') == groupname 156 | 157 | def test_netwire(): 158 | sample_path = "tests/samples/netwire" 159 | results = decode_sample(sample_path) 160 | assert results['Password'] == b'Password' 161 | 162 | def test_njrat(): 163 | njrat_tests = { 164 | "05e": "0.5.0e", 165 | "07d": "0.7d", 166 | "035": "0.3.5", 167 | "036": "0.3.6", 168 | "041": "0.4.1a", 169 | "064": "0.6.4", 170 | "071": "0.7.1" 171 | } 172 | 173 | for filename, version in njrat_tests.items(): 174 | sample_path = os.path.join("tests/samples/njrat/", filename) 175 | results = decode_sample(sample_path) 176 | assert results['version'].lower() == version 177 | 178 | def test_remcos(): 179 | remcos_tests = { 180 | "111": "1.1 Free", 181 | "17pro": "1.7 Pro", 182 | "220": "2.2.0 Light", 183 | "250": "2.5.0 Light" 184 | } 185 | 186 | for filename, version in remcos_tests.items(): 187 | sample_path = os.path.join("tests/samples/remcos/", filename) 188 | results = decode_sample(sample_path) 189 | assert results['version'] == version 190 | 191 | #def test_sakula(): 192 | # sample_path = "tests/samples/sakula" 193 | # results = decode_sample(sample_path) 194 | # print(results) 195 | # assert results['Install Name'] == 'Trojan.exe' 196 | 197 | def test_saefko(): 198 | sample_path = "tests/samples/saefko" 199 | results = decode_sample(sample_path) 200 | assert results['server_pass'] == 'toor' 201 | 202 | def test_spynote(): 203 | spynote_tests = { 204 | "spynote5": "5.0", 205 | "spynote6.4": "2.1.2.79" 206 | } 207 | 208 | for filename, version in spynote_tests.items(): 209 | sample_path = os.path.join("tests/samples/spynote/", filename) 210 | results = decode_sample(sample_path) 211 | assert results['Version'] == version 212 | 213 | def test_xtreme(): 214 | sample_path = "tests/samples/xtreme" 215 | results = decode_sample(sample_path) 216 | assert results['ID'] == 'hack' -------------------------------------------------------------------------------- /tests/test_preprocess.py: -------------------------------------------------------------------------------- 1 | from malwareconfig import fileparser 2 | from malwareconfig.modules import __preprocessors__ 3 | 4 | def test_preproc_import(): 5 | assert 'UPX' in __preprocessors__.keys() 6 | 7 | def test_upx(): 8 | sample_path = 'tests/samples/upx' 9 | file_info = fileparser.FileParser(file_path=sample_path) 10 | module = __preprocessors__['UPX']['obj']() 11 | module.set_file(file_info) 12 | module.pre_process() 13 | assert file_info.malware_name != 'UPX' -------------------------------------------------------------------------------- /tests/test_yara.py: -------------------------------------------------------------------------------- 1 | # Just a simple compile 2 | import yara 3 | 4 | def test_yara_pe(): 5 | yara.compile(source='import "pe" rule a { condition: false }') 6 | 7 | def test_yara_dotnet(): 8 | yara.compile(source='import "dotnet" rule a { condition: false }') 9 | 10 | def test_yara_compile(): 11 | pass 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------