Braodo Analysis

Sep 11, 2025

Executive Summary

In this analysis, I examine a multi-stage malware campaign initiated through a deceptive batch file (health-records-x-ray-pdf.bat) masquerading as a PDF document. The campaign employs layered obfuscation, privilege escalation, scheduled task abuse, PowerShell-based payload delivery, Python obfuscation, and advanced cryptographic routines to harvest sensitive browser and application data. Stolen information—including credentials, cookies, and system metadata—is ultimately exfiltrated via hardcoded Telegram bot tokens.

The infection chain begins with an obfuscated batch script that prompts for elevated privileges and downloads a secondary payload from a shortened URL. Subsequent stages rely on PowerShell to fetch disguised files from GitHub and GitLab, establish persistence through scheduled tasks, and execute a heavily obfuscated Python stealer (prt.py). The Python code leverages AES-GCM and AES-CBC decryption routines, extracting embedded payloads for runtime execution.

Once deobfuscated, the Python stealer demonstrates a sophisticated architecture: it gathers host metadata, enumerates installed browsers and Discord clients, decrypts saved credentials and cookies using DPAPI and cryptographic key derivation, and unlocks locked browser files via Windows Restart Manager APIs. Credentials matching high-value keywords (Facebook, Gmail, Outlook, etc.) are filtered into separate files for attacker convenience.

The malware compresses the stolen data into an in-memory ZIP archive with metadata tags and transmits it to attacker-controlled Telegram bots. To ensure stealth, it deletes temporary files and staging directories post-exfiltration. This operation highlights the evolution of credential-stealing malware toward multi-language payloads, layered obfuscation, and reliance on encrypted cloud-based exfiltration channels.

Infection Chain

Key Findings

  • Initial Access via Fake BAT File

    • Infection begins with health-records-x-ray-pdf.bat, disguised as a PDF.

    • Script performs privilege check; if lacking admin rights, it re-launches itself with elevated privileges using PowerShell -Verb RunAs.

  • Stage 1 – Privilege Escalation & Persistence

    • Downloads rt3-9625.bat via a shortened URL.

    • Establishes persistence through a scheduled task (WindowsSecurityTask) set to run at logon with high privileges.

    • Fetches a disguised .png (actually a batch payload) and a ZIP archive from GitHub/GitLab, extracting a Python stealer (prt.py).

  • Stage 2 – Obfuscated Batch & Python Loader

    • Further downloads (u3t-9625.bat) and executes payloads via hidden PowerShell windows.

    • Executes obfuscated prt.py, which contains embedded .pyc and .pye code encrypted with AES-GCM/AES-CBC.

  • Stage 3 – Python Stealer (Deobfuscated)

    • System Profiling: Collects hostname, username, OS, IP, and geolocation.

    • Persistent Tracking: Generates unique victim ID (id.txt) and maintains infection counter (number.txt).

    • Browser Enumeration & Decryption:

      • Chromium: Extracts DPAPI-protected master keys; decrypts AES-GCM/CBC credential blobs.

      • Gecko (Firefox, etc.): Uses PBKDF2/HMAC/3DES to decrypt logins.

      • Unlocks files via Windows Restart Manager to access locked cookie databases.

    • Data Filtering & Storage: Separates high-value credentials (Facebook, Gmail, Outlook) into Important_Logins.txt and cookies into Facebook_Cookies.txt.

  • Advanced Capabilities

    • Privilege Escalation: Includes LSASS impersonation for SYSTEM-level access.

    • Crypto Functions: Implements AES-GCM, AES-CBC, DPAPI, PBKDF2, and custom key blob parsing.

    • Facebook Ad Account Hijacking: Extracts session cookies, retrieves ad account info via Facebook Graph API.

  • Exfiltration via Telegram

    • Stolen data compressed into timestamped in-memory ZIP archive.

    • Sent via Telegram Bot API using two hardcoded bot tokens and rotating chat IDs.

    • Temporary data and archives are deleted post-exfiltration to minimize forensic traces.

  • Stealth & Persistence

    • Multi-layered obfuscation (batch + PowerShell + Python).

    • Fileless execution of decrypted payloads.

    • Scheduled task ensures persistence across reboots.

    • Cleans up artifacts to evade detection.

