# Axios가 NPM에서 침해되어 원격 접근 트로이목마가 배포됨

> Clean Markdown view of GeekNews topic #28047. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=28047](https://news.hada.io/topic?id=28047)
- GeekNews Markdown: [https://news.hada.io/topic/28047.md](https://news.hada.io/topic/28047.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2026-03-31T17:22:27+09:00
- Updated: 2026-03-31T17:22:27+09:00
- Original source: [stepsecurity.io](https://www.stepsecurity.io/blog/axios-compromised-on-npm-malicious-versions-drop-remote-access-trojan)
- Points: 12
- Comments: 2

## Summary

**주간 다운로드 3억 회**가 넘는 axios가 npm 공급망 공격을 당했습니다. 공격자가 유지관리자 계정을 탈취해 악성 버전을 직접 올렸고, 가짜 의존성을 통해 **원격 접근 트로이목마(RAT)** 를 설치하는 방식이었습니다. 설치 후 흔적까지 지우는 치밀한 구조였는데, npm이 수 시간 만에 제거하긴 했지만 그 사이 얼마나 퍼졌는지는 알 수 없습니다. axios처럼 모두가 당연하게 쓰는 패키지가 뚫릴 수 있다는 점에서, **npm 토큰 관리와 lockfile 검증**을 다시 한번 점검해볼 계기입니다.

## Topic Body

- 널리 사용되는 **axios HTTP 클라이언트**의 두 악성 버전이 npm에 게시되어, 설치 시 **원격 접근 트로이목마(RAT)** 를 배포함
- 공격자는 **유지관리자 계정 자격 증명 탈취**를 통해 GitHub Actions를 우회하고 수동으로 악성 패키지를 업로드함
- 악성 버전은 **가짜 의존성 `plain-crypto-js@4.2.1`** 을 포함해, `postinstall` 스크립트로 RAT를 설치하고 흔적을 삭제함
- RAT는 **macOS, Windows, Linux**를 모두 감염시키며, C2 서버(`sfrclak.com:8000`)와 통신해 추가 페이로드를 다운로드함
- npm과 GitHub이 신속히 악성 버전을 제거했으나, **공급망 보안 강화와 자격 증명 보호의 중요성**이 다시 부각됨

---

### axios npm 공급망 공격 개요
- 2026년 3월 31일, 널리 사용되는 **axios HTTP 클라이언트 라이브러리**의 두 악성 버전(`axios@1.14.1`, `axios@0.30.4`)이 npm에 게시됨
- 공격자는 **axios 주요 유지관리자의 npm 자격 증명**을 탈취해 GitHub Actions CI/CD 파이프라인을 우회하고 수동으로 악성 패키지를 게시함
- 두 버전 모두 **`plain-crypto-js@4.2.1`** 이라는 가짜 의존성을 삽입, 이 패키지는 `postinstall` 스크립트를 통해 **원격 접근 트로이목마(RAT)** 를 설치함
- RAT는 macOS, Windows, Linux를 모두 대상으로 하며, C2(Command and Control) 서버(`sfrclak.com:8000`)와 통신해 2단계 페이로드를 내려받음
- 설치 후 악성 코드와 흔적을 삭제하고 깨끗한 `package.json`으로 교체해 **포렌식 탐지를 회피**함

### 공격 타임라인
- 3월 30일 05:57 UTC: `plain-crypto-js@4.2.0`(정상 버전) 게시
- 3월 30일 23:59 UTC: `plain-crypto-js@4.2.1`(악성 버전) 게시, `postinstall` 훅 추가
- 3월 31일 00:21 UTC: `axios@1.14.1` 게시, 악성 의존성 삽입
- 3월 31일 01:00 UTC: `axios@0.30.4` 게시, 동일한 악성 의존성 삽입
- 3월 31일 03:15 UTC: npm이 두 악성 버전 삭제
- 3월 31일 04:26 UTC: npm이 `plain-crypto-js`를 보안 홀더 스텁(`0.0.1-security.0`)으로 대체

### axios 개요
- axios는 **JavaScript 생태계에서 가장 널리 사용되는 HTTP 클라이언트**로, Node.js와 브라우저 모두에서 사용됨
- 주간 다운로드 3억 회 이상으로, 단 한 번의 악성 릴리스도 **광범위한 피해 가능성**을 가짐
- 일반 개발자는 `npm install` 시 악성 코드 설치를 인지하기 어려움

### 공격 단계
- ## 1단계 — 유지관리자 계정 탈취
  - 공격자는 **`jasonsaayman` npm 계정**을 탈취하고 이메일을 `ifstap@proton.me`로 변경
  - 이후 **1.x와 0.x 릴리스 브랜치 모두**에 악성 빌드를 게시
  - 정상 릴리스는 GitHub Actions의 **OIDC Trusted Publisher**를 통해 게시되지만, `axios@1.14.1`은 수동 게시로 `gitHead`와 OIDC 서명이 없음
  - 공격자는 장기 유효한 **npm 액세스 토큰**을 이용한 것으로 추정됨
- ## 2단계 — 악성 의존성 사전 배포
  - `plain-crypto-js@4.2.1`은 `nrwise@proton.me` 계정에서 게시됨
  - `crypto-js`를 **위장**하며 동일한 설명과 저장소 URL 사용
  - `"postinstall": "node setup.js"` 훅을 포함해 설치 시 자동 실행
  - 공격 후 `package.md`를 `package.json`으로 교체해 **증거 삭제 준비**
- ## 3단계 — axios에 의존성 삽입
  - `plain-crypto-js@^4.2.1`을 **런타임 의존성**으로 추가
  - 코드 내에서는 한 번도 import되지 않음 → **유령 의존성(phantom dependency)**
  - `npm install` 시 자동 설치되어 `postinstall` 스크립트 실행

### RAT 드로퍼(setup.js) 분석
- ## 난독화 기법
  - 문자열을 배열 `stq[]`에 암호화 저장하고 `_trans_1`(XOR)과 `_trans_2`(Base64+역순)로 복호화
  - C2 URL은 `http://sfrclak.com:8000/6202033`
  - 복호화된 문자열에는 OS 식별자(`win32`, `darwin`), 파일 경로, 쉘 명령 등이 포함
- ## 플랫폼별 페이로드
  - ### macOS
    - AppleScript를 `/tmp`에 작성 후 `osascript`로 실행
    - C2에서 RAT 바이너리를 받아 `/Library/Caches/com.apple.act.mond`에 저장 및 실행
    - 파일명은 **Apple 시스템 데몬처럼 위장**
  - ### Windows
    - PowerShell 경로 탐색 후 `%PROGRAMDATA%\wt.exe`로 복사
    - VBScript를 통해 C2에서 PowerShell RAT 다운로드 및 실행
    - 임시 파일(`.vbs`, `.ps1`)은 실행 후 삭제
  - ### Linux
    - `curl`로 `/tmp/ld.py` 다운로드 후 `nohup python3`으로 실행
    - `/tmp/ld.py` 파일이 남음
    - 세 플랫폼 모두 `packages.npm.org/product0~2` POST 바디를 사용해 **정상 npm 트래픽처럼 위장**
- ## 자기 삭제 및 은폐
  - `setup.js`와 `package.json` 삭제
  - `package.md`를 `package.json`으로 교체해 **정상 패키지로 위장**
  - 이후 `npm audit`이나 수동 검토로는 탐지 불가
  - 단, `node_modules/plain-crypto-js/` 존재 자체가 감염 증거

### StepSecurity Harden-Runner를 통한 실행 검증
- Harden-Runner는 GitHub Actions에서 **네트워크·프로세스·파일 이벤트를 실시간 기록**
- `axios@1.14.1` 설치 시 두 번의 C2 연결(`curl`, `nohup`)이 감지됨
  - 첫 번째 연결은 `npm install` 시작 2초 후 발생
  - 두 번째 연결은 36초 후, 백그라운드 프로세스로 지속 실행
- 프로세스 트리 분석 결과, `nohup` 프로세스가 **PID 1(init)** 에 고아 프로세스로 남아 **지속 실행** 확인
- 파일 이벤트 로그에서 `package.json`이 두 번 덮어써짐
  - 첫 번째: 설치 중 악성 버전 작성
  - 두 번째: 36초 후 깨끗한 스텁으로 교체

### 침해 지표(IOC)
- ## 악성 npm 패키지
  - axios@1.14.1 · shasum: 2553649f232204966871cea80a5d0d6adc700ca
  - axios@0.30.4 · shasum: d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71
  - plain-crypto-js@4.2.1 · shasum: 07d889e2dadce6f3910dcbc253317d28ca61c766
- ## 네트워크
  - C2 도메인: sfrclak.com
  - IP: 142.11.206.73
  - URL: `http://sfrclak.com:8000/6202033`
- ## 파일 경로
  - macOS: `/Library/Caches/com.apple.act.mond`
  - Windows: `%PROGRAMDATA%\wt.exe`
  - Linux: `/tmp/ld.py`
- ## 공격자 계정
  - `jasonsaayman` (탈취된 유지관리자)
  - `nrwise` (공격자 생성 계정)
- ## 안전 버전
  - axios@1.14.0 (정상)

### 영향 확인 및 대응 절차
- `npm list axios` 또는 `package-lock.json`에서 **1.14.1 / 0.30.4** 확인
- `node_modules/plain-crypto-js` 존재 여부 확인
- OS별 RAT 파일 존재 시 **시스템 완전 감염으로 간주**
- CI/CD 로그에서 해당 버전 설치 이력 확인 후 **모든 비밀키·토큰 교체 필요**

### 복구 단계
1. axios를 **안전 버전(1.14.0 또는 0.30.3)** 으로 고정
2. `plain-crypto-js` 폴더 삭제 후 `npm install --ignore-scripts` 재설치
3. RAT 흔적 발견 시 시스템 재구축
4. 모든 자격 증명(AWS, SSH, CI/CD 등) 회전
5. CI/CD 파이프라인 점검 및 비밀키 교체
6. 자동 빌드 시 `--ignore-scripts` 옵션 사용
7. C2 도메인/IP를 방화벽 또는 `/etc/hosts`로 차단

### StepSecurity Enterprise 기능
- ## Harden-Runner
  - GitHub Actions에서 **네트워크 송신 화이트리스트** 적용
  - 비정상 트래픽 차단 및 로그 기록
  - `sfrclak.com:8000` 연결을 **사전 차단 가능**
- ## Dev Machine Guard
  - 개발자 PC에서 설치된 npm 패키지 실시간 모니터링
  - `axios@1.14.1`, `0.30.4` 설치된 장비 즉시 탐지
- ## npm Package Cooldown Check
  - 새로 게시된 패키지에 **일시적 설치 차단 기간** 적용
  - `plain-crypto-js@4.2.1`과 같은 신속한 악성 게시 탐지 가능
- ## Compromised Updates Check
  - 실시간 악성 패키지 DB 기반으로 PR 병합 차단
  - `axios@1.14.1`, `plain-crypto-js@4.2.1` 즉시 등록
- ## Package Search
  - 조직 전체 PR 및 저장소에서 특정 패키지 도입 위치 검색
  - 영향 범위(레포지토리, 팀, PR) 즉시 파악
- ## AI Package Analyst
  - npm 레지스트리 실시간 모니터링 및 **행동 기반 악성 탐지**
  - 두 악성 버전 모두 게시 후 수분 내 탐지
- ## Threat Center Alert
  - 공격 요약, IOC, 대응 절차 포함한 **위협 인텔 알림** 제공
  - SIEM 연동을 통한 실시간 가시성 확보

### 감사
- axios 유지관리자 및 커뮤니티가 **GitHub issue #10604**를 통해 신속히 대응
- GitHub은 탈취된 계정을 정지하고 npm은 악성 버전 삭제 및 보안 홀더 적용
- **유지관리자·GitHub·npm 간 협력 대응**으로 전 세계 개발자 피해 최소화

## Comments



### Comment 54221

- Author: chanapple
- Created: 2026-03-31T17:29:37+09:00
- Points: 1

이정도 규모 패키지가 털릴거라고 생각해본적은 없는데, axios는 상상 이상이네요.

### Comment 54217

- Author: neo
- Created: 2026-03-31T17:22:27+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=47582220) 
- npm, bun, pnpm, uv 모두 **패키지 최소 릴리스 기간 설정**을 지원하게 되었음  
  나는 `~/.npmrc`에 `ignore-scripts=true`를 추가해 두었는데, 이 설정만으로도 취약점을 완화할 수 있었음  
  bun과 pnpm은 기본적으로 lifecycle 스크립트를 실행하지 않음  
  각 패키지 매니저별 설정 예시는 다음과 같음:  
  - uv: `exclude-newer = "7 days"`  
  - npm: `min-release-age=7`  
  - pnpm: `minimum-release-age=10080`  
  - bun: `minimumReleaseAge = 604800`  
  흥미롭게도 각자 **시간 단위가 다름**  
  LLM 에이전트를 사용하는 경우, 이 설정으로 인해 실패가 발생할 수 있으므로 `AGENTS.md`나 `CLAUDE.md`에 관련 지침을 추가해야 함
  - “시간 단위가 다 다르다니”라는 말에 “**첫날 자바스크립트 써보는 사람인가?**”라는 농담이 달림
  - pnpm이 이 기능을 **가장 먼저 도입**했음. npm은 11.10.0 이상에서만 지원하며, [2026년 2월 11일 릴리스](https://github.com/npm/cli/releases/tag/v11.10.0)부터 가능함
  - 설정 파일에서 단위를 명시하는 게 좋다는 의견도 있었음. 예를 들어 `timeout` 대신 `timeoutMinutes`처럼
  - npm은 **주석을 지원하지 않을 수도 있음**. `min-release-age=7 # days`가 실제로 적용되지 않을 가능성이 있음
  - yarn berry의 경우 `~/.yarnrc.yml`에 `npmMinimalAgeGate: "3d"`로 설정 가능함

- Axios가 **공급망 공격**에 노출되었다는 소식에 충격을 받았음  
  Axios 내부에는 악성 코드가 없었지만, `plain-crypto-js@4.2.1`이라는 가짜 의존성을 주입해 **RAT(원격 접근 트로이목마)** 를 설치하는 postinstall 스크립트를 실행했음  
  pnpm이나 bun처럼 postinstall 스크립트를 수동 승인해야 하는 사용자에게는 다행인 소식임
  - Node.js에 **fetch가 기본 포함된 것은 v18 이후**이며, 안정화는 v21부터였음. Axios는 훨씬 오래전부터 존재했고, 많은 프레임워크와 튜토리얼에 포함되어 있어 여전히 널리 사용됨
  - “pnpm/bun 사용자는 안전하다”는 말에, “그럼 예전 버전에서는 승인했을 가능성이 높지 않나?”라는 의문이 제기됨
  - pnpm이 **하위 의존성의 postinstall도 차단하는지** 궁금하다는 질문이 나옴

- 패키지 매니저는 **실패한 실험**이라는 주장  
  SQLite처럼 단일 `.c` 파일로 구성된 고품질 C 라이브러리들이 있는데, 이런 방식이면 **transitive dependency** 문제를 피할 수 있음  
  대부분의 공격 표면은 이런 간접 의존성에서 발생함  
  - 패키지 매니저는 이제 언어 채택의 필수 요소가 되었음. 문제는 **품질 관리 부재와 보상 구조**임  
    OpenSSL조차 코드 품질 문제로 **재작성 중**이며, JS는 표준 라이브러리 확장이 어려워 **폴리필 난립**이 심함  
  - “조금 더 노력해야 하는 해결책은 커뮤니티가 받아들이지 않을 것”이라는 의견도 있었음  
    대신 npm 같은 저장소에서 **품질 기준을 강화하고 책임 있는 유지보수자만 등록**하도록 해야 한다는 제안이 나옴  
  - 웹 개발에서는 공격 표면이 훨씬 넓음. 수동 복사 방식은 업데이트 추적이 어렵기 때문에 **패키지 매니저의 알림 기능**이 여전히 유용함  
  - NPM만 유독 **공급망 공격이 심각한 생태계**라는 지적도 있었음  
  - Rust는 C보다 안전하고, 대부분의 crate는 **고품질**이라며 C 라이브러리 중심 주장은 과장이라는 반박도 있었음

- “**오늘은 어떤 npm 패키지가 털렸을까?**”라는 자조 섞인 인사로 하루를 시작한다는 농담이 나옴

- Linux 사용자라면 **bwrap**을 써서 npm, pip, cargo, gradle 등 모든 빌드 로직을 **샌드박스화**하길 권장함  
  bwrap은 Docker처럼 격리 환경을 제공하지만 이미지가 필요 없음. Flatpak도 같은 기술 기반임  
  서버 배포 시에는 **컨테이너 하드닝**이 중요하며, CI/CD 환경을 불신 영역으로 다루는 것이 핵심임  
  AI 실행 시에도 같은 샌드박스를 쓰면 좋음
  - 하지만 이 방식은 **postinstall 공격에만 유효**하다는 지적이 있었음. 코드 내부에서 `require`만 해도 실행될 수 있음  
  - Docker 기반의 개인용 샌드박스 [amazing-sandbox](https://github.com/ashishb/amazing-sandbox)를 만든 사람도 있었음  
  - [drop](https://github.com/wrr/drop)이라는 **bwrap보다 상위 수준의 도구**도 추천됨  
  - firejail이 더 **유연한 보안 샌드박스**라는 의견도 있었음  
  - SSH 소켓 포워딩은 개인 키 접근을 허용하므로 **보안 이점이 없고**, 비밀번호 보호 키를 쓰는 게 낫다는 지적도 있었음

- 의존성 문제를 반복해서 보며 **Rust 생태계도 언젠가 비슷한 일을 겪을까 걱정**됨  
  표준 라이브러리를 비대하게 만드는 건 어렵지만, **신뢰할 수 있는 패키지 품질 보장 체계**가 필요함
  - 대형 패키지(Axios 등)는 **MFA 승인자 여러 명이 출판을 승인**해야 한다는 제안이 있었음  
  - 검증된 의존성을 **상업적으로 제공하는 서비스**가 생길 것이라는 예측도 있었음  
  - 또 다른 제안으로는, 의존성의 테스트를 직접 복사해 코드베이스에 포함하고, **자체 코드 리뷰 절차**를 거치는 방식이 있었음  
    AI 덕분에 이런 추가 작업이 가능해졌고, 사실 예전부터 이렇게 했어야 했다는 반성도 있었음

- NPM 공급망 공격 노출을 최소화하기 위한 **핵심 수칙**  
  - Yarn의 **zero-installs 모드** 사용  
  - postinstall 스크립트 비활성화 또는 실행 전 확인  
  - 개발 중 제3자 코드가 실행될 경우 **VM/컨테이너 내에서만 실행**  
  - 패키지 추가 시 **인기도는 플러스, 최근 커밋은 마이너스 요인**으로 보고 코드와 변경 이력을 직접 검토  
  - 의존성 트리 전체를 검증하고, 새로운 개발자가 추가될 때마다 신뢰 여부를 재평가해야 함  
  - lockfile과 `--frozen-lockfile` 옵션만으로도 충분히 보호된다는 의견도 있었음  
  - “나는 그냥 **문제 많은 스택을 피한다**”는 단순하지만 강력한 철학을 가진 사람도 있었음

- “이런 공격을 완전히 피하려면 어떻게 해야 하나?”라는 질문에,  
  **Qubes OS로 전환**해 비밀번호 관리자와 빌드 환경을 완전히 분리하고 싶다는 의견이 나옴  
  - 어떤 팀은 NPM 관련 작업을 **Apple 컨테이너** 안에서만 수행하고, Python과 Rust도 같은 방식으로 옮길 계획임  
    이는 마치 **화학 실험실의 보호장비(PPE)** 처럼, 개발 환경에서도 격리와 보호가 필요하다는 비유를 들었음  
    pip도 이미 virtualenv 외부 설치를 막기 시작했으며, 앞으로는 패키지 매니저가 **샌드박스 외부 실행을 거부하는 옵션**을 제공하길 기대함  
  - 어떤 사람은 아예 **Node.js/npm을 시스템에 설치하지 않는다**고 함. 지금까지 꼭 필요한 경우를 본 적이 없다고 함

- pnpm과 bun은 이제 기본적으로 **postinstall 스크립트를 무시**하지만, npm은 여전히 실행함  
  `~/.npmrc`에 `ignore-scripts=true`를 설정하는 것이 좋음  
  npm은 여전히 **주간 8천만 다운로드**를 기록함

- 이번 사건의 자격 증명 유출은 **LiteLLM 이전 사건에서 비롯된 것**일 가능성이 높다고 추측함  
  Python이나 Node.js 사용이 불안하지만, 사실 **보편적인 문제**라고 느낌  
  - 최소 릴리스 기간 설정이 도움이 되긴 하지만 여전히 **의존성 업데이트가 두렵다**는 의견이 많음  
  - LiteLLM보다 **Trivy 사건**이 근본 원인이라는 주장도 있었음  
  - **루트리스 임시 컨테이너**에서 실행해 피해 범위를 제한하는 방법도 제안됨
