Skip to content

Document viewer and desktop update surfaces (Sept 23 09:30Z wave)

A September 23 09:30Z GitHub advisory wave landed a ~40-GHSA Foxit PDF Editor/Reader pack, the Tauri updater plugin's unsigned-manifest downgrade (companion to the allowDowngrades XSS bridge folded Sept 22), and an Acer NitroSense localhost-listener pair. Individually most items are "memory-safety in a desktop app"; together they form a durable operator playbook for the two surfaces every engagement touches and almost nobody audits systematically: document viewers (the #1 phishing delivery target) and desktop updaters / localhost IPC (the #1 silent-lateral path on endpoints).

1. Signature coverage vs displayed content — incremental-update signature survival (Foxit, CVE-2026-91814 / GHSA-c9r7-69j9-jq5j, medium)

Foxit's handling of incrementally updated PDF documents lets changes to visible content pass while the existing signature still renders as valid. This is the PDF spelling of the invariant already canonical on the May 26 signature-boundary page for Fedify JSON-LD shape confusion: the signature verifies a copy of the document, and the viewer displays a different copy.

  • PDF incremental updates append a new xref/body segment to the original bytes; a signature's byte ranges were fixed at signing time. Content added or re-pointed in the appended segment is outside what was signed but inside what is displayed.
  • Sweep rule for any signed-document validator (PDF, XML-DSG, JOSE-detached, S/MIME with mutable wrappers): take one legitimately signed document, then (a) append an incremental-update segment with a new/changed visible page, (b) re-point an object via the appended xref, and (c) check whether the validator's verdict, the badge in the UI, and the rendered text agree. A valid-badge-over-altered-render is the finding; "signature still validates" is not the negative control — the UI verdict is.
  • Report framing for bounty/consulting work: signed-contract/signed-invoice workflows are the impact story (content spoofing that survives the signature badge), not parser quality.
  • Lab-only: sign your own PDF with a self-signed cert; never present forged signature tests against real signed agreements you don't own.

2. Viewer permission flags are enforced only where code checks them — SMB hash leak with no prompt (Foxit, CVE-2026-91796 / GHSA-3q39-rxpg-hqqg, medium)

Foxit's interface lacks the permission verification required for secure reading mode, so a crafted PDF can trigger external SMB authentication with no security prompt, leaking the user's credential hash (classic \\attacker\share NTLM capture) just from opening the file.

  • Operator axis: document "protected view"/secure-mode restrictions are a policy object the viewer must consult at every outbound primitive (external links, embedded URLs, remote template loads, SMB/UNC references). Any reader feature that opens a network resource is an NTLM-coercion primitive; the prompt/permission layer is a separate gate that gets forgotten per-feature.
  • Initial-access red team pattern: crafted-PDF SMB coercion is the same play as the printer/PDF coercion families — pair with Responder/NTLM-relay posture on the outbound path you can legitimately see; capture hashes against your own responder only, and hash → crack/relay decisions stay inside engagement scope.
  • Hunt rule for any document viewer/editor you're authorized to fuzz: grep/reference-test for every URL/UNC/file-path resolution reachable from document content (annotations, embedded files, XObjects, form actions) and check which ones fire without the user-permission prompt in hardened mode. The missing prompt is the finding.

3. Same-process document isolation — cross-document reads via the JS interface (Foxit, CVE-2026-91788 / GHSA-mw87-v3r3-7j2h, medium)

Foxit's JavaScript interface omits the attribute-authorization checks required by the PDF specification, so a trusted malicious PDF can read sensitive content from other documents open in the same process and transmit it out.

  • Durable axis: desktop apps routinely assume per-document isolation while running all documents in one process with one script engine. Enumerate the script API surface (PDF-LA JS, Office VBA/OMAuto, editor extension APIs) for attributes/objects that resolve "current/other document" globally rather than per-sandbox; a document-scoped security model over a process-scoped object graph is the bug shape.
  • This is the desktop twin of the browser same-origin-object oracles (Sept 22 GHES name-scoped token page): authorization scoped to A, object graph scoped to the containing process/session.
  • Validation stays in a lab with two synthetic documents you own; prove with a marker string from document B surfacing in document A's script context plus an owned listener; never test on documents containing real third-party content.

4. The updater is a privileged file consumer — MITM, swap window, and LPE (Foxit 91812/91813/91803/91800/91798 pack)

Advisory Behavior
CVE-2026-91812 / GHSA-748f-jhmv-j7jm Update mechanism lets MITM attackers bypass certificate validation and package integrity checks → code execution with system privileges.
CVE-2026-91813 / GHSA-597r-vgr7-9622 Update package swapped between download and high-privilege extraction (insufficient file locking + integrity revalidation) → local privileged code execution.
CVE-2026-91803 / 91800 / 91798 Local privilege escalation in the updater, macOS installer, and update daemon (insecure permissions/paths class).