Technical Analysis

Stage 0: health-records-x-ray-pdf.bat

MalwareBazaar

A batch file was used for initial access, disguised with the filename health-records-x-ray-pdf.bat to lure the victim into believing it was a legitimate PDF document

vscode

The content initially appeared as unreadable or garbled text—similar to Chinese characters—but this was due to an incorrect text encoding. After converting it to UTF-8, the content became legible and revealed structured code.

This batch script performs the following actions:

  1. Privilege Check & UAC Elevation:

    • It first checks if the script is running with administrator privileges.

    • If not, it invokes a UAC prompt by using PowerShell with the -Verb RunAs flag to re-run itself with elevated privileges.

    • This is handled in the askUAC section using:

  2. Post-Elevation Execution:

    • Once elevated, the script proceeds to the afterAdmin section.

    • It uses cmd /c to run a PowerShell command in a hidden window that downloads a secondary .bat payload from a shortened URL:

    • The downloaded file is saved to the %TEMP% directory and executed, again with a hidden window via:

Stage 1: rt3-9625.bat

The next batch file is obfuscated but contains recognizable labels like askUAC and afterAdmin, indicating a similar privilege escalation and execution flow as the previous script.

I deobfuscated the batch file using DissectMalware’s batch_deobfuscator .

The above executables performs the following actions.

  1. Privilege Escalation:

    • Uses the same method as the previous script (askUAC) to request elevated privileges via PowerShell with -Verb RunAs.

  2. Scheduled Task Creation:

    • Creates a scheduled task named WindowsSecurityTask using schtasks, set to run at logon with the highest privileges.

    • The task points to a batch file located at:

  3. Payload Download & Execution:

    • First PowerShell Command:

      • Sets the TLS protocol.

      • Downloads a .png file from GitHub (disguised .bat) and saves it to the same path used in the scheduled task (WindowSecurity.bat).

    • Second PowerShell Command:

      • Also sets TLS protocol.

      • Downloads a ZIP archive from GitLab.

      • Extracts its contents to C:\Users\Public\Desktops.

      • Sleeps for 1 second.

      • Executes a Python script (prt.py) using a Python interpreter included in the extracted contents.

      • Deletes the ZIP file afterward.

Stage 2: WindowSecurity.bat and u3t-9625.bat

The script executes a cmd process that launches a hidden PowerShell command. This command downloads a .png file from a GitHub URL, saves it as a .bat file in the %TEMP% directory (u3t-9625.bat), and then executes it.

u3t-9625.bat

The script launches a PowerShell process with a hidden window, which executes the prt.py script located in the Desktops folder extracted earlier.

Additionally, an investigation into the GitHub profile https://github.com/827-mh1-3t/827 reveals that this is part of a recurring and ongoing campaign as the article by nullspike was posted on 19th of May 2025.

Stage 3: prt.py

prt.py

The Python file is heavily obfuscated using Pyobfuscate. Upon inspection, it contains embedded .pyc bytecode and even .pye encrypted code. I attempted multiple deobfuscation methods, including using Pyobfuscate's own deobfuscator and various public scripts and tools, but none were effective.

Eventually, I came across an article by NullSpike, where researcher Gurumoorthi had published a Python-based deobfuscation script tailored for this type of obfuscation(Credits to him). I made slight modifications to his script to get it working in my environment.

Python Script to Deobfuscate the code.
Deobfuscated Code

This is the final deobfuscated code. It defines a decryption function d(b, p) that takes two inputs—b (the encrypted data) and p (the password or key material). It uses AES-GCM decryption with a key derived via PBKDF2, and a base85-decoded ciphertext. Once decrypted, it executes the result using exec().

If decryption via the try block fails, the except block defines an alternate AES-CBC-based unpad() function and attempts to decrypt and execute the second-stage payload using a different method, also based on structured values within the pyobfuscate object. I made my own script to do the same and write the output in a file for further investigation as you can see below.

