Axios npm 패키지 공급망 공격 분석 — 2시간 54분 동안 벌어진 일
3월 마지막 주말, 개발자들을 긴장하게 만든 사건
지난 3월 31일 새벽, JavaScript 생태계에 작은 지진이 일어났습니다. 주간 다운로드가 1억 건에 달하는 axios 패키지가 공격을 받았다는 소식이었죠. 평소 같으면 그냥 지나쳤을 수도 있는데, 이번엔 조금 달랐습니다. 공격의 정교함이나 배후 분석 결과를 보니, 단순한 장난이 아니라는 생각이 들더라고요.
2시간 54분. 악성 버전이 npm에 올라가 있던 시간입니다. 짧다면 짧지만, axios를 쓰는 프로젝트가 얼마나 많은지 생각해보면 결코 짧지 않은 시간이죠.

사건의 전개 과정
사전 준비 단계 (3월 30일)
공격자들은 하루 전부터 치밀하게 준비했습니다. **[email protected]**라는 이름의 패키지를 미리 배포해둔 거죠. 이름만 봐서는 crypto-js를 모방한 것 같은데, 실제로는 악성 코드가 숨어있는 패키지였습니다.
이런 사전 배치는 탐지를 우회하기 위한 전형적인 수법입니다. 갑자기 새로운 패키지가 나타나서 axios에 추가되면 의심스럽겠지만, 이미 하루 전에 올라와 있던 패키지라면 덜 의심스러워 보이거든요.
본격적인 공격 (3월 31일 00:21 UTC)
공격의 핵심은 jasonsaayman 계정 탈취였습니다. axios의 핵심 관리자 계정이죠. 공격자들은 이 계정의 npm 장기 액세스 토큰을 확보했고, 계정 이메일까지 자신들의 Proton Mail 주소로 바꿔버렸습니다.
여기서 흥미로운 부분이 하나 있습니다. 해당 계정은 OIDC Trusted Publishing이 활성화되어 있었는데도 공격이 성공했다는 점입니다. OIDC가 있으면 더 안전할 것 같은데 왜 뚫렸을까요?
문제는 워크플로우에서 NPM_TOKEN 환경 변수를 함께 사용하고 있었다는 점입니다. npm은 OIDC보다 NPM_TOKEN을 우선시하기 때문에, 공격자가 탈취한 토큰만으로도 모든 보안 장치를 우회할 수 있었던 거죠.
악성 버전 배포 전략
공격자들은 axios 1.14.1과 axios 0.30.4 두 버전을 배포했습니다. 그런데 이걸 GitHub Actions를 통하지 않고 npm CLI로 직접 올렸더라고요. 그래서 GitHub 저장소에는 어떤 흔적도 남지 않았습니다.
더 교묘한 건 latest와 legacy 태그 모두에 악성 버전을 지정해서, npm install axios를 실행하는 모든 사용자가 악성 버전을 받도록 했다는 점입니다. package.json의 의존성 목록에 plain-crypto-js만 추가된 형태라, 일반적인 diff 분석으로는 탐지하기 어려웠을 겁니다.
악성 코드의 동작 방식
postinstall 훅을 통한 실행
패키지가 설치되면 plain-crypto-js의 postinstall 스크립트가 자동으로 실행됩니다. 이때 난독화된 setup.js 파일이 백그라운드에서 돌아가면서 운영체제를 확인하고, 각 플랫폼에 맞는 백도어를 다운로드해서 실행하는 구조였습니다.
플랫폼별 감염 방식
| OS | 동작 방식 | 저장 경로 |
|---|---|---|
| Windows | PowerShell을 %PROGRAMDATA%\wt.exe로 복사 후 숨김 실행 |
%TEMP%\6202033.ps1 |
| macOS | curl로 Mach-O 바이너리 다운로드 후 백그라운드 실행 | /Library/Caches/com.apple.act.mond |
| Linux | Python 백도어 스크립트 다운로드 후 실행 | /tmp/ld.py |
모든 플랫폼의 페이로드는 sfrclak[.]com:8000 서버와 통신하면서 시스템 정보를 수집하고, 추가 명령을 받을 수 있는 구조였습니다.
흔적 지우기
정말 치밀했던 부분은 anti-forensics 기능입니다. 드로퍼는 자신의 임무를 마치면 스스로를 삭제하고, 변조했던 package.json도 원본으로 되돌려놓습니다. 그래서 나중에 포렌식 분석을 시도해도 감염 여부를 확인하기 어렵게 만들어 놓았더라고요.
배후 분석 - UNC1069
Google의 Threat Intelligence Group에서 이번 공격을 UNC1069라는 북한 연계 위협 행위자의 소행으로 귀속했습니다. 근거는 다음과 같습니다:
- WAVESHAPER.V2 백도어가 과거 UNC1069가 사용했던 WAVESHAPER의 업데이트 버전
- C2 인프라가 이들의 과거 캠페인과 연결점을 보임
- 2018년부터 활동해온 금전 목적의 그룹으로, 주로 암호화폐와 AI 분야를 노림
개인적으로는 이런 국가 차원의 위협 행위자들이 오픈소스 생태계를 노리고 있다는 게 좀 우려스럽습니다. npm 같은 중앙화된 레지스트리의 취약점이 이렇게 광범위한 영향을 미칠 수 있다는 걸 보여주는 사례거든요.

즉시 해야 할 일들
혹시 3월 31일 새벽에 axios를 설치했거나 CI/CD가 돌아갔다면 다음 조치들을 확인해보세요:
1. 버전 확인 및 업데이트
npm list axios
npm audit
axios 1.14.1이나 0.30.4가 있다면 즉시 안전한 버전(1.7.8 등)으로 교체해야 합니다.
2. 자격증명 전면 교체
해당 시간대(3월 31일 00:21–03:15 UTC)에 관련된 환경이라면 모든 시크릿과 API 키를 폐기하고 새로 발급받으세요. 백도어가 이런 정보들을 수집했을 가능성이 있거든요.
3. 시스템 점검
다음 파일들이 있는지 확인해보세요:
- Windows:
%PROGRAMDATA%\wt.exe - macOS:
/Library/Caches/com.apple.act.mond - Linux:
/tmp/ld.py
4. 전이 의존성 감사
axios를 직접 쓰지 않아도 다른 패키지가 간접적으로 가져왔을 수 있습니다. 전체 의존성 트리를 점검해보세요.
5. npm 계정 보안 강화
패키지 관리자라면 장기 액세스 토큰을 폐기하고, OIDC 설정 시 워크플로우에서 NPM_TOKEN을 제거해야 합니다. MFA는 당연히 필수고요.

마치며
이번 사건을 보면서 공급망 보안의 중요성을 다시 한번 느끼게 됩니다. 2시간 54분이라는 짧은 시간이었지만, axios만큼 널리 쓰이는 패키지가 공격받으면 파급 효과가 얼마나 클 수 있는지 보여주는 사례였죠.
특히 OIDC 같은 보안 기능이 있어도 설정 실수로 우회될 수 있다는 점은 꽤 교훈적입니다. 보안은 결국 가장 약한 고리에서 뚫리는 법이니까요.