Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
obsidian-note-toolbar-PoC — PoC — Obsidian용 Note Toolbar의 frontmatter 기반 임의 JavaScript 실행 (GHSA-q8cw-3m8c-5pf2, CVE-2026-87002, CVSS 7.0). | Kitploit
도구/GitHubGitHub/squeeze440/obsidian-note-toolbar-poc
Static AnalysisVulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPapers & ResearchLearning & EducationPayload Development

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
GitHub
squeeze440/obsidian-note-toolbar-poc

obsidian-note-toolbar-PoC

PoC — Obsidian용 Note Toolbar의 frontmatter 기반 임의 JavaScript 실행 (GHSA-q8cw-3m8c-5pf2, CVE-2026-87002, CVSS 7.0).

저장소 보기
7일 전아직 검토되지 않음
공유

{{prop_NAME}} → {{js:}} 변수 체인을 통한 Note Toolbar의 Frontmatter 기반 임의 코드 실행

CVE 상태: 요청됨, 할당 대기 중. 이 발견은 GHSA-q8cw-3m8c-5pf2로 공개되었습니다. CVE가 할당되면 이 저장소는 CVE-YYYY-NNNNN-obsidian-note-toolbar-PoC로 이름이 변경되고 이 배너는 CVE 링크로 대체됩니다.

연구자Dostxodjayev Abdullox (@squeeze440)
권고GHSA-q8cw-3m8c-5pf2
CVSS 3.17.0 (High)
취약점CWE-94, CWE-1336

{{prop_NAME}} → {{js:}} 변수 체인을 통한 Note Toolbar의 Frontmatter 기반 임의 코드 실행

요약

Note Toolbar(Obsidian 플러그인)의 변수 치환 엔진에서 출력 중화가 부적절하여, 노트의 YAML frontmatter를 제어할 수 있는 공격자(예: 동기화/공유 vault의 협업자)가 기존의 비스크립트 툴바 항목 레이블/툴팁/링크가 {{prop_NAME}}을 통해 참조하는 frontmatter 속성에 {{js: ...}} 페이로드를 주입함으로써, 피해자가 단순히 노트를 열기만 해도 피해자 머신에서 임의 코드 실행을 달성할 수 있습니다.

제품

Note Toolbar (note-toolbar) — Obsidian 커뮤니티 플러그인 저장소: https://github.com/chrisgurney/obsidian-note-toolbar

테스트 버전

v1.34.12 (커밋 520271c3027eb6da38bab10a686b21e14c664c13, 2026-07-29), Linux의 Obsidian 데스크톱 1.13.4 대상.

추정 CVSS v3.1

7.0 (High) — CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H

  • AV:L — 취약한 구성 요소(Obsidian의 Electron 렌더러 내부에서 실행되는 플러그인의 JS 평가기)는 네트워크에 노출되지 않으며, 페이로드는 피해자가 사용하는 수단(Git, Syncthing, Obsidian Sync, 공유 폴더)으로 동기화된 로컬 vault/파일 콘텐츠로 도착합니다. 이는 이 계열의 이전 발견들(예: obsidian-syncthing-integration, obsidian-codescript-toolkit)과 일치합니다.
  • AC:H — 익스플로잇은 피해자 설치 환경에 이미 존재해야 하는 공격자 통제 밖의 조건에 의존합니다: (1) 플러그인의 "Scripting" 설정이 이미 활성화되어 있어야 하며(기본값은 꺼짐), (2) 피해자가 이미 레이블, 툴팁 또는 링크가 특정 frontmatter 키에 대한 순수한 {{prop_NAME}} 참조인 툴바 항목을 구성해 두어야 합니다. 둘 다 현실적이고 문서화된 사용 패턴이지만, 어느 것도 기본값은 아닙니다.
  • PR:N — 공격자는 피해자 시스템에 대한 접근이 필요하지 않으며, 공격자가 제어하는 frontmatter가 있는 노트를 피해자의 vault에 넣을 수 있는 능력만 있으면 됩니다.
  • UI:R — 피해자가 특정 노트를 열거나 보아야 합니다. 툴바 항목을 클릭할 필요는 없으며, 툴바는 자동으로 렌더링됩니다.
  • S:U, C:H/I:H/A:H — 코드는 Obsidian을 실행하는 로컬 사용자 계정으로 전체 Node.js 접근(require('child_process'), 파일 시스템 등) 권한으로 실행됩니다.

세부 사항

Note Toolbar는 노트의 frontmatter 값을 툴바 항목의 레이블, 툴팁 또는 링크에 치환하는 문서화된 {{prop_NAME}} 변수를 지원합니다(skills/note-toolbar-variables/SKILL.md, 위키 Variables.md). 또한 "Scripting" 플러그인 설정이 활성화된 경우 표현식을 라이브 JavaScript로 평가하는 {{js: <expr>}} 변수도 지원합니다.