Modified Script

The encryption key, pyc and pye code is present in the obfuscated code itself as you can see in the below snippet:

Reference Image To locate the key and code

Flow Overview

Initialization & Setup

  • Defines string aliases, ImportantKeywords, and environment variables.

  • Prepares working directories under:

  • Hardcodes Telegram bot tokens for exfiltration:

    • 7686322208:AAEqSm20vyfjVi6gz3MeY_Kouf1uGZcok

    • 7529959374:AAHV5sS_Yzf3rpMs6akTn12ErTXQD0xANqw

Persistent IDs & Counters

  • id() creates or retrieves a unique 15-digit victim ID stored in:

  • Counter() manages a session counter in:

Gathering System Info

  • info() collects computer name, username, OS version, IP, and geolocation (via ipinfo.io).

  • Provides metadata context for stolen data.

Browser & Discord Enumeration and Key Extraction

  • Enumerates installed Chromium-based browsers, Gecko-based browsers, and Discord clients from AppData paths.

  • Uses taskkill() to close Chrome, Edge, Brave and unlock files.

  • Extracts browser master keys (get_ch_master_key for Chromium DPAPI, PBKDF2/HMAC/3DES for Gecko).

Credential & Cookie Decryption

  • Decrypts Chromium credentials with decrypt_ch_value() (AES-GCM, CBC).

  • Decrypts Firefox/Gecko logins with decrypt_pbe_password, getGeckoKey, and decrypt3DES.

  • Filters stolen logins against ImportantKeywords (Facebook, Gmail, Outlook, etc.).

Data Storage & File Locks

  • Saves credentials into:

    • All_Passwords.txt

    • Important_Logins.txt

  • Saves cookies into:

    • Facebook_Cookies.txt

    • All_Cookies.txt

  • Uses Windows Restart Manager API to unlock browser files.

Archiving Data

  • Builds in-memory ZIP archive with staged files from PathBrowser.

  • Adds timestamp and bot reference as archive metadata.

Exfiltration via Telegram Bot API

  • Writes ZIP archive to disk temporarily.

  • Prepares message with victim ID, country, username, cookie count, card data count.

  • Exfiltration sent through hardcoded Telegram endpoints:

  • Deletes archive and %TEMP%\Browsers Data after successful send.

In Depth Analysis of the deobfuscated Code with snippets:-

  • These variables are shorthand aliases for strings used later in the malware.

  • Example:

    • _w = 'c_user' → Used to identify Facebook session cookies.

    • _t = 'facebook.com' → Domain filter for Facebook cookies.

    • _a = 'Facebook_Cookies.txt' → Filename where stolen Facebook cookies are stored.

  • _o and _n are hardcoded cryptographic keys later used to help decrypt browser data.

It defines a list of ImportantKeywords that the stealer uses to filter “high-value” credentials such as Facebook, Gmail, Yahoo, Outlook, and other business-related accounts. Stolen logins matching these keywords are stored separately for quick attacker access.

It then sets up environment variables (LOCALAPPDATA, APPDATA, TEMP, USERPROFILE) and constructs a working directory under the Temp folder called Browsers Data, where harvested data will be staged before exfiltration.

Next, it declares hardcoded Telegram bot tokens and chat IDs:

  • TOKEN_BOT_1 = "7686322208:AAEqSm20vyfjVi6gz3MeY_Kouf1uGZcok"

  • TOKEN_BOT_2 = "7529959374:AAHV5sS_Yzf3rpMs6akTn12ErTXQD0xANqw"

  • Plus associated CHAT_ID_NEW and CHAT_ID_RESET values.

These values represent the attacker’s command-and-control infrastructure.

This is the deobfuscated id() function. It builds a path to id.txt inside the user’s Local AppData (using environment variables defined earlier). If the file exists, it reads the stored value; if not, it generates a random 15-digit number, writes it to id.txt, and returns it.

