OXLOADER: Undocumented Windows Loader Abuses PE .reloc Section to Stage Shellcode and Deliver CASTLESTEALER via Fake Node.js Google Ads

Elastic Security Labs has uncovered OXLOADER, a previously undocumented Windows loader delivering the CASTLESTEALER infostealer through malicious Google Ads impersonating Node.js. It abuses the PE .reloc section to stage shellcode and evades static engines and sandboxes.

Share
OXLOADER: Undocumented Windows Loader Abuses PE .reloc Section to Stage Shellcode and Deliver CASTLESTEALER via Fake Node.js Google Ads

Elastic Security Labs has documented a previously unreported Windows loader it tracks as OXLOADER, observed delivering the CASTLESTEALER infostealer in an active malvertising campaign. The loader posts low detection rates across static engines and sandbox detonations, and the researchers found no prior public reporting on the family before identifying it in a campaign targeting one of their own customers.

Region and language exclusions baked into the malware - it refuses to run on CIS-region systems or Russian-language Windows installs - point to a financially motivated, Russian-speaking operator.

The Delivery Chain

The campaign starts with malvertising. A victim searching for an LTS build of Node.js clicks a sponsored Google result leading to node-js[.]prentiva99[.]info, a landing page impersonating a legitimate Node.js deployment platform. The ads targeted US-based users and were last shown on April 23, 2026. On May 14, 2026, Google removed the advertiser and its campaigns entirely. The advertiser was registered under a verified name based in Ukraine, though whether that reflects the real operator, a front account, or a purchased identity is unclear.

Clicking through redirected the victim via an intermediary domain (app[.]miloyannopoulos[.]com) that issued a 302 to a Windows batch script hosted on Storj's legitimate link-sharing service - a deliberate choice to dodge domain-reputation filtering. The batch script renders a fake software-installation wizard, pulls the next-stage executable from another Storj URL via PowerShell, and launches it with -Verb RunAs to trigger a UAC elevation prompt.

How OXLOADER Hides

The loader's engineering is the story here. The first analyzed sample masqueraded as the legitimate tool API Monitor from rohitab.com, padding itself with real code to slip past static file analyzers.

Execution begins before any user code runs. OXLOADER hijacks a C++ runtime initializer entry during the CRT initialization phase, redirecting one of the table entries to its own function that tail-jumps into the first decryption stub. The loader then unrolls itself through several self-modifying decryption stubs - each patched into memory at runtime before being jumped to. One routine decrypts a 28,233-byte region using a rolling single-byte XOR key that updates after every byte, with the just-decrypted plaintext added back into the key. The same style of routine runs three times across different regions.

To break automated analysis, OXLOADER layers four obfuscation techniques: control-flow flattening, mixed Boolean-Arithmetic, opaque predicates, and function chunking across non-contiguous code regions. Functions are stitched together with unconditional jumps, and some regions are reached through indirect jumps whose targets are computed at runtime via MBA arithmetic. The result is that IDA Pro cannot reliably reconstruct function boundaries without manual intervention. Imports are resolved dynamically using a combination of runtime string decryption and an Adler-32 API hashing algorithm.

Five Checks Before It Runs

Once its APIs are resolved, OXLOADER runs a battery of environment checks designed to avoid sandboxes and analysis VMs:

  • Emulation: calls WNetAddConnection2W with a deliberately malformed resource and expects the error code ERROR_BAD_NAME (0x43), reading the result directly from the TEB. Anything else and it stops.
  • CPU count: requires at least 3 CPUs, filtering out the one or two-core VMs common in automated analysis.
  • RAM: requires at least 3 GB of physical memory via GlobalMemoryStatusEx.
  • Display refresh rate: queries Win32_VideoController over WMI and aborts if the refresh rate is below 20 Hz - headless and default-virtualized configs typically report 0 or 1.
  • Geography and language: uses GetUserGeoID against a hardcoded CIS GEOID list, and GetUserDefaultUILanguage against the Russian (0x419) language ID, halting on a match.

The .reloc Section Abuse

This is the technique defenders should note. After the checks pass, OXLOADER copies the Windows DirectUI Engine DLL (dui70.dll) to a temporary file with a randomly generated name and an .ocx extension - the source of the family's name. It adds a new section named .xtext to that DLL with read/write/execute permissions, then loads the modified DLL into the loader process via LoadLibraryA.

In a normal PE file, the .reloc section holds base-relocation tables used to patch absolute addresses when an image loads at a non-preferred base. OXLOADER instead stuffs malicious shellcode into the .reloc section. As Elastic notes, this is a strong static-analysis red flag - legitimate toolchains do not emit executable code into .reloc. The shellcode is copied from .reloc into the new RWX .xtext section and executed, with yet another decryption stub unpacking the next stage.

OXLOADER to CASTLESTEALER attack summary: six-step infection chain from malicious Node.js Google Ad to in-memory infostealer, five anti-analysis checks, and .reloc section shellcode abuse.

The Payload

The next stage is a DonutLoader-generated position-independent payload - DonutLoader being an open-source tool that wraps .NET assemblies, DLLs, and EXEs into shellcode for in-memory execution. It decrypts its embedded configuration using the Chaskey-LTS block cipher in CTR mode, decompresses via aPLib, and bootstraps the final payload through DonutLoader's RunPE function.

That final payload is CASTLESTEALER, an information stealer recently identified by Huntress. Elastic attributes it based on a shared AES key used for C2 communications, found between 0xDEADBEEF markers matching a previous sample.

A second OXLOADER variant swapped the API Monitor disguise for a Node.js installer masquerade (and at one point a CMake-themed binary), but used the identical loader mechanism and also deployed CASTLESTEALER. Elastic believes the API Monitor sample was likely a distribution error by the operator.

The Takeaway

OXLOADER is early in its operational life, but the engineering investment is the warning sign. The layered obfuscation, five-check anti-analysis gate, benign-code masquerading, and the .reloc staging trick are deliberate choices that are paying off in low detection rates - buying the operator a window to run before the family is widely hunted. The .reloc section abuse in particular is a useful, high-confidence static-detection signal: any PE carrying executable instructions in .reloc rather than relocation entries deserves immediate scrutiny. Defenders should ingest the IOCs below, watch for the malvertising lure (fake Node.js downloads delivered via Storj-hosted batch scripts), and treat CLR-loaded-from-suspicious-memory behavior as a high-priority signal.

Indicators of Compromise

Domains:

  • node-js[.]prentiva99[.]info (malvertising landing page)
  • app[.]miloyannopoulos[.]com (redirector)

CASTLESTEALER C2:

  • 89.124.95[.]161
  • 89.124.115[.]82

SHA-256:

  • fdfc7831e5c24cfa80152860dfe8c056ba079f7df1393bf6bb7b18ed974eda37 (BATPackageBuilderSetup.bat)
  • de4f51649ec1a33071854aefe93ffb3fc225e19f802d8dd914676dd5dfef2615 (BATPackageBulderSetup.bat)
  • 9a9939dff297997732aaade9b243d695632cbd64033c5fbcb9de3d09b7e6c28d (apimonitor-x64.exe / OXLOADER)
  • c85f2765a6c3c3f3907c17e57df12f8f68826f74bff3bbfd272af50666d065fe (node-v24.15.0-x64-86.exe / OXLOADER)
  • 4ec9d9d4d10ad78fc6d7bda7cb17d52984878ccd2dd4302fd1cef152313b9741 (CASTLESTEALER)
  • 39019279686c820c3af5684012a0085a7e2109f612c9fab886dd0577ace5b5c6 (CASTLESTEALER)

Read more