버그는 src/Toolbar/VariableResolver.ts::replaceVars()의 연산 순서에 있습니다:

  1. 74–92행: {{prop_KEY}} 자리 표시자가 현재 노트의 frontmatter[KEY] 원시 값으로 치환됩니다 — scriptingEnabled 설정과 무관하게, 그리고 해당 frontmatter 값의 출처와 무관하게 무조건 치환됩니다.
  2. 94–104행 (if (this.ntb.settings.scriptingEnabled) 내부): 그런 다음 함수는 이미 치환된 문자열 s에 대해 s.trim().startsWith('{{js:')를 확인하고, 참이면 {{js:/}} 래퍼를 제거하고 나머지를 평가를 위해 JavaScriptAdapter.use()에 전달합니다.

1단계가 2단계보다 먼저 실행되고 s를 제자리에서 다시 쓰기 때문에, 그 자체가 {{js:로 시작하고 }}로 끝나는 frontmatter 값은 "표시되는 텍스트"에서 "실행되는 스크립트"로 승격됩니다 — 툴바 항목이 스크립트 항목으로 작성된 적이 없음에도 불구하고 말입니다. 툴바 항목의 소유자는 {{prop_status}}만 입력했을 뿐이며, 실행 가능한 페이로드는 전적으로 노트 자체의 frontmatter에서 비롯됩니다.

JavaScriptAdapter.evaluate() (src/Adapters/JavaScriptAdapter.ts:143-205)는 실제 샌드박스가 없는 AsyncFunction으로 표현식을 실행합니다:

root@kitploit:~
src/Adapters/Adapter.ts:11
protected static readonly AsyncFunction = (Object.getPrototypeOf(async function(){}) as { constructor: typeof Function }).constructor;

src/Adapters/JavaScriptAdapter.ts:164
const func = new JavaScriptAdapter.AsyncFunction("input", expression);
...
result = await Promise.resolve((func as (...args: unknown[]) => unknown)(args));

이것은 인터프리터/샌드박스가 아닌 JS 엔진의 진짜 AsyncFunction 생성자입니다 — Obsidian의 Electron 렌더러 내부에서 이것은 전체 require()/Node.js 접근 권한을 가지며, 아래 PoC에서 require('child_process').execSync(...)를 통해 확인되었습니다.

결정적으로, JS 어댑터는 내장되어 있으며 동반 플러그인이 필요하지 않습니다 (src/Adapters/AdapterManager.ts:59: adapter = this.js; // built-in, doesn't rely on plugin). 이는 해당 플러그인들이 설치되어야 하는 Dataview/Templater/JS-Engine 변수 접두사와 다릅니다. 플러그인 자체의 "Scripting" 토글(scriptingEnabled, 기본값 false, src/Settings/NoteToolbarSettings.ts:300)만이 이를 제한합니다.

마지막으로, 치환을 트리거하는 렌더 경로는 클릭으로 제한되지 않고 자동입니다: ToolbarRenderer.ts::renderLItems()는 노트에 대해 툴바가 렌더링될 때마다 — 즉 노트를 열거나 볼 때 — this.ntb.vars.resolveText(toolbar, file) (src/Toolbar/ToolbarRenderer.ts:418)를 호출하며, 이는 replaceVars()를 통해 모든 항목의 레이블과 툴팁을 해석합니다. 영향을 받는 툴바 항목을 클릭할 필요가 없습니다.

툴바-노트 할당 자체도 frontmatter 기반입니다(toolbarProp 설정, 기본 키 notetoolbar, src/Settings/NoteToolbarSettings.ts:317). 따라서 동기화된 노트는 자신 위에 어떤 툴바가 렌더링될지 선택할 수 있습니다 — 그러나 피해자가 이미 취약한 항목을 포함하는 기본 툴바나 폴더 매핑을 사용하고 있다면 익스플로잇에 이것이 필요하지 않습니다.

이는 유지관리자 자신의 SECURITY.md가 문서화한 위험("User scripts... executes JavaScript provided by the user... intentional and by design")과 구별됩니다 — 그것은 사용자가 의도적으로 {{js:}} 툴바 항목을 작성하거나, 그것을 포함하는 공유 툴바 구성을 의도적으로 가져오는 경우를 설명합니다. 여기서는 구성자 관점에서 툴바 항목이 전혀 스크립트 항목이 아니며(평범한 속성 표시 레이블임), 실행 가능한 페이로드는 일반적인 노트 콘텐츠/frontmatter를 통해 도착합니다 — 다른 Obsidian 동기화/구성 코드 실행 발견들(obsidian-syncthing-integration, obsidian-codescript-toolkit)과 동일한 신뢰할 수 없는 vault 콘텐츠 위협 모델입니다.

개념 증명

단순한 함수 추출이 아닌, 실제 빌드된 플러그인(감사된 소스에서 빌드된 main.js)을 로드한 실제 Obsidian 1.13.4 데스크톱 인스턴스(Xvfb + fluxbox)에 대해 동적으로 검증되었습니다.

  1. Project Alpha.md가 있는 vault PoCVault/:

    root@kitploit:~
    ---
    status: "{{js: require('child_process').execSync('id > /tmp/ntb_poc_proof.txt; date >> /tmp/ntb_poc_proof.txt; whoami >> /tmp/ntb_poc_proof.txt'); return 'ok';}}"
    notetoolbar: Toolbar
    ---
    
  2. Note Toolbar 플러그인 설치, 커뮤니티 플러그인 신뢰, 플러그인 자체 Settings UI를 통해 "Toolbar"라는 이름의 툴바 하나를 생성하고, Label 필드가 {{prop_status}}인 단일 항목을 구성(평범한 속성 표시 레이블 — 스크립트 항목으로 구성된 적 없음).

  3. 플러그인 설정 Scripting을 켬(피해자 옵트인, 기본값은 꺼짐).

  4. 단순히 Project Alpha.md를 열거나 봄 — 툴바 항목을 클릭하지 않음 — 툴바가 ok(JS 표현식의 반환 값)를 표시하며 렌더링되고, 주입된 셸 명령이 실제로 실행됨:

    터미널 증거 (~/engagements/obsidian-note-toolbar/evidence/02_terminal_proof_of_execution.png):

    root@kitploit:~
    $ cat /tmp/ntb_poc_proof.txt
    uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),105(docker),992(kvm)
    Fri Jul 31 09:31:33 PM +05 2026
    kali
    

    원시 frontmatter 페이로드와 렌더링된 ok 툴바를 보여주는 Obsidian 스크린샷 (~/engagements/obsidian-note-toolbar/evidence/01_obsidian_toolbar_rce_trigger.png).

    소스 노트는 ~/engagements/obsidian-note-toolbar/evidence/poc_note_frontmatter.md에 보존됨.

