Skip to content

Stored XSS / RCE via `setBlockAttrs` icon attribute (bypass of dynamic icon XSS fix #15970)

High
88250 published GHSA-7c6g-g2hx-23vv Jan 18, 2026

Package

No package listed

Affected versions

<=v3.5.3

Patched versions

v3.5.4

Description

Summary

A stored Cross-Site Scripting (XSS) vulnerability in SiYuan allows an attacker to inject arbitrary HTML attributes into the icon attribute of a block via the /api/attr/setBlockAttrs API. The payload is later rendered in the dynamic icon feature in an unsanitized context, leading to stored XSS and, in the desktop environment, potential remote code execution (RCE). This issue bypasses the previous fix for issue #15970 (XSS → RCE via dynamic icons).

Details

The /api/attr/setBlockAttrs endpoint accepts attacker-controlled data in the attrs.icon field:

  • Endpoint: POST /api/attr/setBlockAttrs
  • Parameter: attrs.icon

The value of icon is stored with the block and later used by the dynamic icon feature. When the icon is rendered, the string is inserted into HTML in an attribute context without proper escaping. By injecting a quote and a new attribute (e.g. onload), an attacker can execute arbitrary JavaScript whenever the affected block is rendered.

This reaches the same sink as the original dynamic icon issue reported in #15970 (XSS → RCE). The previous fix appears to constrain the UI path, but the same sink is still reachable via the backend API (setBlockAttrs), making this a bypass/regression of that fix.

Tested on: v3.4.2 .
Earlier versions are likely affected as long as they expose /api/attr/setBlockAttrs and the dynamic icon feature in the same way.

PoC

POST /api/attr/setBlockAttrs HTTP/1.1
Host: localhost:6806
Content-Type: application/json

{
  "id": "<BLOCK_ID>",
  "attrs": {
    "icon": "api/icon/getDynamicIcon\" onload=\"alert(document.domain)"
  }
}
  1. Replace <BLOCK_ID> with the ID of a block you can modify.
  2. Send the request to the running SiYuan instance.
  3. Open the UI and display the block with the modified icon.

When the dynamic icon is rendered, the injected onload attribute is executed and alert(document.domain) pops, demonstrating stored XSS.

The payload can be replaced with JavaScript that uses Node/Electron APIs (similar to #15970) to achieve RCE in the desktop application -> api/icon/getDynamicIcon\" onload=\"require('child_process').exec('gnome-calculator', null)

POC

Sxss-CVE-siyuan.mp4

Impact

  • Stored XSS: Any user who views the affected block triggers attacker-controlled JavaScript.
  • Desktop RCE: In the desktop app, the XSS can be escalated to arbitrary command execution via available Node/Electron/Electron-bridge APIs (same class of impact as #15970).
  • Attack prerequisites: Attacker must be able to reach the SiYuan API and modify a block’s attributes (directly via API, or indirectly via a feature that uses setBlockAttrs).

Given the potential for XSS → RCE in the desktop environment, this should be treated as a bypass/regression of #15970 and assigned its own CVE.

Reporter: 0xNayel

Solution

0be7e1d

Severity

High

CVE ID

CVE-2026-23852

Weaknesses

Improper Control of Generation of Code ('Code Injection')

The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment. Learn more on MITRE.

Credits