Fuzzing and Systematically Testing Electron IPC and Preload Surfaces

Part 6: inventory-first IPC/preload testing, dangerous sink wordlists, and a constrained PocForge Electron lab fuzz harness for authorised training.

Series Electron application penetration testing (draft series) · Deep dive hub · Desktop VAPT service
← Part 5 · ChecklistResearch library →
Part 6 focus Systematically testing IPC and preload surfaces: inventory, sink classification, dangerous-name wordlists, and a constrained lab fuzz harness—without blind-fuzzing production mains.

Parts 2 and 3 showed how a single misconfiguration or over-exposed bridge method becomes desktop impact. Real applications expose dozens of channels. This part turns that insight into a repeatable methodology: map the surface, classify sinks, drive constrained probes from wordlists, and keep proofs inside lab clamps. The companion harness lives under electron-lab/fuzz/.

Five-step diagram for IPC and preload surface testing with dangerous sink wordlist
Methodology strip: inventory → classify → wordlists → constrain → evidence. Highlighted chips are high-priority sink names for greps and bridge enums.
IPC fuzzing explorerPOCFORGE / INTERACTIVE

Keyboard: ← → · Respects reduced-motion (no autoplay).

1. Why systematic testing beats ad-hoc clicks

Manual UI buttons catch the obvious. Systematic testing catches the tenth invoke alias, the forgotten debug channel, and the handler that accepts an options object with an unexpected shell: true field. Community methodologies (DeepStrike, HackTricks, Electronegativity checks) all converge on the same idea: treat the bridge as an API catalogue, then test it like one.

Blind fuzzing of a customer’s production main process is usually the wrong tool—timeouts, dialogs and disk writes disrupt users. Prefer inventory-first probes on lab builds, unpackaged debug builds the customer supplies, or disposable VMs under written RoE.

2. Inventory recipe (preload + ipcMain)

  1. Extract ASAR or open the lab tree; record package.json main.
  2. Grep contextBridge.exposeInMainWorld, ipcRenderer.invoke, ipcRenderer.send, ipcMain.handle, ipcMain.on.
  3. Build a spreadsheet: bridge method → channel → handler file:line → args → side effect → sender check?
  4. Mark anything that reaches child_process, fs, shell, net, protocol, session, or executeJavaScript as privileged.
# Teaching greps against an extracted app or the series lab
rg -n "exposeInMainWorld|ipcMain\.(handle|on)|ipcRenderer\.(invoke|send)" .
rg -n "execFile|exec\(|spawn\(|openExternal|writeFile|readFile|loadURL" .

3. Dangerous sink wordlists

The lab ships electron-lab/fuzz/dangerous-sinks.txt—seeds for greps and for guessing over-exposed bridge keys when source is messy. High-signal names include exec, runCommand, writeFile, openExternal, invoke, executeJavaScript, loadURL, setProxy and protocol. Pair with path-probes.txt for path-normalisation and scheme-allowlist probes aimed at path-like handlers.

# See electron-lab/fuzz/dangerous-sinks.txt in the lab folder
# Seeds cover process, filesystem, navigation and protocol-related API names.
# Keep the list in the lab tree — do not paste raw OS command names into tickets.

Do not treat the wordlist as an exploit kit. It is a coverage checklist: if a bridge method’s name matches, read the handler before sending anything.

4. Safe lab fuzz harness notes

Under electron-lab/fuzz/:

  • run-lab-fuzz.js --dry-run — prints planned cases (allow vs deny commands, path probes, marker names) without touching Electron.
  • --apply-markers — writes sanitised markers under the OS temp lab directory only.
  • README documents principles: inventory first, classify sinks, constrain blast radius, never expand the command allowlist for “better demos.”
cd electron-lab
node fuzz/run-lab-fuzz.js --dry-run
node fuzz/run-lab-fuzz.js --apply-markers
npm run start:bridge   # then probe labApi with planned cases in DevTools
Lab demo screenshot showing bridgeAbuse IPC exploit path with whoami and writePath results
Lab demo: bridgeAbuse mode — find over-exposed labApi keys, exploit via runCommand(whoami) and writePath, confirm allowlist denies ls, prove raw invoke still reaches ipcMain.

5. Find → exploit walkthrough (lab)

Strip of three lab screenshots: secure, nodeIntegration and bridgeAbuse exploit proofs
Lab demo: same UI across modes — secure fails closed; nodeIntegration exposes require/fs; bridgeAbuse yields IPC command and path effects.
  1. Find prefs — open main.js, note ELECTRON_LAB_MODE branches. Run npm run start:node and confirm the mode banner.
  2. Exploit nodeIntegration — in DevTools, evaluate typeof require and write a marker with require('fs') under the lab temp directory.
  3. Find bridge surface — npm run start:bridge, run Object.keys(window.labApi).
  4. Exploit IPC — await labApi.runCommand('whoami'), then a denied command, then writePath, then raw invoke.
  5. Contrast — npm run start:secure and show dangerous keys absent.
// LAB ONLY — DevTools in bridgeAbuse mode
Object.keys(window.labApi)
await window.labApi.runCommand('whoami')
await window.labApi.runCommand('ls') // expect deny
await window.labApi.writePath('fuzz/ipc-demo.txt', 'pocforge-lab-write\n')
await window.labApi.invoke('lab:write-marker', 'fuzz-invoke')
Lab demo screenshot proving nodeIntegration with typeof require function and hasFs true
Lab demo: nodeIntegration misconfiguration — renderer typeof require is function, hasFs true, marker written under OS temp; prefs show isolation off.
Lab demo screenshot of secure mode where runCommand and writePath are not exposed
Lab demo: secure baseline — labApi exposes only getMode and writeMarker; runCommandExposed and writePathExposed are false.