This info() function gathers system and network details. It pulls the computer name, current username, and OS version, then queries https://ipinfo.io for IP address and geolocation data. The results are returned as a dictionary

This Counter() function manages a counter stored in number.txt inside the Local AppData path (similar to how id.txt was handled earlier). If the file exists, it reads the current number, increments it by one, and saves it back. If not, it initializes the value as 1. The updated count is then returned and stored in the variable Count.

This section sets up browser data collection and master key extraction.

  • A dictionary ch_dc_browsers is defined, mapping multiple Chromium-based browsers (Chrome, Brave, Edge, Opera, Vivaldi, etc.) and Discord variants to their respective user-data directories under AppData or LocalAppData.

  • taskkill() is a helper that force-closes Chrome, Edge, or Brave processes to unlock files before stealing data.

  • installed_ch_dc_browsers() iterates over the dictionary and checks which browser/Discord paths exist, returning a list of installed targets.

  • get_ch_master_key(path) attempts to read a Local State file, decode the Base64-encoded encrypted_key, and use the UnprotectData function (Windows DPAPI) to recover the decryption key used for saved browser credentials.

This part handles credential decryption for browsers.

  • decrypt_ch_value(buff, ch_master_key) checks if saved browser values use AES-GCM or older CBC formats and decrypts them with the master key obtained earlier.

  • Additional helpers (decrypt_pbe_password, getGeckoKey, decrypt3DES) implement decryption for Firefox/Gecko-based browsers. They use PBKDF2, SHA1, and HMAC to derive keys from the global salt and master password, then decrypt saved logins with 3DES in CBC mode.

  • Together, these functions let the malware turn encrypted credential blobs from Chromium and Gecko browsers into plaintext usernames and passwords.

This part handles the removal of file locks, likely to access browser files such as cookie stores or login databases.

The function Unlock_Cookies(cookies_path) uses the Windows Restart Manager API (via rstrtmgr.dll) to close applications that may have locked the target file path (cookies_path), such as running browser processes.

  • It starts a Restart Manager session with RmStartSession.

  • Then it registers the cookie file as a resource with RmRegisterResources.

  • RmGetList retrieves the list of applications locking the file.

  • If any are found, RmShutdown attempts to gracefully shut them down.

  • Finally, it ends the session with RmEndSession.

This technique allows the malware to access and read locked browser files (e.g., cookies or login databases) by temporarily releasing OS-level locks without crashing the system or the browser.

The function save_gck_login_data(profiles, profile_name, browser_name) processes login credentials from Gecko-based browsers (like Firefox).

  • It parses the logins.json file in each profile directory, extracting saved credentials.

  • The encryptedUsername and encryptedPassword fields are decrypted using the previously retrieved decryption key and helper functions decodeLoginData() and getKey().

Once credentials are decrypted, the function:

  1. Prints the results: including the URL, username, password, and browser profile.

  2. Performs keyword filtering:

    • It loops through each saved credential and compares the URL against a predefined list of ImportantKeywords (e.g., facebook, gmail, yahoo, outlook).

    • Matches are stored in a file named Important_Logins.txt under a folder structure organized by browser name and profile.

  3. Saves all credentials:

    • Regardless of filtering, all logins are saved to All_Passwords.txt for bulk exfiltration

This part handles password extraction from Gecko-based browsers like Firefox, Waterfox, Pale Moon, SeaMonkey, and others.

  1. get_gck_basepath(browser_type) returns the appropriate base directory path for each supported Gecko-based browser by mapping browser_type to a specific path under AppData.

  2. get_gck_profiles(basepath) reads the profiles.ini file in the browser’s base path and extracts all profile paths using regex. These profiles typically contain saved login credentials and browser data.

  3. get_gck_login_data(browser, path, profile_key) loads logins.json from the login path and:

    • Decrypts stored credentials using B_decrypt_v20 and the master key.

    • Extracts and formats each login entry: origin_url, username, and password.

Like before, it performs ImportantKeywords filtering:

  • High-value credentials (e.g., Facebook, Gmail) are saved to Important_Logins.txt.

  • All credentials are saved to All_Passwords.txt.

