한 번 뜯어보는 github.dev 1-Click 토큰 탈취 익스플로잇

TLDR Dev에서 본 분석글. .ipynb 한 번 열기 = OAuth 토큰 통째 노출. WOW.

Jun Noh

오늘도 메일로 온 TLDR Dev 보다가 분석글 하나를 발견했다.

How I almost stole your GitHub token with a 1-click VSCode exploit

제목부터 끌려서 한 번 정리해두고 싶어졌다.

요약하자면 — github.dev 에서 악성 .ipynb 한 번 클릭으로 내 모든 private repo에 접근 가능한 OAuth 토큰이 자동 탈취된다는 거다.

그게 다다. 클릭 한 번에 모든 게 끝나는 거다.


github.dev 가 어떻게 동작하길래?

먼저 배경부터. github.com 보다가 . 키 누르면 열리는 그 브라우저 안의 VSCode 있잖나. 그게 바로 github.dev다.

이 친구는 토큰을 이렇게 받는다.

  1. github.com 이 OAuth 토큰을 발급
  2. github.dev 한테 POST로 넘김
  3. github.dev 가 그걸 브라우저 localStorage 에 저장
  4. 이후 깃 작업할 때 그 토큰으로 GitHub API 호출

여기서 두 가지가 좀 거슬린다.

  • 토큰이 full-repo scope다. 지금 보고 있는 레포만이 아니라 내가 접근 가능한 모든 레포에 대한 권한이라는 거다.
  • localStorage 에 박혀 있다. JS로 접근 가능한 곳에 그냥 놓여 있다는 뜻이다.

즉, github.dev 안에서 임의 JS 한 번만 돌릴 수 있으면 그걸로 게임이 끝나버린다는 거다.

그럼 어떻게 임의 JS를 돌릴까? CSP도 빡세고 iframe 격리도 되어 있는데 말이다.

답은 Jupyter notebook이었다.


키보드 이벤트 위조

.ipynb 셀 안에 박힌 JS는 iframe 안에서 실행된다. 정상적으로는 부모 VSCode UI를 못 만진다.

근데 VSCode가 UX 편의성 때문에 keydown 이벤트는 webview → 메인 윈도우로 버블링되는 걸 허용해놨다. 노트북 안에서 키 입력해도 단축키가 먹혀야 하니까 그런 거다.

문제는 “진짜 키 입력”이랑 “JS가 위조한 키 입력”을 구분 못한다는 거다.

핵심 코드는 이 한 줄이다.

window.dispatchEvent(
  new KeyboardEvent("keydown", {
    key: "a", code: "KeyA", keyCode: 65,
    ctrlKey: true, shiftKey: true
  })
);

이걸로 Ctrl+Shift+A 같은 단축키를 JS로 위조해서 명령 팔레트를 열고, 또 위조 키로 “워크스페이스 익스텐션 설치” 명령을 호출하는 거다.

설치되는 익스텐션은 공격자가 미리 레포에 박아둔 거다. 익스텐션이 깔리고 나면 그냥 localStorage.getItem(...) 으로 토큰 들고 외부 서버에 fetch 한 번 쏘면 그걸로 상황 종료다.


공격 흐름

악성 .ipynb 가 있는 레포
    ↓ (사용자가 github.dev 에서 클릭)
notebook iframe 안에서 JS 실행

KeyboardEvent 위조로 VSCode 명령 호출

워크스페이스 익스텐션 자동 설치

익스텐션이 localStorage 토큰 읽음

공격자 서버로 전송

사용자가 한 행동은 클릭 1번이 전부다.


어디서 무너졌나

뜯어보면 결국 세 곳이 동시에 무너진 거다.

약점내용
권한 범위github.dev 토큰이 full-repo scope. fine-grained 였으면 피해 한정
저장 위치토큰이 JS-accessible 한 localStorage 에 있음
신뢰 경계iframe 안의 위조 키 입력이 메인 UI 까지 전파됨

한 군데만 제대로 막혀 있었어도 익스플로잇이 안 됐을 텐데, 셋이 동시에 살짝씩 허술했던 셈이다.

그래서 글쓴이가 “거의 털릴 뻔” 이라고 쓴 거다. CSP / iframe sandbox / DOMPurify 같은 다른 방어막이 살아있어서 끝까지 가진 않았다는 뉘앙스다.

defense-in-depth 라는 단어 책으로만 보다가 실전 사례로 보니까 와닿는다.


그래서 패치는?

이미 막혔다. Microsoft가 두 가지 조치를 했다.

  1. github.dev 에서 노트북 열 때 확인 다이얼로그 추가
  2. notebook webview 의 keydown 이벤트 버블링 차단

근데 같은 패턴의 변종은 또 나올 거다. 웹뷰에 임의 콘텐츠 띄우는 모든 곳이 다 잠재 공격면이니까 말이다.


메모

가벼운 글이라 길게 안 쓰고 메모만.

뭐, 어차피 github.dev가 뭔지 이번에 알았다.

  • 모르는 레포의 .ipynb. 키로 열지 말기
  • github.dev 안 쓸 땐 로그아웃 (localStorage 토큰 안 남게)
  • Classic PAT 말고 fine-grained PAT 쓰기. Classic 은 사실상 admin
  • VSCode “이 워크스페이스가 추천하는 익스텐션 설치할래?” 팝업, 무지성 yes 누르지 말기

뭐, 애초에 추천 익스텐션이 진짜 도움이 됐던 적이 없어서… 그냥 참고 정도만 해두면 될 거 같다.


마치며

한 줄 정리: 편의 기능 하나가 보안 경계 하나를 무너뜨릴 수 있다.

VSCode 가 keydown 버블링 허용한 건 진짜 사소한 UX 결정이었을 거다. 근데 그게 OAuth 토큰 탈취까지 연결됐다.

이 분석글이 좋았던 건 “이게 됨” 에서 멈추지 않고 왜 됐는지의 구조적 원인을 다 까놨다는 점이다.

익스플로잇 자체보다 이 분해 과정이 훨씬 더 재밌었다.

다음에 또 비슷한 분석글 보이면 정리해야겠다.

마침.

다른 글 보기