Reusable updater audit — this pair completes the Foxit-style updater checklist for any desktop/daemon updater:

  1. Transport: is the update fetch pinned (cert pinning or pubkey-pinned metadata)? A "TLS" download with validator-bypassable cert checks is a plaintext download for anyone on-path. Test only on lab networks you control with a lab MITM proxy.
  2. Metadata authenticity: is the descriptor (version, URL, hash) signed, or only the payload? See §5 — unsigned descriptors convert a signed-payload guarantee into a downgrade channel.
  3. Swap window: is the downloaded file re-verified at extraction time by the privileged process, or verified earlier by the unprivileged one? Download-dir writability + delayed privileged extraction = TOCTOU LPE — the same extract-before-verify family as Grafana plugins (Sept 18) and the tar-file filter verdict drops (May 6). Check dir ACLs as the low-priv user and the extraction account separately.
  4. Privileged helper hygiene: updater/daemon/installer service — what paths does it write, load DLLs from, or chmod with elevated rights? (World-writable install dirs and unquoted service paths are the classic spells; the advisory pack says all three helper binaries carry class bugs.)

October 3 follow-up: OpenLiteSpeed ≤1.9.2 — the checklist violated by a web server's own updater (CVE-2026-104474, LPE)

GHSA-4qqw-wg6j-834r: admin/misc/lsup.sh runs update packages from a nobody-writable directory (/usr/local/lsws/autoupdate/) as root, with no integrity verification — whoever controls the web worker process (any web-facing RCE or upload-to-exec on the server) swaps the package before extraction and its install.sh runs as root on the next update cycle. Checklist verdict: question 1 (no pinned/verified material), question 3 (the low-priv user owns the drop directory the root consumer later executes), question 4 (root shell script consuming a web-writable path). Generalizes to every web server hosting control-plane: web-writable directory + root-side consumer script is the standing LPE pair — enumerate dirs owned by the worker uid and grep root cron/init/update paths that read them (on lab installs; on engagements the report item is the ACL mismatch, not the swap).

5. Tauri updater: signature covers the binary, manifest is the attack (CVE-2026-95625 / GHSA-p8c6-29q6-cqw7, medium)

The Tauri updater plugin verifies update binaries with minisign signatures, but the manifest — version number, download URL, signature field — is fetched over TLS and never itself signed. The only anti-rollback check compares the manifest's unsigned version field against the current version, so an attacker who can serve a crafted manifest forces installation of any older signed release without the developer's private key.

  • The invariant: authenticate the descriptor, not just the payload. "The binary is signed" tells you nothing until you ask which copy of the update data decides which binary gets installed — here that copy (the manifest) is unauthenticated. Same-shape families already on the wiki: patch-the-payload-vs-check-the-metadata (Sept 18 Grafana extract-before-verify), and if (state==X) verify() skip conditions (Sept 23 payment cluster).
  • Downgrade ≠ theoretical: the Sept 22 allowDowngrades-from-webview bug (CVE-2026-95624) made anti-rollback XSS-disableable from JS; this second advisory makes it network-disableable for anyone in the manifest path (DNS/TLS-terminating middleboxes, compromised-but-not-forged hosting). Two advisories, one component, opposite entry points — when you find one broken gate on an updater, enumerate every field that gates version selection, transport, and acceptance separately.
  • Audit rule for any update framework (Tauri, Squirrel, Sparkle, Electron-updater, OTA stacks): diff what the signature covers vs what the install-decision code reads. Prove in a lab app with your own keypair: serve a valid old release's signed binary under a manifest claiming it's the "update"; acceptance without a fresh-version signature is the finding. Never test against a vendor's live update endpoint.

6. Desktop app localhost inventory — production DevTools and unauthenticated IPC to execSync (Acer NitroSense, CVE-2026-50228/50227 / GHSA-xv7c-3mjr-8rg4, GHSA-x445-jgfc-928v)

Acer NitroSense (preloaded on Acer gaming laptops):

  • Chromium remote debugging left enabled in the production build: unauthenticated local attacker connects to the Electron DevTools endpoint on localhost TCP 9993 → execute JavaScript in the privileged application context → arbitrary code execution.
  • MQTT broker exposed over a localhost WebSocket endpoint with no auth → invoke the app's ddsc RPC functions including child_process.execSync() → arbitrary command execution in the app context.

Durable axes:

  • Post-access local recon: on any Windows/Linux endpoint, enumerate localhost listeners before hunting credentials — OEM/preloaded suites (Acer, ASUS, MSI, Lenovo, Killer/Bigfoot networking stacks) ship always-on localhost RPC services with zero authentication because "localhost = trusted". Quick pass: netstat -ano/Get-NetTCPConnection -State Listen + Get-Process -Id per port, fingerprint the owning binary's version info, fuzz anything that smells like JSON-RPC/MQTT/DevTools (GET /json/version on an unknown port answering JSON = Electron DevTools).
  • DevTools-in-production is a class, not an accident: a --remote-debugging-port build shipped at scale converts any local user into app-context code execution — and browser-driven exploitation (drive-by from web content to localhost) is the escalation leg below.
  • Web-content → local native service bridges: same wave, CGServiSign (CVE-2026-15027 / GHSA-q25p-jh2x-hv2j, high, sparse detail) is OS command injection in a local service interface reachable by inducing a victim to visit a malicious web page — the browser-side variant of the same localhost-trust gap (native service listens on loopback, accepts requests steerable from web content, and reaches a shell). Chinese e-gov/banking sign-controls (Changing/EGoon style) are a large install base worth scanning in targeted-scope internal engagements.
  • Validation: prove with an inert RPC call whose effect you patch/observe (or a JS marker evaluated in your own session on your own machine); never run destructive commands through a discovered execSync sink, and report port + version + auth-state as the evidence.