This function retrieves and decrypts the Chrome browser’s encryption key used to protect saved passwords.

  • It reads the Local State file to get the encrypted key.

  • Uses Windows CryptUnprotectData (via win32crypt) to decrypt it.

  • Connects to the Windows Service Manager to run some system commands for key extraction.

  • Finally, it returns the decrypted key in a format usable for decrypting Chrome passwords (AES-GCM or ChaCha20).

Function decrypt_v20()

  • This function decrypts data encrypted with AES-256-GCM, such as browser cookies or passwords.

  • It extracts the nonce (initialization vector) and ciphertext from the encrypted value.

  • Uses the provided key to initialize an AES-GCM decryptor and decrypt the ciphertext.

  • Returns the decrypted plaintext or raw decoded string based on the data_type.

Function impersonate_lsass(context manager)

  • This function elevates the script’s privilege to SYSTEM by impersonating the lsass.exe process.

  • It obtains and duplicates the token from the lsass.exe process.

  • Enables debug privileges (SeDebugPrivilege) needed to interact with system processes.

  • Allows running code with SYSTEM-level permissions temporarily inside a with block.

Function parse_key_blob()

Parses a binary cryptographic key blob by reading its header and fields based on the blob type. It extracts key info needed for further decryption or processing. Raises an error if the blob format is unsupported.

Explaination of the above snippet

  • decrypt_with_cng(input_data): Uses Windows CNG API to decrypt input data with different cryptographic providers and keys.

  • byte_xor(ba1, ba2): Performs a byte-wise XOR operation on two byte arrays.

  • derive_v20_master_key(parsed_data): Derives the master key used for Chrome v20 encryption by parsing and processing input data.

  • get_encryption_key_2(browser_path): Retrieves and decrypts the Chrome encryption key using a different approach involving impersonation of LSASS and DPAPI.

fetch_passwords(login_db_path): Connects to the browser’s SQLite login database and extracts saved credentials where passwords are encrypted with the v20 encryption scheme. Returns a list of these login entries.

get_ch_cookies(browser, path, profile, key): Collects cookies from the browser’s cookies database. For Edge and Brave, it kills running processes to unlock files. Queries SQLite to get cookie data, decrypts each cookie value, filters expired ones, and stores the results in a structured format.

save_gck_cookies(profiles, profile_name, browser_name):

  • Connects to the browser’s SQLite cookie database for each profile.

  • Extracts cookie details like host, path, name, value, expiry, security flags.

  • Filters cookies, particularly identifying Facebook cookies separately.

  • Formats and writes the cookies into text files in a designated directory (PathBrowser).

  • Keeps track of the number of saved cookies and updates a global counter.

Explanation of the above

  • __init__(self, cookie) Initializes with a cookie, sets up a requests.Session with custom headers, updates cookies, and retrieves a token.

  • Parse_Cookie(cookie) Parses the cookie string into a dictionary, filtering out some specific keys.

  • Get_Market() Requests Facebook Business content management API, extracts ad account ID and access token.

  • Get_info_Tkqc() Fetches detailed ad account info from Facebook Graph API, including spend cap, balance, account status, payment methods, etc. Returns a formatted summary string with key metrics.

  • ADS_Checker(B) Wrapper that calls Get_info_Tkqc() with error handling and returns ad account data.

  • Gets browser profiles using get_gck_profiles() for multiple base paths.

  • Loops through browsers:

    • Sets browser path and checks for saved encryption key file.

    • Reads and decodes the key if it exists.

    • Otherwise, fetches keys with get_encryption_key_2 and get_encryption_key.

    • Saves the key to a file if found.

  • Finds profile folders inside browser paths with glob.

  • Processes each profile:

    • Retrieves login data and cookie counts.

    • Saves cookies using save_gck_cookies().

  • Loops again through profiles:

    • Determines profile names, handling some special cases.

  • Reads Facebook cookies file:

    • Counts lines containing "c_user" cookies.

  • Exception handling prevents crashes.

