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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
zotlit-PoC — PoC — ZotLit에서 승인되지 않은 로컬 경로의 파일을 복사하는 첨부 파일 가져오기 (GHSA-4qh7-66xv-h329, CVE-2026-87000, CVSS 5.5). | Kitploit
도구/GitHubGitHub/squeeze440/zotlit-poc
Vulnerability AnalysisExploitationData ExfiltrationInformation GatheringSecurity VirtualizationPapers & Research
GitHubsqueeze440/zotlit-poc

zotlit-PoC

PoC — ZotLit에서 승인되지 않은 로컬 경로의 파일을 복사하는 첨부 파일 가져오기 (GHSA-4qh7-66xv-h329, CVE-2026-87000, CVSS 5.5).

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

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

ZotLit: 보안 권고

CVE 상태: 요청됨, 할당 대기 중. 이 발견은 GHSA-4qh7-66xv-h329로 게시되었습니다. CVE가 할당되면 이 저장소는 CVE-YYYY-NNNNN-zotlit-PoC로 이름이 변경되고 이 배너는 CVE 링크로 대체됩니다.

연구자Dostxodjayev Abdullox (@squeeze440)
권고GHSA-4qh7-66xv-h329
CVSS 3.15.5 (Medium)
취약점CWE-73, CWE-200

요약

AidenLx ZotLit(aidenlx/zotlit) 1.1.12의 첨부 파일 가져오기 기능에서 파일 이름 또는 경로에 대한 외부 제어가 존재하여, 공유/동기화된 Zotero 라이브러리를 제어하는 공격자가 조작된 linked_file 첨부 경로를 통해 피해자의 파일 시스템에서 임의의 로컬 파일을 피해자의 Obsidian vault로 유출시킬 수 있습니다.

제품

ZotLit — Zotero 통합을 위한 Obsidian 플러그인 (aidenlx/zotlit, 플러그인 id zotlit, 매니페스트 버전 1.1.12)

테스트된 버전

Git 커밋 41e60aaa6178629b34f8a42d104e757594b72435 (2026-07-31)

추정 CVSS v3.1

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

직관적이지 않은 지표: AV:L — 익스플로잇은 피해자의 로컬 Obsidian/zotlit 프로세스가 공격자가 제공한 Zotero 항목 메타데이터(공유/동기화된 라이브러리를 통해 전달됨)를 처리할 때 발생하며, 이는 "로컬 앱이 처리하는 악성 파일" 버그에 사용되는 것과 동일한 관례입니다. 전달 자체는 네트워크를 통해 이루어질 수 있음에도(공유 그룹 라이브러리, 이메일로 보낸 내보내기 파일) 그렇습니다. UI:R — 피해자가 악성 라이브러리를 Zotero로 가져오기/동기화하고, 조작된 첨부 파일을 참조하는 노트에서 ZotLit의 노트 가져오기(또는 주석/인용 삽입) 기능을 실행해야 합니다. 이는 플러그인의 핵심이며 기본 활성화된 기능(attachment.import는 기본값 true)의 일상적인 사용이지, 특이한 동작이 아닙니다. I:N/A:N — 이 발견은 읽기/복사 프리미티브에 불과합니다. 목적지 측 경로 순회는 주장되지 않습니다(관련되지만 검증되지 않은 관찰은 세부 정보 참조).

세부 정보

