# TidGi Desktop Remote Code Execution via Malicious TiddlyWiki Repository Import — Tiddler Startup Module Auto-Execution

TidGi Desktop auto-loads .tid files from imported TiddlyWiki repos; a tiddler with module-type:startup executes arbitrary JS with Node require() access, yielding RCE.

- Payout: Research
- Severity: critical
- Program: TidGi Desktop
- Found by: nuiifornet
- Disclosed: 2026-07-14
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-9hc2-hjx8-q6pv
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-9hc2-hjx8-q6pv
- Tags: area:os domain:desktop impact:integrity impact:rce kind:research platform:ghsa sev:critical tech:electron tech:nodejs technique:source-review vuln:rce

**Target:** TiddlyWiki wiki workspace boot in TidGi Desktop (Electron wiki worker)
**Root cause:** TidGi loads all .tid files from an imported wiki's tiddlers/ directory into the store before boot; TiddlyWiki's defineTiddlerModules() registers any tiddler with module-type and type application/javascript as an executable module, and startup modules without a platforms field pass doesTaskMatchPlatform() and run with full Node.js require() access.
**Steps:**
1. Create a repo with `tiddlers/$__plugins__poc__startup.js.tid` containing `title: $:/plugins/poc/startup.js`, `type: application/javascript`, `module-type: startup`, and `exports.startup = function() { require('child_process').execSync('touch /tmp/TidGi-RCE-PoC.txt'); }`
2. Victim uses Add Workspace → Clone Git Repository / Open Local Folder and confirms
3. TidGi boots the wiki: loadWikiTiddlers → wiki.addTiddlers() loads the .tid, then boot.startup() → execStartup() → defineTiddlerModules() registers the module
4. forEachModuleOfType('startup') collects it, doesTaskMatchPlatform() returns true (no platforms field), executeNextStartupTask() calls exports.startup()
5. Verify: `node -e "const $tw = require('tiddlywiki/boot/boot.js').TiddlyWiki(); $tw.boot.argv = ['/tmp/evil-wiki']; $tw.boot.startup();"` prints STARTUP_EXECUTED and creates /tmp/TidGi-RCE-PoC.txt
**How found:** Code review of TidGi's wiki worker tiddler loading combined with reading TiddlyWiki boot.js: noticed loadWikiTiddlers adds all .tid files to the store before execStartup, then traced defineTiddlerModules() registering module-type tiddlers and doesTaskMatchPlatform() returning true when platforms is unset, confirming startup auto-execution.
**Exploitation:** Attacker publishes a Git repo (or folder) that looks like a normal TiddlyWiki with a malicious .tid in tiddlers/. Victim imports it via Add Workspace; on boot the startup module executes in the Wiki Worker Node.js process, giving arbitrary command execution, file read/write, reverse shell and persistence (LaunchAgents, crontab, startup scripts). One click of user interaction, cross-platform.
**Escalation:** From code execution: arbitrary file read/write, reverse shell via net module, and persistence by writing startup scripts, LaunchAgents or crontab entries.
**Insight:** When an app embeds a wiki/plugin engine, check whether content files loaded from user-supplied directories can declare engine-level module metadata (module-type, type) that the boot sequence auto-registers and executes; the platform/allowlist guard often defaults to permissive when the field is absent.

## Proof of concept (copied exactly from the original report)

**Malicious .tid startup module payload**

```
title: $:/plugins/poc/startup.js
type: application/javascript
module-type: startup

exports.startup = function() {
  require('child_process').execSync('touch /tmp/TidGi-RCE-PoC.txt');
  console.log('STARTUP_EXECUTED');
};
```

**Node boot command executing malicious wiki**

```
$ node -e "
  const \$tw = require('tiddlywiki/boot/boot.js').TiddlyWiki();
  \$tw.boot.argv = ['/tmp/evil-wiki'];
  \$tw.boot.startup();
"
STARTUP_EXECUTED
```

