# SiYuan: Second-order SSTI to arbitrary SQL via attribute-view template column (queryBlocks): malicious imported package executes SQL on victim kernel

SiYuan attribute-view template column's queryBlocks function substitutes SQL strings instead of binding, so an imported malicious AV package executes arbitrary SQL on the victim's kernel.

- Payout: Research
- Severity: high
- Program: SiYuan
- Found by: Shirshakhtml
- Disclosed: 2026-09-03
- CVE: CVE-2026-72807
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-x67c-8pwr-m8g3
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-x67c-8pwr-m8g3
- Tags: area:os domain:desktop domain:web impact:data-leak impact:integrity kind:research platform:ghsa sev:high tech:go technique:source-review vuln:sqli vuln:ssti

**Target:** Attribute-view template column / queryBlocks template function in SiYuan kernel
**Root cause:** queryBlocks passes its template argument to SelectBlocksRawStmt using `?`→argument string substitution rather than parameter binding, on the read-write SQLite handle (statement-stacking capable). AV template columns are live-evaluated at render, so attacker-controlled template content becomes arbitrary SQL.
**Steps:**
1. Create host doc and AV as admin: `POST /api/filetree/createDocWithMd {notebook, path:"/ssti-poc", markdown:"host"}` then `POST /api/av/renderAttributeView {id:"<AV>"}` to materialize the AV.
2. Plant malicious template column via one performTransactions call: `addAttrViewCol` (type template), `updateAttrViewColTemplate` with data `.action{range queryBlocks "SELECT * FROM blocks WHERE root_id='<PROTECTED_DOC_ID>'"}.action{.Markdown} .action{end}`, and `insertAttrViewBlock`.
3. Trigger render: `POST /api/av/renderAttributeView {id:"<AV>"}` — the template cell renders the protected document's content, proving SQL execution.
4. Deliver second-order: share/import the crafted document or AV package; the embedded SQL runs on any kernel that imports and renders it.
**How found:** Noted doc-level `{{…}}` templates are rendered at insert-time and become static, so not re-evaluated on reader view; the residual live-evaluated surface is AV template columns. Checked the template function set: `env`, `expandenv`, `getHostByName` were removed, leaving `queryBlocks` as the remaining live sink, then traced it to SelectBlocksRawStmt's `?`→string substitution.
**Exploitation:** Attacker distributes a SiYuan document or AV package whose template column contains `.action{queryBlocks "<arbitrary SQL>"}`. When a victim imports the package and renders the AV, the template is live-evaluated and the SQL executes on the victim's read-write kernel DB handle, enabling cross-notebook reads (bypassing publish/password boundary) and, via statement stacking, writes.
**Escalation:** Statement stacking on the read-write handle upgrades read-only SQL injection to write; cross-notebook read bypasses the publish/password boundary.
**Insight:** When a template engine is hardened by deleting dangerous builtins, enumerate the remaining functions and trace each to its sink — a 'safe' data-query helper that string-substitutes into SQL is still an injection primitive. Also check whether template evaluation is insert-time (static) vs render-time (live); only the latter is second-order exploitable via imported content.