October 2 follow-up: web content → native messaging host RCE (SConnect, CVE-2026-18397)

  • SConnect native host unauthenticated remote code execution on the victim's machine (CVE-2026-18397 / GHSA-cq27-3937-pqqv, critical, published Oct 2 00:31Z): a combination of cryptographic weaknesses and memory-management issues in the native host component reached through an unrestricted messaging interface between an attacker-controlled web page and the native host — malicious web input bypasses the host's security checks.
  • This generalizes the CGServiSign lesson above from a sparse single to a named class: MFA/SSO client native hosts are browser-reachable command endpoints. Delivery path for authorized phishing-simulation and targeted internal scopes: enumerate registered native-messaging manifests (%APPDATA%\..\NativeMessagingHosts, /usr/lib/mozilla/native-messaging-hosts, Chrome/Chromium policy paths), fingerprint the host binary, then test whether it (a) validates the requesting extension/page identity cryptographically or only trusts a name, (b) rate-limits or schema-checks message fields, and (c) parses attacker-sized fields into memory-unsafe paths. A native host that accepts messages from web content without binding them to an approved extension ID is a drive-by RCE regardless of how "internal" its protocol is.
  • Details withheld in the advisory are the class markers (crypto + memory-safety + web-reachable bridge); treat any vendor advisory worded this way as a native-bridge audit prompt for the installed MFA/VPN/signing suites on the target estate. Proof stays local: your own lab VM with the client installed, inert message-schema probes, crash artifacts only.

Same-wave Foxit memory-safety items — tracked, not published

The remaining ~30 Foxit GHSAs (CVE-2026-91789–91818: UAFs in annotations/JS/page-tree/form handling, heap OOB read/write in WebP/JPEG2000/rendering paths, embedded-resource and attachment path traversal writes 91801/91797, FileOpen encryption-metadata pointer misuse 91795, reentrant-zoom recursion 91791/91792) are the parser memory-safety class already canonical on this wiki (per pypdf/ImageMagick precedent): relevant if you build authorized client-side fuzzing/phishing-simulation campaigns, but no new replayable operator axis beyond §1–§3. Note the two file-write path traversals (91801 embedded resources, 91797 attachment filenames) as the highest-value subset for a delivery-chain engagement — viewer writes outside the intended directory combine with the updater/autoloader locations from §4.

October 5 follow-up: imported-file markup → privileged IPC bridge chain (Twine 2 desktop, CVE-2026-105220)

  • Twine 2 desktop through 2.12.0 (CVE-2026-105220 / GHSA-qx8m-f384-xrj3, high 7.8): importStories() executes markup from imported story files inside the editor window; the story's script then calls the twineElectron openWithScratchFile IPC bridge to write and open a .bat file → code execution as the user. Delivery is a file the user voluntarily imports — the exact phishing posture for a creative-tool desktop app.
  • Durable axes for Electron/desktop audits: (1) renderer-trust assumption check — imported content rendered in the main editor window inherits the renderer's IPC surface; grep the preload/bridge definitions (twineElectron, contextBridge, ipcRenderer.on) for handlers that write a file AND launch it in one call (openWithScratchFile-style two-effect handlers are the worst shape — split-effect your findings around them: which handlers combine filesystem write + process/file open?); (2) file-content XSS as RCE preamble: any "untrusted document rendered in a privileged window" pattern (story, .blend, .mlt, preview panes) should be probed for script execution, then matched against the exposed IPC method list — the chain closes the moment one bridge method reaches a shell or open-with-default-app; (3) audit method: enumerate registered IPC channel names from the app's app.asar (npx @electron/asar extract), classify each as read/write/open/exec-capable, and report the imported-content-to-handler reachability as the finding. Proof: inert marker file written via a scratch handler on your own install; never launch payloads.

October 5 03:3xZ follow-up: renderer XSS escalated through app-owned bridges — Legcord, GitAhead, Chaterm

Four same-wave advisories extend this page's desktop-surface playbook with the "XSS is just the preamble" pattern: the renderer bug is medium-impact; the app's own config/update/protocol bridges turn it into persistence, downgrade, or key theft.

  • Legcord (Discord wrapper) 1.1.0–1.3.0, config-injection leg (CVE-2026-105294 / GHSA-3q64-f25r-4v38, high 7.4): the window.legcord settings.setConfig IPC bridge lets script running in the Discord page origin write any config key — including additionalArguments. A Discord XSS persists --proxy-server and --ignore-certificate-errors as Chromium launch switches, so every subsequent client launch routes all traffic through an attacker interception proxy with TLS verification off. Audit rule: enumerate every settings/config write bridge exposed to the webview and diff writable keys against boot-affecting keys — additionalArguments, commandLineArgs, --proxy-*, --ignore-certificate-errors, --load-extension are Chromium/electron flag-injection sinks where a config write equals a browser downgrade; the persistence (survives restart) makes it a implant-grade beacon, not a one-shot XSS.
  • Legcord same wave, theme IPC leg (CVE-2026-105293 / GHSA-m6xw-v6hf-7jc2, critical 8.1): themes.folder, themes.uninstall, themes.install IPC handlers take unvalidated theme ids, escaping the themes directory to launch local executables, recursively delete directories, and write files outside the confine. Same audit method as the Twine leg above (@electron/asar extract, classify channels): here the handler name implies a safe scope ("themes") but the parameter is a free path/identifier — grep every IPC handler whose path join uses a caller-supplied id without a realpath/prefix re-check, and treat recursive-delete handlers reachable from the renderer as destructive-scope findings on sight.
  • GitAhead 2.5.0–2.7.1, updater leg (CVE-2026-105295 / GHSA-6xm7-h392-mjvq, high 7.5): updates install with no integrity or signature verification, and the client permanently ignores all future TLS errors after the user dismisses one SSL error dialog. A single on-path invalid-cert encounter at any point converts the update channel into permanent code-exec-as-user (fake version offered → user installs). Durable axis for the §4 updater checklist: (a) does a transient TLS error persist a trust-downgrade? grep update-checker code for error-dialog handlers that set persistent ignore flags; (b) unsigned-update = any network position between client and update host is RCE; proof stays lab-side — your own update host, your own cert, inert marker binary.
  • Chaterm before 0.12.1, custom-protocol login leg (CVE-2026-105292 / GHSA-xv7q-7232-3gx8, medium 5.9, durable shape): chaterm:// login callbacks accept attacker-supplied userInfo with no OAuth state validation from any web page — forced login into the attacker's account (login CSRF via custom URL scheme), after which default data sync uploads the victim's saved hosts, passwords, and private keys to that attacker-controlled account. Reusable sweep for every desktop app that registers a custom protocol scheme: does the callback handler bind the session to a pre-negotiated nonce/state? Can a browser-triggered app:// URL mint an authenticated session? And what auto-uploads once a session exists? Forced-login plus implicit-sync is the quiet ATO-to-exfil chain.