Explanation for the above snippet:

  • Creates an in-memory ZIP archive using io.BytesIO() and zipfile.ZipFile.

  • Sets archive filename based on the current date and time.

  • Adds a comment to the ZIP archive indicating the creation time and "Bot Telegram".

  • Walks through the PathBrowser directory:

    • Adds all files and directories inside PathBrowser to the ZIP archive, preserving their relative paths.

  • Iterates through folders in folders_to_archive:

    • Skips any subdirectories starting with "user_data".

    • Walks through each folder’s files and directories.

    • Adds all files and directories to the ZIP, except .zip files.

  • Adds any additional specified files in files_to_archive to the ZIP archive.

  • Exception handling with try-except blocks prevents interruptions if files or directories cannot be accessed or written.

Explanation of the above snippet:-

  • Writes the ZIP archive from memory (zip_data) to disk at archive_path in binary mode.

  • Prepares a message body containing:

    • Country and ID info from info dictionary.

    • Username from the local system.

    • Cookie and card counts.

  • Selects Telegram bot tokens and chat IDs based on the Count variable.

  • Sends the ZIP archive to Telegram using a POST request to the Telegram Bot API sendDocument method:

    • Tries up to 10 times if sending fails.

    • Prints error messages on failure.

  • Deletes the local archive file and directory (PathBrowser) after successful sending.

IOC

URLs / Domains

https://tinyurl.com/mh1-t3-9625

https://raw.githubusercontent.com/ud3-ux/9625-3t/main/ud.png

https://raw.githubusercontent.com/ud3-ux/9625-3t/main/u-p.png

https://raw.githubusercontent.com/827-mh1-3t/9625/main/MH1-T3-9625.png

Telegram API: https://api.telegram.org/bot7686322208:AAEqSm20vyfjVi6gz3MeY_Kouf1uGZcok/sendDocument

Telegram API: https://api.telegram.org/bot7529959374:AAHV5sS_Yzf3rpMs6akTn12ErTXQD0xANqw/sendDocument

Github User Profile:- https://github.com/827-mh1-3t/827/

Telegram Bot Tokens

7686322208:AAEqSm20vyfjVi6gz3MeY_Kouf1uGZcok

7529959374:AAHV5sS_Yzf3rpMs6akTn12ErTXQD0xANqw

File Hashes

health-records-x-ray-pdf.bat - 9021abcfd5a428db4c1bdb4b5ec3e3724de2591ef49bedc7ac40da060d957c23

rt3-9625.bat - 353e89bbe60bce6e74969ce0f6ffa0fc9b2badbda24e81d31b03117cff3a5e0e

WindowSecurity.bat -

f117d27d5d9a27c74bf5309db6994a14a6a87f7d320a43f95fca4c8d33572287

u3t-9625.bat - 67c07b29b4d297a9b9b257836aafa613b86db7af398f53267a4f5074d41734fc

T3-9625.zip - 310fd8a487da7a5c023557a87862eca890db4efd8b6e31ad657634640ee267e5

prt.py - ad3ed926ae11f3f1391b80d1ec793c9cdf176fc49c10945d1e505860bbedd279

MITRE ATT&CK MAPPING

Initial Access

T1204.002 – User Execution: Malicious File

Execution

T1059.003 – Windows Command Shell

T1059.001 – PowerShell

T1059.006 – Python

Persistence

T1053.005 – Scheduled Task/Job: Scheduled Task

Defense Evasion

T1027 – Obfuscated Files or Information

T1140 – Deobfuscate/Decode Files or Information

T1070.004 – Indicator Removal on Host: File Deletion

Credential Access

T1555.003 – Credentials from Web Browsers

T1552.001 – Credentials in Files

Discovery

T1082 – System Information Discovery

T1057 – Process Discovery

Collection

T1005 – Data from Local System

T1114.001 – Local Email Collection

Exfiltration / C2

T1048.003 – Exfiltration Over Unencrypted Non-C2 Protocol

T1071.001 – Application Layer Protocol: Web Protocols

Last updated