영향

Obsidian을 실행하는 로컬 사용자로서의 완전한 임의 코드 실행으로, 단순히 노트를 여는 것만으로 트리거됩니다 — 클릭도, 명시적인 "스크립트 실행" 동작도, 대화 상자도 없습니다. 팀/공유 vault 워크플로(Obsidian Sync, Git 기반 vault, Syncthing, Google Drive 등)에서 노트의 frontmatter를 편집할 수 있는 모든 기여자는 Note Toolbar의 Scripting 설정이 활성화되어 있고 노트 속성을 표시하는 툴바 항목이 있는 다른 모든 협업자에 대해 RCE를 달성할 수 있습니다.

취약점 (CWE)

  • CWE-94: 코드 생성의 부적절한 제어('코드 주입') — 신뢰할 수 없는 frontmatter 데이터가 나중에 실행 가능한 스크립트 지시문으로 해석되는 문자열에 연결됩니다.
  • CWE-1336: 템플릿 엔진에 사용되는 특수 요소의 부적절한 중화 — {{...}} 템플릿/변수 구문이 그 자체로 추가 템플릿 지시문을 포함하는 공격자 제어 치환 값을 중화하지 않습니다.

해결 방안

VariableResolver.replaceVars()에서 스크립팅 접두사 검사({{js:, {{dv:, {{jse:, <%/{{tp:)는 {{prop_*}}/{{note_title}}/등의 치환이 실행된 후의 문자열이 아니라 원본, 치환 전 문자열에 대해 평가되어야 합니다 — 즉 툴바 항목 자체가 스크립트 지시문으로 작성되었는지 미리 판단하고, 치환된 frontmatter 값이 스크립트 지시문으로 재해석되도록 절대 허용하지 않아야 합니다. 구체적으로: hasVars 스타일의 스크립팅 접두사 검사를 PROP/SELECTION/NOTE_TITLE/등의 치환 블록 이전에 수행하고, 치환 후 문자열에 대해 startsWith('{{js:'/'{{dv:'/...)를 다시 검사하지 않도록 합니다. 심층 방어로서, frontmatter에서 오는 값들이 {{...}} 문법에 다시 진입할 수 없도록 이스케이프/중화될 수도 있습니다.

크레딧

Dostxodjayev Abdullox

신고 채널

이 저장소의 SECURITY.md는 GitHub 비공개 취약점 신고가 활성화되어 있으며 선호되는 채널임을 확인합니다("GitHub private vulnerability reporting: use the 'Report a vulnerability' button under the Security tab of this repo"). GitHub 계정이 없는 신고자를 위한 Google Form 대체 수단도 있습니다. 독립적으로 재검증됨: gh api repos/chrisgurney/obsidian-note-toolbar/private-vulnerability-reporting --jq .enabled → true, 그리고 gh api repos/chrisgurney/obsidian-note-toolbar/security-advisories --jq 'length' → 0 (이전 권고 없음, 중복 위험 없음).

도구 다운로드