ZotLit은 Zotero의 SQLite 데이터베이스(또는 동기화/공유 라이브러리)에서 첨부 파일 메타데이터를 직접 읽으며, 자유 텍스트 itemAttachments.path 컬럼을 포함하고, "연결된" 첨부 파일을 어디에서 읽을지 결정할 때 이를 완전히 신뢰합니다:

  • packages/db/src/lib/zt-path.ts:62-63 — attachmentAbsPath(), "linked-absolute" 케이스:

    root@kitploit:~
    case "linked-absolute":
      return parsed.path;
    

    path에 attachments: 기본 디렉터리 플레이스홀더가 없는 linkMode: 2(linked_file) 행의 경우, parsed.path는 읽을 절대 파일 시스템 경로로 그대로 반환되는 원시 DB 문자열입니다 — 허용 목록도, Zotero 데이터 디렉터리나 사용자가 승인한 위치로의 제한도 없습니다.

  • packages/db/src/lib/context/zt-template-attach.ts:101-106 — attachmentFilename(), "linked-absolute" 케이스:

    root@kitploit:~
    case "linked-absolute":
      return basename(path.path);
    

    vault 내 복사본을 만들 때 사용되는 파일 이름은 동일한 공격자 제어 경로에서 basename()을 통해 파생되므로, 목적지 파일 이름은 공격자의 영향을 받지만 경로 구분자가 없습니다(이 분기에서는 순회 안전).

  • apps/obsidian/src/services/note-import/note-parser.ts:403-430 — resolveEmbeddedImage()는 Zotero 문헌 노트의 임베디드 이미지 주석을 Obsidian Markdown으로 변환하는 동안 호출됩니다(ZotLit의 "Import Note" 기능의 일상적이고 기본적인 동작). 이 함수는 sourcePath = attachmentAbsPath(attachment, ...)를 해석하고 deps.resolveLink({ sourcePath, vaultName: \${attachment.key}-${filename}` })`를 호출하여 — 공격자가 선택한 모든 절대 경로의 복사본을 vault로 큐에 넣습니다.

  • apps/obsidian/src/services/attachment-import/service.ts:125-155 — AttachmentImportBatch.resolveLink()는 { source: sourcePath, dest: <vault attachment folder>/<key>-<filename> }를 큐에 넣으며, 이는 attachment.import 설정으로만 제한되며 그 기본값은 true입니다(apps/obsidian/src/services/settings/schema.ts:123).

  • apps/obsidian/src/lib/copy-attachments.ts:43-60 — copyAttachment()는 경로 검증 없이 실제 읽기+쓰기를 수행합니다: stat(source) 후 copyFile(source, dest).

최종 효과: 공격자가 linked_file(linkMode 2) Zotero 항목을 피해자의 라이브러리에 넣을 수 있다면 — 예를 들어 공유 Zotero 그룹 라이브러리, 피해자가 가져오는 .rdf/.json/Better BibTeX 내보내기 파일, 또는 공격자가 쓰기 권한을 가진 동기화된 라이브러리 — 그 항목의 첨부 경로를 피해자 디스크의 임의 파일(~/.ssh/id_rsa, 브라우저 자격 증명 저장소, 다른 vault, .env 파일 등)로 설정할 수 있습니다. 피해자가 기본 attachment.import: true 설정으로 ZotLit의 노트 가져오기를 실행하는(또는 해당 주석을 렌더링/임베드하는) 순간, ZotLit은 예측 가능한 이름(<attachmentKey>-<basename>)으로 해당 파일의 내용을 피해자의 Obsidian vault에 조용히 복사합니다. vault는 일상적으로 동기화되거나, git에 커밋되거나, 게시되므로, 이는 공격자가 다른 방법으로는 결코 접근할 수 없었던 데이터를 공격자(또는 동기화 대상에 접근할 수 있는 누구든)가 읽을 수 있는 위치로 이동시킵니다.

관련되지만 검증되지 않은 관찰(이 발견의 PoC에는 포함되지 않음): attachmentFilename()의 형제 분기인 "storage" 분기(zt-template-attach.ts:103-104)는 다른 두 분기에 적용된 basename() 호출 없이 원시 경로 접미사를 반환합니다. 이것이 독립적으로 익스플로잇 가능한지는 Zotero 자체의 저장소 동기화 메커니즘이 로컬로 다운로드된 파일의 이름을 어떻게 지정하는지에 달려 있으며, 이는 이 저장소 외부의 문제이고 검증되지 않았습니다 — 일관성을 위해 해결할 가치가 있는 강화 격차로 여기에만 표시합니다.

개념 증명

동적 검증: 정확한 취약 함수들(parseAttachmentPath, attachmentAbsPath, attachmentFilename, copyAttachments/copyAttachment/writeCopy/destMatches, isErrno, reflink)을 배포된 소스에서 그대로(위에 file:line 인용, 로직 변경 없음) 추출하여 Node에서 직접 실행했습니다. 이 샌드박스에서는 완전한 오프라인 pnpm 워크스페이스 설치가 불가능했기 때문입니다(corepack/pnpm 툴체인이 오프라인에서 깨짐). 유일한 대체는 LogTape 로거 호출을 console.warn으로 바꾼 것뿐이며 — 외형상의 변경일 뿐 제어 흐름에는 전혀 영향이 없습니다. 스크립트: ~/engagements/zotlit/evidence/poc-verify.mjs.

단계:

  1. ~/engagements/zotlit/evidence/victim-disk/id_rsa에 시뮬레이션된 피해자 비밀 파일(더미 내용, 시뮬레이션임이 명확히 표시됨).
  2. 조작된 공격자 제어 Zotero 첨부 행: { key: "EVILKEY1", path: "<path to victim's id_rsa>", linkMode: 2 } — getAttachmentByKey가 Zotero DB에서 반환하는 것과 정확히 같은 형태.
  3. 실제 attachmentAbsPath() 실행 — 피해자의 파일을 소스로 그대로 해석, 검증 없음.
  4. 실제 attachmentFilename() 실행 — id_rsa를 안전해 보이는 목적지 basename으로 해석.
  5. note-parser.ts:427의 실제 vaultName 구성(${key}-${filename})을 재현.
  6. 실제 copyAttachments() 실행 — 결과 { copied: 1, skipped: 0, missing: 0 }.
  7. fake-vault/attachments/EVILKEY1-id_rsa를 다시 읽음 — 내용이 피해자의 원본 파일과 바이트 단위로 일치.

실제 xterm에서의 전체 실행(명령 + 출력) 스크린샷: ../evidence/poc-run.png

영향

피해자의 Zotero 라이브러리 내용에 영향을 줄 수 있는 공격자(공유/그룹 라이브러리, 가져온 서지 내보내기 파일, 또는 동기화된 라이브러리)는 피해자의 OS 사용자가 읽을 수 있는 임의의 로컬 파일 — SSH 키, 자격 증명 저장소, 다른 vault 내용, .env/설정 비밀 — 을 피해자의 Obsidian vault로 유출시킬 수 있으며, 피해자가 기본 설정으로 ZotLit의 핵심 노트 가져오기 기능을 사용하는 순간 프롬프트나 확인 없이 이루어집니다. vault는 일반적으로 동기화되거나(Obsidian Sync, git, 클라우드 폴더) 게시되므로, 이는 로컬 파일 읽기 프리미티브를 현실적인 원격 노출로 전환합니다.

취약점

  • CWE-73: 파일 이름 또는 경로에 대한 외부 제어
  • CWE-200: 비인가 행위자에 대한 민감 정보 노출

해결 방안

linked_file(linkMode 2) 첨부 소스를 피해자가 명시적으로 구성하거나 승인한 파일 시스템 위치(예: Zotero 자체의 구성된 baseAttachmentPath, 또는 Zotero 데이터 디렉터리)로 제한하고, 경로가 예상 루트를 벗어나는 연결된 첨부 파일을 자동 복사하기 전에 사용자에게 확인을 요청할 것을 제안합니다. 심층 방어로, attachmentFilename()의 linked-absolute/linked-base 분기에 이미 사용되는 basename() 전용 정리(sanitization)를 "storage" 분기에도 적용하여, 어떤 코드 경로도 정리되지 않은 파일 이름 조각을 반환하지 않도록 하는 것을 고려하십시오.

크레딧

Dostxodjayev Abdullox (GitHub: squeeze440)

보고 채널

aidenlx/zotlit에서 비공개 취약점 보고가 활성화되어 있습니다 — 공개 이슈가 아닌 저장소의 GitHub Security Advisory(GHSA) 초안을 통해 보고하십시오.

도구 다운로드