Proof boundaries: Legcord/Chaterm/GitAhead checks run on your own installs and disposable accounts; config-injection proofs use your own proxy listener and a self-signed CA you control; never intercept another user's client traffic, and never demonstrate the theme-IPC leg against real user directories (marker files in a disposable themes dir only).

A six-advisory LibreOffice pack publishes one durable weaponization lesson: a "passive" office document is a fetch/read/write/code-load instruction set, and vendor hardening patches individual URL-supply sites, not the supply-site set. All six legs fire on document open, before any macro prompt.

  • Remote Java class path load = code execution on open (GHSA-5jx8-5g92-5w59 / CVE-2026-63277, high): a Calc cell-range link to an external data source can name a Java database driver loaded from a remote location — opening the document runs Java code from there. Fix shape: fixed versions require a Java classpath entry to be a file URL. This is the highest-segment leg: the data-provider connector config is a class-loading vector.
  • Fixed-CVE check missed sibling URL suppliers (GHSA-p537-j8hg-x628 / CVE-2026-63270): URLs in documents expanded environment-variable and INI-file values, exfiltrating them to a remote server on open — because the check added for CVE-2024-12426 "did not cover every place a document can supply a URL": XForms instance data and the Calc csv/sql data providers still reached the expansion. Durable rule for any document-format audit: find the CVE that previously patched this sink, then enumerate every place the format can supply the tainted value — patch coverage is per-supplier, and the suppliers are scattered across the format spec (hyperlinks, forms, data links, media refs, styles).
  • Local file read + outbound request on load via external data links (GHSA-2r33-w77m-6p5h / CVE-2026-63267): a saved csv external-data link was fetched while the document loaded — local file contents into the sheet, or a request to an attacker-chosen host, with no link-update control. Fix = fold csv links under the same link-update control as other spreadsheet links.
  • sql provider naming a folder of local text files as a "database" (GHSA-rvj5-mfwr-769g / CVE-2026-63268): reading a local text file into the sheet on open. Fix = only csv/html/xml providers are restored on load — the fix de-authorizes provider types, telling you the provider registry (csv|html|xml|sql|…) is itself the attack-surface enumeration to replay on any older build.
  • Embedded Firebird DB writing a file to any user-writable location (GHSA-5436-5r9x-m4jv / CVE-2026-63266): embedded-database ops inside the document performed file writes outside any sandbox on open. Fix = Firebird confined to its private directory. Embedded engines (Firebird, HSQLDB, LibreOffice's own) inherit full process file privileges — same rule as the ConvertX Calibre-recipe leg on the Sept 20 media-pipeline page: the wrapped engine's capability list, not the document's nominal purpose, is the surface.
  • HLS playlist inside a linked media file = GStreamer-driven file read + remote fetch (GHSA-hx75-xjjr-jmv5 / CVE-2026-63269, Linux): a linked media file could be an HLS playlist whose entries GStreamer reads — local files and remote URLs listed in the playlist get fetched during load, contents potentially landing in the document. The media playlist parser is a second-order SSRF/file-read primitive, one layer below the app that linked the media. Generalizes to any player/pipeline consuming attacker-authored manifests (M3U8, DASH MPD, concat demuxer lists).

Operator takeaways: (1) audit axis — per-supplier fix differentials: for any document-format hardening advisory, diff "which suppliers now refuse internal schemes/provider types" and test the unpatched siblings; each fix names the boundary it moved (link-update control, provider allow-list, file-URL-only classpath, private-dir confinement) — those boundaries map the format's URL-supplier graph. (2) weaponization posture (authorized phishing sims / lab targets only): the family composes into zero-interaction-after-open collection — data-link file read into sheet cells, env/INI expansion exfil, HLS playlist enumeration, remote JDBC driver fetch — all before macro security UI. (3) detection/hunt counterpoint for scope notes: soffice/oosplash spawning GStreamer plugin processes or JDBC class downloads, and outbound fetches with spreadsheet-ish Referer-less GETs at document-open timing, are the telemetry a defender sees; keep proofs to your own lab docs with marker values and owned callbacks, never real target user workstations.

October 12 21:3xZ follow-up: EdgeEver Desktop — a complete renderer-XSS-to-privilege-escalation showcase: raw-HTML previews, userinfo-prefix allowlist defeat, and a sender-less IPC handler that spawns caller-supplied binaries (five GHSAs, ≤1.112.0)

The 21:31Z wave landed a five-advisory chain on one Electron notes/agent desktop app that reads as a textbook completion of this page's recurring "renderer XSS + bridge = RCE" pattern, with three distinct bypass layers an auditor can reuse as a checklist:

  • Stored XSS #1 — template preview (GHSA-f2xf-xr5r-qf3x / CVE-2026-109558, 8.7): TemplatesPane.tsx renders contentMarkdown through dangerouslySetInnerHTML with no sanitization — attacker-authored template HTML with event handlers executes on preview. Entry point matters: templates are writable via API or MCP token, i.e. an agent/tool integration is the content-injection leg.
  • Stored XSS #2 — drag previews (GHSA-489r-rjvf-hwg5 / CVE-2026-109557, 8.7): setMemoDragPreview injects notebook names and memo titles unsanitized — script executes when the victim drags the item. The trigger is an interaction nobody warns about (drag, not click), and the injection channel is again MCP create_notebook/rename_notebook/create_memo tools.
  • Navigation allowlist defeat via userinfo syntax (GHSA-p9qx-79pg-cv5g / CVE-2026-109559, 8.7): the main-window navigation guard uses string-prefix matching on the dev-server URL, and https://dev-server.attacker.example/... shaped as https://dev-server@attacker.example/ (attacker host in the userinfo position, legit string as prefix) passes the check while loading an attacker origin into the privileged window — which exposes the whole window.edgeeverDesktop bridge: session token, sidecar RPC, ACP channels. URL-string-prefix guards defeat themselves here because the prefix grammar of a URL isn't the grammar of the guard: parse first, compare URL.host, never startsWith.
  • IPC handler without sender check → spawn (GHSA-x8jx-mq2j-m3pv / CVE-2026-109556, 8.0): the desktop:acp-probe IPC handler lacks a sender check and spawns input.path supplied by the caller — any renderer-context script (either XSS above) calls probeAcpAdapter with a chosen executable and runs it as the desktop user. Renderer XSS → arbitrary local execution, completed in one IPC call.
  • Chain assembly: MCP-token template write (no desktop access needed) → victim previews template → renderer script → bridge token or direct acp-probe spawn → user-privilege code execution. Every step is a separate advisory; the chain is the report.

Durable rules (extending the Legcord/GitAhead/Chaterm and Obsidian legs on this page):

  1. When a desktop app ships an MCP/API write surface, that surface is a remote stored-XSS injector into every unsanitized render path in the app. Audit order: enumerate MCP tools/APIs that write user-visible strings (templates, titles, names, previews), then enumerate render components that consume those strings with dangerouslySetInnerHTML/innerHTML/drag-preview painting. The MCP-tool → stored-XSS pairing is the desktop-app version of the agent-content-crossing-into-trusted-render class, and it removes the "attacker needs to phish a file" precondition.
  2. Navigation/webview allowlist battery (replayable): prefix-of-legit-origin string, userinfo-form (https://legit@evil/), subdomain suffix (https://legit.evil/), backslash and encoded-separator forms, dev-server defaults that ship enabled. Test by proving origin of the loaded document (read location.origin from injected script in the privileged window in your lab), not by whether the guard throws.
  3. Every IPC handler is a pre-auth endpoint of the local root-of-trust if it has no sender/frame check. Grep fingerprint: ipcMain.handle( / on( registrations whose callback uses event-less arguments directly for spawn/file/exec sinks. Enumeration of handler names (they're strings — greppable, sometimes extractable from the asar) plus a per-handler sender-identity question is the whole audit skeleton for Electron desktop pentests; shell.openExternal/child_process.spawn/execFile sinks with caller-controlled path/URL fields are the ones that convert XSS to RCE in one hop.
  4. Proof boundary: your own disposable vault/workspace, inert marker payloads (alert-free require('os').userInfo() echo), lab-hosted attacker origin you own; never chain against third-party users' workspaces or MCP tokens.

October 12 21:3xZ follow-up #2: NetEase Cloud Music Windows client — fixed public AES key on API responses + zero cert validation + scheme-less ShellExecuteW bridge = network-position RCE (three GHSAs, 3.1.38/3.1.39)

The same wave shipped a three-advisory desktop chain reached from the network, not from XSS:

  • TLS validation absent (GHSA-mg5w-3rqq-96m8 / CVE-2026-109423, 7.4): the client validates no server certificate chain, no hostname, no Windows trust store — a rogue CA at network position decrypts HTTPS, captures the MUSIC_U session credential (full account takeover), and modifies server responses in transit.
  • Fixed, publicly documented AES-128-ECB key decrypts /eapi/ and /xeapi/ response bodies with no integrity credential (GHSA-h7w7-mfj4-5gxw / CVE-2026-109424, 5.9): even without the MITM position, anyone who can originate a response (see above, or any proxy/redirect) forges valid "server" payloads by encrypting under the known key with PKCS#7 padding — response forgery is a public-key exercise, not a crypto break.
  • os.navigateExternal IPC bridge passes caller URLs to ShellExecuteW with no scheme validation (GHSA-8rjw-mvfx-hpfv / CVE-2026-109422, 8.1): forged /pc/upgrade/get response content reaching the renderer (e.g. img onerror payload) invokes the bridge with a file:// URL → arbitrary local binary execution as the logged-in user.

Why this earns a fold next to EdgeEver: it is the client-side-response-trust completion of the same chain shape — the trusted-input source is the network (via broken transport + baked-in symmetric crypto), the renderer sink is a native bridge, and ShellExecuteW/openExternal scheme-validation absence is again the last missing guard.

Durable rules:

  1. Response-side "encryption" with a public key is obfuscation, and it converts a transport bug into a structured-payload forge. When reversing any desktop client that "decrypts" API responses, hunt the key first (strings/frida on the binary; client-embedded keys are frequently published in API-reverse-engineering repos — grep GitHub for the endpoint names + key material). If the key is symmetric and embedded, you can author valid server responses — build a local mock-server harness that emits your own ciphertext and drive the client's full response-handling paths offline. That harness is a fuzzing rig for every renderer-bound field the API schema exposes.
  2. ShellExecuteW/openExternal scheme-allowlist check is per-bridge, and its absence is the XSS/forge-to-RCE edge. Same grep target as EdgeEver's acp-probe: enumerate renderer→native bridges, ask per-bridge "does the path/URL field get scheme-validated before the exec/dispatch syscall?"
  3. Desktop-client recon at network position (red-team C2/pivot contexts, authorized labs only): popular regional clients with broken validation are standing MITM-to-ATO tooling on shared networks; fingerprinting = version string in UA + endpoint shapes, and the MUSIC_U-class session cookie rides authenticated API requests you now see in cleartext. Keep proofs to your own test client VM on your own network.

Tracked without publication (rest of the 09:30Z wave)

  • Apache Doris FE meta-service improper authentication (CVE-2026-31377 / GHSA-6cx7-4x8x-cqpx, high, fixed 4.0.8/4.1.4) — client-supplied node information used as authentication on internal FE metadata endpoints; folded on the Sept 21 auth-precedence page.
  • Rename wp-login.php SQLi via log (CVE-2026-93368) + Advanced Contact form 7 DB contributor shortcode read-all (6831) + Getwid eval() stored XSS (5924) — folded on the Sept 19 WP alternate-surface page.
  • Apache BuildStream tar source-plugin symlink write on Python <3.12 (CVE-2026-82331) — sibling of the May 6 extraction-page family (same symlink-escape shape; mitigation is the tarfile data-filter that already appears there); tracked, no new axis.
  • NT-ware uniFLOW Online logout-session retention under Service Offline Emergency Mode timing window (CVE-2026-92378) — print-management appliance session-lifecycle edge, sparse; tracked.
  • ASR Crane/Falcon kernel NULL-deref (CVE-2026-42801) and the wave's remaining memory-safety singles — kernel/driver class, tracked per precedent.

Evidence and reporting boundaries

  • Document-signature proofs use self-signed lab documents only; the finding is badge-vs-render divergence, not "I broke the signature."
  • SMB-coercion proofs run against your own responder on lab networks you control; hashes captured are yours to discard and never leave the engagement boundary.
  • Updater/downgrade proofs run in a lab app with your own keypair and your own update server; never MITM, spoof, or replay against a vendor's live update infrastructure.
  • Localhost-service findings stop at reachability + one inert patched/instrumented call; discovered execSync-style sinks are documented, not exercised destructively.
  • Cross-document isolation proofs use two synthetic documents you own; never read real third-party content as the marker.

October 5 follow-up: Fiddler Classic helper-launch integrity and cert-install TOCTOU (2 durable legs)

Two legs from the Telerik Fiddler Classic <6.0.20262.10021 pack extend this page's desktop-privilege playbook (the smuggling pair folded on the Sept 15 parser/header-route page):

  • Authenticode subject-fragment allowlist as execution gate (GHSA-mpj7-wp32-q53h / CVE-2026-77805, high): before launching an external helper tool, Fiddler verifies only that the file carries a valid Authenticode signature whose certificate subject name matches a broad allow list of publisher-name fragments — not that the file is the shipped executable. Any validly signed binary from an allow-listed publisher substituted at the helper path executes, including elevated for the tools that request elevation → LPE to Administrator. Durable rule for every Windows desktop app that spawns signed helpers: the integrity check's granularity is the finding — publisher-fragment match ≠ file identity. Audit sweep: locate helper-launch call sites, record what's verified (signature present? subject fragment? full thumbprint / publisher+digest? version+path?), then test substitution with an authentic signed Microsoft/OS binary copied over the helper path in a disposable install (benign substitution proof — e.g., helper replaced by notepad.exe, evidence is which process launches elevated, never a malicious binary).
  • Elevated cert-import helper consumes a user-writable temp file (TOCTOU) (GHSA-rx7m-qprj-p54h / CVE-2026-77804, medium): the HTTPS-interception root cert is written to a user-writable temp location, then the elevated TrustCert helper imports whatever that path holds at read time — swap the file in the window and an attacker-chosen root lands in the Local Computer Trusted Root store = persistent TLS interception for the whole machine. Generalizes to any "write material as low-priv, consume it elevated" handoff (updater staging dirs, print/spooler drops, scheduled-task inputs): audit rule = trace every file a privileged helper reads and ask who could have written it between creation and read; proof stays lab-bounded with your own CA cert into your own disposable VM's store, then certutil evidence.

October 7 follow-up: desktop scheme-handler record injection (Telegram Desktop) and "inspect the image" tools that run the image (kube-compare)

Two workstation-side legs from the Oct 7 15:31Z wave extend this page's desktop cluster:

  • tg:// link record-separator injection → local file upload → account takeover (GHSA-2h6w-ccjh-5jhr / CVE-2026-107181, high, Telegram Desktop < 7.2.9): Core::Sandbox parses the custom-scheme payload as semicolon-delimited records and the link builder never escapes ; — a crafted tg:// link injects an additional OPEN: record, chaining to the interpret: scheme handler that uploads local files (including tdata session keys) to an attacker channel. Durable rules: (1) custom-protocol handlers are a parser with an undocumented grammar — every delimiter/separator the internal format uses (;, |, newline) is an injection point if the entry-point builder doesn't escape it; fuzz separators in any app:///tg:///vscode://-style link your target registers, then diff the handler's dispatch log for extra records. (2) Any desktop handler with a "share/upload/export file" capability is an arbitrary-local-file-read-to-attacker exfil primitive once reachable from link content — the session-key theft here is just this product's instance. Phishing a single link is the trigger leg; validate in your own disposable profile.
  • container:// reference executes the image's entrypoint instead of reading its filesystem (GHSA-c73r-9gcf-qjwr / CVE-2026-87114, high, kube-compare): a diff/compare tool "extracting data from a container reference" ran the untrusted image's entrypoint → code execution on the operator's workstation, root-mediated when the Docker daemon is elevated. Generalization: any devtool that accepts a reference to an artifact (image, chart, package, repo) and claims to inspect it may invoke that artifact's lifecycle hooks — Docker create+start vs export/cp, npm prepare scripts, setup.py at sdist-build time. Audit question for every inspector-style tool on a pentest workstation: does the read path start a process? Proof: build a canary image whose entrypoint writes a marker, point the tool at it under a disposable Docker context.

October 8 18:3xZ follow-up: Obsidian Desktop — two sanitizer-bypass chains that end at require('child_process') (fixed 1.14.0)

Two legs from the Oct 8 18:32Z wave extend this page's desktop-renderer cluster, and they're a lesson for every Electron/Tauri app you test that bundles a third-party display framework:

  • data-background-iframe attribute survives DOMPurify and is consumed by bundled Reveal.js 4.3.1 inside the Slides plugin → javascript: URL executes in the background iframe → parent.require() reaches Node (GHSA-fj5r-pww3-qc5p / CVE-2026-104077, high). Precise chain: Obsidian <1.14.0 Markdown notes with insufficiently sanitized data-background-iframe are processed by the bundled Reveal.js; the attribute accepts a javascript: URL; the vault renderer runs with Node integration enabled and context isolation disabled, so injected script calls parent.require('fs'/'child_process') → OS command execution as the desktop user (trigger: victim opens the note and starts the presentation).
  • MathJax 3.2.2 safeProtocols filter bypass via TAB byte in \href URL scheme (GHSA-4pcg-gm3h-3m4r / CVE-2026-104078, high): a javascript:<TAB>...-shaped href makes filterURL compute an empty protocol, which passes the configured restriction → javascript: anchor in Live Preview → same require('child_process') sink on click.

Durable rules:

  1. Sanitizer allow-lists are keyed on the sanitizer's own parser; every consumer downstream re-parses with its own grammar. The MathJax leg is the trim-vs-empty-protocol differential (browsers strip control characters from URL schemes after the safety check ran) — same "validation-to-connect drift" family as the Coraza CTL-trim and scheme-normalization legs. Any attribute the sanitizer permits by name (framework data-attributes!) is attacker-controlled input to the framework's loader: data-* attributes consumed as URLs/handlers are the blind spot of generic HTML sanitizers.
  2. Electron renderer trust model = the second half of the chain. Node integration + no context isolation converts any renderer-context script into host RCE; when auditing desktop apps, record nodeIntegration/contextIsolation per renderer as recon metadata — the XSS-class leg and the RCE-class finding are separated by exactly those two flags.
  3. Bundled-framework inventory is desktop recon: grep the app bundle for reveal, mathjax, mermaid, katex, markdown-it versions — this pair proves the bundled display framework, not the app's own code, carried both bugs. Same rule as the Handlebars </script> and vendor-ship FilePond legs folded the same day.
  4. Canonical advisory links: GHSA-fj5r-pww3-qc5p / CVE-2026-104077 (Reveal.js chain) and GHSA-4pcg-gm3h-3m4r / CVE-2026-104078 (MathJax TAB bypass). Proof boundary: own disposable vault with inert marker (console.log/calc-free require('os').userInfo() in a lab VM), never shipped-note phishing against real users.

October 12 follow-up: Metasploit loot-path traversal — target-controlled filenames reach the operator's auto-executed startup file (GHSA-663p-9fgr-v7m7 / CVE-2026-87690, high 8.8, fixed 6.5.5)

The 06:31Z Oct 12 wave's most operator-relevant leg is not a target-side bug — it is in the attack toolchain itself. The post module post/apple_ios/gather/ios_image_gather interpolated file names taken from the Meterpreter directory listing (data fully controlled by a hostile or tampered iOS device) directly into the local destination path passed to client.fs.file.download_file(). The stdapi sink writes to whatever path it is given, so a listing entry like ../../msfconsole.rc escapes the random ~/.msf4/loot/<random> directory and overwrites ~/.msf4/msfconsole.rc — a resource file every msfconsole invocation auto-executes at startup (lib/msf/ui/console/driver.rb:196). The written rc file can load a second traversally-written Ruby plugin whose top-level code runs on require → persistent code execution as the operator (frequently root) on the pentest workstation at next console launch. Fix (PR #21880): scrub separators/File.basename on listing entries before building local paths — the same scrub the recursive downloader (stdapi/fs/dir.rb) already had, omitted here.

Durable rules:

  1. The loot directory is a trust boundary, and gather modules are the leak in it. Every operator-side routine that turns target-controlled names (directory listings, registry keys, env vars, process paths, file metadata) into local filesystem paths is a traversal sink where the target writes to the operator's disk. Audit pattern for any pentest/C2/console tool: grep download/save/write helpers for call sites whose path components come from remote listing data, and diff scrubbing coverage per call site — this leg proves the scrub existed in the recursive sibling and was omitted from one gather module (per-call-site symbol sweep, not per-feature).
  2. Enumerate the operator home's auto-exec surfaces before judging severity. The traversal alone is arbitrary file write; the RCE is msfconsole.rc auto-exec plus rc-loadable Ruby plugin. Same audit on any tool you run from a workstation: rc/init/startup files (~/.msf4/, ~/.iterm2, ~/.config/fish/config.fish, daemon unit paths), then ask which remote-controlled strings can name a path that reaches them. From a target-side view this is a workstation implant primitive with no second bug — a tampered device that lures an operator into running one gather module gets persistent code exec on the operator host; treat it as an APT-grade engagement-risk finding when red-teaming against teams that use interactive consoles on shared/jump boxes.
  3. Fix-pattern fingerprint: the upstream migration to store_loot (basename-derived paths) is the canonical shape; other toolkits' equivalents are filename normalization at the sink rather than the caller. When auditing a fork or an old Metasploit install (<6.5.5), the vulnerable call signature (file.download_file(<remote-name-interpolated path>)) is greppable across every module in seconds.
  4. Proof boundary: lab-only. Reproduce against a disposable Meterpreter session you host yourself with a synthetic listing entry pointing at a temp marker file outside the loot dir; never write real rc/startup files, and never run third-party gather modules against systems you don't own.

October 12 follow-up: AnyDesk Linux "AnyPwn" — pre-approval root RCE via 32-bit length wrap in the mode-5 stream handler (CVE-2026-109390 / GHSA-p2jv-fpmf-jqjw, critical, fixed 8.0.3, public PoC at v12-security/pocs)

The 15:32Z Oct 12 wave landed a critical with a published working exploit: AnyDesk Linux ≤8.0.2 trusts a varint-declared payload length in the session protocol's mode-5 stream packet handler before allocation — uint32(declared_len + 0x10) wraps to 0–15 bytes for declared lengths 0xfffffff0..0xffffffff, while the object keeps its original huge logical length, so a few attacker bytes overflow the allocation. Linux service mode runs as root, and the whole path executes before the victim approves desktop control — the "accept prompt" the product markets as the security boundary sits behind the vulnerable parser. Reachable over direct TCP/7070 and validated on relay connections (Frida trigger in the writeup).

Durable rules for remote-desktop/support-tool surfaces (extends this page's desktop cluster):

  1. The consent UI is not the authentication boundary — everything before it is still attack surface. For every remote-access tool (AnyDesk, TeamViewer, RustDesk, VNC variants, vendor support agents): enumerate which protocol handlers run pre-accept (transport handshake, stream/object parsers, profile exchange), because that's where root-RCE bugs land while the vendor threat model assumes "attacker needs the user to click accept." Scan posture: AnyDesk's service listens on TCP/7070 locally per instance — an inventory of localhost listeners (this page's Acer/AutoExec lesson) catches support tools that employees install; on perimeter scopes, direct-connectable AnyDesk instances ≤8.0.2 are pre-auth root targets.
  2. Length-field arithmetic wrap fingerprint: allocation computed as declared_length + header_size in 32-bit arithmetic with the declared length attacker-controlled — declared values clustered just below 2^32 are the probe shape. For protocol fuzzing, the varint-encoded near-2^32 battery is cheap and catches the whole bug class without large payloads ("the outer frame remains small, its varint causes the wrap").
  3. Build-availability as recon signal: the vendor pulled the 8.0.2 Linux build when the PoC dropped — old-version artifact hoarding (package archives, docker layers, apt pool caches, archive.org) is a live research surface, and target-side fingerprinting should hash the binary (PoC README publishes the 8.0.2 SHA-256) rather than trusting --version strings, since patched and vulnerable builds report similarly.
  4. PoC methodology worth reading (not republishing): size-graded spray across allocator size classes to reduce layout dependence, multi-connection victim grooming, and a repeated ret-sled ROP object to make the stack pivot land anywhere in a large region — the "many equivalent landing points instead of one exact address" pattern is how probabilistic heap exploits get reliable enough to demo. Engagement use stays lab-bounded against your own VM; the exploit offsets are build-exact and the crash-mode failure is loud.