6. Payload classes that earn their keep

Class Examples (lab) What you learn
Allowlisted command whoami, id Handler reaches process-spawn path
Denied command ls, command-substitution probes Allowlist fail-closed behaviour
Relative path nested/a.txt Intended write root
Traversal / absolute parent-directory segments, absolute paths outside the app root Normalisation gaps vs safety clamps
Raw channel invoke('lab:…') Passthrough anti-pattern
Type confusion arrays / numbers where strings expected Schema absence

On customer apps, replace lab channels with inventored names. Keep the same classes. Prefer markers and allowlisted binaries over GUI calculators in enterprise evidence packs.

7. Reporting systematic IPC work

One finding per privileged capability is clearer than a hundred payload rows. Example title: “Preload exposes runCommand IPC leading to allowlisted process execution.” Attach the inventory row, a representative success, a representative deny, and the secure-mode contrast when available. Link prefs from Part 2 when isolation is also wrong—severity compounds.

Electronegativity and similar static checkers complement this work; they do not replace reading handlers. Use them to seed the spreadsheet, then prove impact manually under RoE.

8. Limits and ethics

  • Do not point the lab harness at production estates.
  • Do not remove temp clamps or widen command allowlists to “prove RCE harder.”
  • Do not invent CVE IDs when a custom bridge bug has none.
  • Stop when RoE excludes update hijack, integrity bypass or host-wide DoS—document residual risk instead.

9. Channel guessing versus source-backed inventory

When ASAR is available, prefer source-backed inventories. When it is not—or when minification obscures names—testers sometimes probe guessed channel strings. Keep that activity on lab or explicitly authorised builds: random invoke storms against a live corporate desktop are a denial-of-service risk and a professionalism problem.

A practical compromise: extract strings from the binary/ASAR (strings, rg, unpacked renderer bundles), build a candidate channel list, then exercise only those candidates. The lab’s raw labApi.invoke passthrough exists specifically so analysts can practise that discipline safely—call known lab: channels, observe successes, then try a nonsense channel and record the rejection.

// LAB ONLY — channel discipline
await window.labApi.invoke('lab:get-mode')
await window.labApi.invoke('lab:does-not-exist') // expect error; note message

10. Light automation patterns (lab)

Once cases are classified, a short script can iterate payloads and write a JSON report under the temp lab directory—the harness’s --apply-markers mode is the minimal version. For richer runs inside bridgeAbuse, keep a DevTools snippet or a Spectron/Playwright-driven lab launcher that:

  1. Starts Electron with ELECTRON_LAB_MODE=bridgeAbuse.
  2. Waits for labApi.
  3. Iterates the planned case list with per-call timeouts.
  4. Writes fuzz-report.json next to markers.
  5. Exits without opening external URLs beyond the lab allowlist.

Resist the urge to turn that runner into a general “Electron RCE fuzzer” published against the internet. Scope it to electron-lab/ paths and documented handlers. Customer work gets a fresh inventory, not a recycled payload file aimed at unknown channels.

11. Severity hints for IPC findings

Pattern Typical severity band Notes
Raw invoke/send exposure High–Critical Depends on reachable handlers
Command string to exec Critical Even with partial allowlists
Path write with weak normalisation High Show clamp vs intended root
openExternal without scheme allowlist High Platform-dependent impact
Read-only info leak via IPC Medium Tokens/paths elevate it
Missing sender checks only Medium Higher if multi-frame content

Always narrate the path. “IPC exists” is not a finding; “renderer-controlled string reaches execFile” is.

12. Part 6 mini-checklist

  • □ Bridge + ipcMain inventory spreadsheet complete
  • □ Privileged sinks classified
  • □ Wordlist greps run on ASAR/lab
  • □ Dry-run case list reviewed
  • □ Lab proofs: success, deny, secure contrast
  • □ Customer probes limited to inventored channels under RoE
  • □ Report uses safe markers, not destructive demos

Together with Part 5’s engagement spine, this checklist keeps systematic IPC testing auditable—another analyst should be able to replay your cases from the spreadsheet and the lab notes alone.

13. After Part 6

Return to Part 5’s deliverables map and attach your IPC spreadsheet as an appendix. Link the deep-dive hub for the condensed privilege-boundary framing, and the desktop service page when scoping paid work. Keep the lab folder versioned beside engagement notes so demos stay reproducible when Electron majors move defaults again.

Systematic IPC testing is not glamorous—but it is how “we disabled nodeIntegration” stops being the end of the conversation and becomes the start of a proper bridge review.

FAQ

Is this the same as web API fuzzing?

Similar discipline, different blast radius. Desktop handlers can touch the OS user profile. Constrain first; broaden only with authorisation.

Should every channel be fuzzed?

Prioritise privileged sinks. Read-only telemetry handlers can wait. Time-box coverage in the RoE.

Where does this sit in the series?

After Part 3’s bridge abuse narrative and Part 5’s checklist. Use Part 6 when you need repeatable IPC coverage, not only two demo clicks.

Sources (public)

  • Electron — Security tutorial (IPC sender validation; do not expose Electron APIs)
  • DeepStrike — Electron penetration testing (IPC / preload sections)
  • HackTricks — Electron Desktop Apps
  • Doyensec — Electronegativity / Electron security research
  • PocForge — Parts 1–5 of this series · Deep dive · Desktop VAPT

Lab demos and the fuzz harness are intentionally vulnerable training aids for authorised use only. PocForge does not claim CERT-In empanelment in this article. No fabricated CVE identifiers or statistics.

Series Electron application penetration testing (draft series) · Deep dive hub · Desktop VAPT service
← Part 5 · ChecklistResearch library →