북한 Sapphire Sleet, Mastra npm 패키지 140개 오염해 개발자 암호화폐 탈취
2026년 6월 17일 새벽 1시 20분(UTC), AI 에이전트 프레임워크 Mastra의 npm 저장소에 조용한 변화가 일어났다. 정규 버전 업데이트처럼 보이는 1.13.1 릴리스가 배포됐고, 그 안에는 개발자 컴퓨터의 암호화폐 지갑 166개를 동시에 노리는 다단계 악성코드가 숨어 있었다. 장악된 관리자 계정 하나, 의존성 한 줄 추가, 그리고 단 19분. 그것으로 @mastra 스코프 전체 140개 패키지가 무기화됐다.
배경: 공급망을 노리는 북한 크립토 사냥꾼
Sapphire Sleet는 2020년부터 추적되는 북한 국가 연계 위협 행위자다. 미국·아시아·중동의 금융·암호화폐·블록체인 기업을 주요 표적으로 삼아 국가 자금 조달을 위한 암호화폐 탈취에 집중한다. 보안 업계에서는 BlueNoroff, UNC1069, STARDUST CHOLLIMA 등의 이름으로도 추적된다.
이 그룹이 소프트웨어 공급망 채널을 택한 것은 이번이 처음이 아니다. 2026년 4월, Sapphire Sleet는 주간 1억 건 이상 다운로드되는 JavaScript HTTP 클라이언트 Axios의 npm 패키지를 오염시킨 전례가 있다. Axios 공격 이후에도 이 채널이 반복 사용됐다는 사실 자체가, 성공 여부와 무관하게 npm 공급망이 이 그룹에게 운영 패턴으로 정착했음을 보여준다.
공격 전개: 단 한 줄의 의존성으로 140개 패키지를 뒤집다
공격 설계는 정밀했다. 공격자는 npm 생태계의 신뢰 구조, 즉 관리자 계정 권한과 시맨틱 버전 자동 업데이트 규칙을 동시에 역이용했다.
6월 16일 오전 7시 5분, 공격자 계정(sergey2016@tutamail.com)은 easy-day-js@1.11.21을 npm에 등록했다. 주간 5,700만 건 다운로드를 기록하는 정규 라이브러리 dayjs와 이름이 유사한 이 패키지는 처음에는 완전히 무해한 미끼였다. 탐지를 피하기 위한 평판 세탁이었다.
다음 날 새벽 1시 1분, 같은 계정에서 easy-day-js@1.11.22가 등록됐다. 이번에는 달랐다. 4,572바이트의 난독화된 드로퍼가 내장돼 있었다. 그리고 19분 후인 1시 20분, 공격자는 이미 장악해 둔 Mastra 메인테이너 계정 ehindero를 이용해 패키지 업데이트를 시작했다. 이때부터 약 88분 동안 mastra@1.13.1을 포함한 140개 이상의 @mastra 패키지가 잇따라 오염된 버전으로 교체됐고, 모두 latest 태그를 받았다. ehindero는 권한이 회수되지 않은 채 남아 있던 옛 기여자 계정이었으며, 어떤 방법으로 탈취됐는지는 아직 공개되지 않았다. 변경 내용은 단 하나, "easy-day-js": "^1.11.21" 의존성 한 줄 추가였다.
npm의 시맨틱 버전 레인지 규칙상 ^1.11.21은 최신 패치 버전인 1.11.22를 자동으로 가리키게 돼 있었다. 이후 어느 개발자든 npm install을 실행하는 순간, 악성 드로퍼가 node setup.cjs --no-warnings postinstall 훅을 통해 자동 실행됐다. postinstall 훅은 패키지 설치 완료 직후 npm이 자동으로 호출하는 스크립트로, 별도 코드 실행 없이 설치 행위만으로 트리거된다.
드로퍼는 세 단계로 확장됐다. 첫 단계에서 TLS 인증서 검증을 비활성화한 채 C2 서버에 연결하고 약 41KB 규모의 크로스플랫폼 Node.js 태스킹 클라이언트를 내려받았다. 두 번째 단계에서는 운영체제별로 영속성을 확보했다. Windows는 레지스트리 Run 키(NvmProtocal)에, macOS는 LaunchAgent 설정 파일에, Linux는 systemd 유닛에 임플란트를 심었다. 세 번째 단계에서는 Windows 환경에 한해 별도 C2 인프라에서 PowerShell 백도어를 내려받아 메모리에서 실행하고, PSReadLine 명령 기록을 삭제해 흔적을 지웠다.
피해와 결과: 166개 지갑, CI/CD 파이프라인도 위험
페이로드의 최종 표적은 암호화폐였다. MetaMask, Phantom, Coinbase, Binance를 포함해 166개의 브라우저 암호화폐 지갑 익스텐션 ID가 하드코딩돼 있었다. 여기에 더해 Chrome·Edge·Brave의 히스토리 SQLite 데이터베이스, 실행 중인 프로세스 목록, API 키, 환경 변수도 C2로 전송됐다.
피해 범위는 단순히 개발자 개인에 그치지 않는다. postinstall 훅은 코드에서 패키지를 import했을 때가 아니라 설치되는 순간 실행된다. 따라서 CI/CD 파이프라인에서 npm install이 실행되는 빌드 서버라면, @mastra 패키지를 실제로 사용하지 않아도 의존성 트리 어딘가에 이름이 있는 것만으로 드로퍼가 동작했다. 빌드 서버에는 개발자 개인 PC보다 더 많은 API 키와 환경 변수가 집중돼 있는 경우가 많다.
Microsoft는 사고를 탐지하고 분석 결과를 공개하며 Defender 시그니처 4종(Trojan:JS/NpmStealz.Z!MTB 외)을 배포했고, 6월 19일 후속 분석에서 이 캠페인을 높은 신뢰도(high confidence)로 Sapphire Sleet에 귀속했다. 피해 개발자의 정확한 규모는 아직 공개되지 않았다.
왜 이 사건이 중요한가
이번 공격에서 Sapphire Sleet가 택한 핵심 전략은 "의존성 사슬 무기화"였다. 코드를 직접 수정하지 않고 신뢰받는 패키지 생태계에 가짜 라이브러리 한 줄을 심는 것만으로 140개 패키지를 동시에 오염시켰다. 관리자 계정 하나가 전체 스코프의 공격 표면이 된다는 것을 다시 보여준 사례다.
19분이라는 속도는 탐지 창이 사실상 없었다는 의미이기도 하다. npm 레지스트리가 패키지 이상 징후를 감지하고 대응하기 전에, 세계 어딘가의 빌드 서버에서 이미 npm install이 완료됐을 수 있다.
한국은 전 세계에서 암호화폐 거래 활동이 가장 활발한 시장 중 하나다. Sapphire Sleet의 표적 기준인 금융·크립토·DeFi 개발 환경과 국내 블록체인 스타트업 생태계가 교차하는 지점은 좁지 않다. npm 기반 프로젝트를 운용하는 개발팀이라면, 이번 사건의 공격 경로인 메인테이너 계정 보안과 postinstall 훅 실행 정책이 어떻게 설정돼 있는지 짚어볼 이유가 생겼다.