보안 사고 · npm 패키지 침해

Nx npm 침해: 개발자의 Mac에서 한 일과 Kilobat이 표시했을 내용

2025년 8월 26일과 27일 약 5시간 동안 npm의 Nx 빌드 시스템 여덟 버전에는 개발자의 컴퓨터에서 비밀 정보를 검색하는 postinstall 스크립트가 포함되어 있었습니다. 이 스크립트는 발견한 AI 코딩 CLI의 도움을 받아 수집한 정보를 피해자의 GitHub 계정에 새로 만든 공개 저장소에 게시했습니다.

발생 시기: 2025년 8월 26~27일출처 확인: 2026년 11월 1일

요약

  • 보호: 도움이 되지 않음

    차단할 대상이 없습니다. 코드는 registry.npmjs.org에서 왔고 데이터는 api.github.com으로 전송되었으며 어떤 위협 목록에도 두 호스트가 없습니다.

  • 에이전트 보호: 도움이 되지 않음

    붙여넣기 사이트, 터널 또는 IP 주소는 사용되지 않았으며 업로드 차단 기능은 설계상 api.github.com 같은 잘 알려진 호스트를 차단하지 않습니다.

  • 에이전트 네트워크 로그: 제한적

    GitHub API에 연결한 설치 스크립트와 스크립트가 시작한 AI CLI 실행을 기록했을 것입니다. 다만 이 Mac에서 이루어진 설치만 기록하며 차단하지는 않습니다.

발생한 일

공격자는 프로젝트 자체 CI를 통해 Nx의 npm 게시 토큰을 얻었습니다. 유지관리자의 보안 권고에 따르면 8월 21일 병합된 GitHub Actions 워크플로가 pull_request_target 트리거를 사용하고 풀 리퀘스트 제목을 셸에 출력했기 때문에 조작된 제목으로 저장소 권한을 가진 명령어를 실행할 수 있었습니다. 8월 24일 이 경로를 이용해 악성 커밋에서 게시 워크플로를 시작하고 npm 토큰을 웹훅으로 보냈습니다.

공격자는 토큰으로 nx 20.9.0~20.12.0 및 21.5.0~21.8.0과 @nx/devkit, @nx/js, @nx/workspace, @nx/node, @nx/eslint, @nx/key, @nx/enterprise-cloud의 여러 버전을 게시했습니다. 각각에는 telemetry.js를 실행하는 postinstall 훅이 포함되어 있었으며 이 코드는 macOS와 Linux에서만 동작했습니다.

스크립트는 claude, gemini, q 명령줄 도구를 찾아 설치된 경우 권한 확인을 건너뛰는 플래그(--dangerously-skip-permissions, --yolo, --trust-all-tools)와 함께 실행했습니다. 프롬프트는 지갑, 키 및 .env 파일처럼 보이는 파일을 /tmp/inventory.txt에 나열하도록 요청했습니다. Snyk와 StepSecurity가 코드를 공개했습니다. Wiz는 수백 건에서 이 방식이 성공했으며 일부는 AI 제공업체의 안전장치에 의해 거부되었다고 관찰했습니다.

이어 해당 파일을 읽고 gh auth token로 GitHub CLI 토큰과 ~/.npmrc에서 npm 토큰을 수집한 뒤 피해자 계정에 s1ngularity-repository라는 공개 저장소를 만들어 모든 것을 세 번 base64로 인코딩한 results.b64로 업로드했습니다. 또한 ~/.zshrc와 ~/.bashrc에 sudo shutdown -h 0를 추가해 새 터미널을 열 때마다 암호를 묻고 컴퓨터를 종료하게 했습니다.

많은 설치는 누군가 직접 입력한 것이 아니었습니다. 유지관리자는 Nx Console 편집기 확장 프로그램 버전 18.6.30~18.65.1이 시작할 때 최신 nx를 확인한다는 사실을 발견했습니다. 따라서 해당 시간에 편집기를 열기만 해도 충분했습니다. Wiz는 CI 파이프라인에서도 악성코드가 실행되는 것을 발견했습니다. Wiz는 유출된 데이터에서 유효한 GitHub 토큰을 1,000개 이상 확인했으며 두 번째 단계에서는 탈취한 토큰으로 5,500개가 넘는 비공개 저장소를 공개했습니다.

타임라인

  1. 2025년 8월 21일

    셸 인젝션 결함이 있는 워크플로가 Nx 저장소에 병합됩니다(유지관리자 보안 권고).

  2. 2025년 8월 24일

    조작된 풀 리퀘스트가 악성 커밋에서 게시 워크플로를 시작하고 npm 토큰을 웹훅으로 보냅니다.

  3. 8월 26일 22:32 UTC

    nx 21.5.0 및 여러 @nx/* 패키지가 telemetry.js와 함께 게시되고, 7분 후 20.9.0이 뒤따릅니다.

  4. 8월 27일 00:30 UTC

    사용자가 악성 버전에 대한 GitHub 이슈를 엽니다. 마지막 두 버전 21.8.0과 20.12.0은 00:37에 나타납니다.

  5. 8월 27일 02:44 UTC

    npm이 영향을 받은 버전과 게시 토큰을 제거합니다.

  6. 8월 27일 약 09:00 UTC

    GitHub가 공격자가 만든 저장소를 비활성화합니다. 첫 저장소가 나타난 지 약 8시간 뒤입니다(Wiz).

  7. 2025년 8월 27일

    Nx Console 18.66.0이 최신 nx 설치를 중단합니다. Nx는 게시 방식을 2FA를 사용하는 npm Trusted Publishers로 옮깁니다.

  8. 2025년 8월 28~29일

    탈취된 토큰으로 비공개 저장소의 이름을 바꾸고 공개합니다. Wiz는 400개 이상의 계정과 5,500개의 저장소를 집계했습니다.

네트워크에서 보인 모습

Mac에서 이 공격은 개발자의 컴퓨터가 매일 연결하는 두 호스트를 사용했습니다. 패키지는 다른 패키지처럼 registry.npmjs.org에서 왔습니다. 탈취 데이터는 피해자의 GitHub CLI 토큰을 사용해 피해자 자격으로 api.github.com에 전송되었습니다. 저장소를 만드는 POST /user/repos 다음에 인코딩된 데이터를 담은 PUT /repos/<owner>/s1ngularity-repository/contents/results.b64가 이어졌습니다. 명령 및 제어 서버나 다른 업로드 목적지는 없었습니다.

프로세스 트리는 짧았습니다. npm(또는 편집기 확장 프로그램)이 셸을 실행하고 셸은 node telemetry.js를 실행했으며 node가 자식 프로세스로 claude, gemini 또는 q를 시작했습니다. 각 AI CLI는 평소처럼 자체 제공업체와 통신했고 파일 읽기는 로컬에서 이루어졌습니다. 업로드 자체는 node에서 이루어졌으며 크기는 파일의 내용에 전적으로 좌우되었습니다.

침해 지표

출처에 기록된 그대로입니다. 도메인은 링크가 아닌 텍스트이며 출처가 무해화한 경우 그대로 무해화했습니다([.]). 이 인프라는 대부분 오래전에 사라졌습니다. 지표는 현재 캠페인을 차단하기보다 오래된 로그, 파일 및 계정을 확인하는 데 도움이 됩니다.

지표유형표시 위치참고
api.github.com호스트(정상)내 Macnpm 설치 중 node가 만든 POST /user/repos 다음 PUT …/contents/results.b64.
s1ngularity-repository, s1ngularity-repository-0, s1ngularity-repository-1GitHub 저장소 이름GitHub 계정생성 시 공개 상태였으며 GitHub가 8월 27일 비활성화했습니다. 두 번째 단계에서 이름이 변경된 저장소에는 s1ngularity-repository- 뒤에 다섯 글자가 사용되었습니다.
results.b64파일GitHub 저장소수집된 데이터로, base64로 두세 번 인코딩되었습니다.
/tmp/inventory.txt, /tmp/inventory.txt.bak파일내 MacAI CLI가 만든 파일 경로 목록.
sudo shutdown -h 0~/.zshrc, ~/.bashrc의 줄내 Mac터미널을 열고 암호를 입력하면 Mac을 종료합니다.
nx 20.9.0–20.12.0, 21.5.0–21.8.0npm 버전잠금 파일, npm 캐시@nx/devkit, @nx/js, @nx/workspace, @nx/node 20.9.0 및 21.5.0, @nx/eslint 21.5.0, @nx/key 및 @nx/enterprise-cloud 3.2.0도 해당합니다.

Kilobat이 표시하거나 차단했을 내용

  • 에이전트 네트워크 로그. Claude Code가 npm install를 실행하는 것처럼 에이전트가 설치를 수행한 경우, 세션에는 설치 중 npm 아래의 node가 api.github.com로 전송하는 모습이 기록됩니다. 설치 스크립트가 GitHub API와 통신한다면 살펴볼 만합니다. api.github.com은 잘 알려진 호스트이므로 처음으로 표시되지 않으며 10 MB를 초과한 경우에만 대용량 업로드로 표시됩니다.
  • 에이전트 네트워크 로그. Kilobat은 Claude Code와 Gemini CLI를 시작한 주체가 아니라 실행 파일로 인식합니다. 따라서 스크립트가 시작한 실행은 사용자가 에이전트를 시작하지 않은 시점에 프로젝트 폴더에서 별도 세션으로 나타납니다. Amazon의 q는 Kilobat이 인식하는 에이전트에 속하지 않습니다.
  • 보호. 차단할 것이 없습니다. registry.npmjs.org와 api.github.com은 위협이 아니며 Kilobat이 차단해서는 안 됩니다.
  • 에이전트 가드. 역시 감지하지 못합니다. 가드의 규칙은 붙여넣기 사이트, 파일 공유, 터널, 요청 수집기 및 원시 IP 주소를 다루지만 이 중 어느 것도 나타나지 않았습니다. 또한 업로드 차단은 잘 알려진 호스트를 제외하므로 git push가 계속 작동합니다.

Kilobat이 도움이 되지 않았을 경우

  • 피해자 자신의 토큰으로 github.com에 데이터를 유출하는 행위는 정상적인 GitHub CLI 작업과 같아 보입니다. Kilobat은 호스트와 바이트 수만 확인하며 전송된 내용은 확인하지 않습니다.
  • npm 토큰은 GitHub 러너에서 실행된 Nx의 GitHub Actions에서 탈취되었고, 두 번째 단계는 공격자 측에서 탈취한 토큰으로 실행되었습니다. 어느 쪽도 Mac에 접촉하지 않았습니다.
  • VS Code의 편집기 확장 프로그램이 시작한 설치는 에이전트에 귀속되지 않아 에이전트 세션이 생성되지 않습니다. Cursor와 Windsurf에서는 해당 앱의 작업으로 집계됩니다.
  • 파일 읽기, AI CLI의 파일 검색 실행 및 ~/.zshrc 편집은 네트워크 이벤트가 아닙니다.
  • 에이전트 네트워크 로그는 30일 동안 보관되므로 2025년 8월에 이 코드가 Mac에서 실행되었는지 지금 확인할 수 없습니다.

지금 Mac 확인하기

  1. 각 프로젝트에서 영향을 받은 Nx 버전이 설치되어 있는지 확인하고 설치되어 있다면 업데이트하세요.

    npm ls nx
  2. 스크립트가 남긴 파일을 찾으세요. macOS는 재시작할 때 /tmp를 지우므로 파일이 없다고 해서 많은 것을 증명하지는 못합니다.

    ls -la /tmp/inventory.txt /tmp/inventory.txt.bak
  3. 셸 시작 파일에서 종료 명령줄을 찾고 있다면 삭제하세요.

    grep -n "shutdown" ~/.zshrc ~/.bashrc
  4. GitHub 보안 로그에서 이름에 s1ngularity-repository가 포함된 repo.create 이벤트를 찾으세요: github.com/settings/security-log. 저장소 자체는 이미 비활성화되었을 수 있습니다.

  5. 영향을 받았다면 Settings › Applications › Authorized OAuth Apps에서 GitHub CLI의 접근 권한을 취소하고 gh auth login로 다시 로그인한 다음 npm 토큰과 .env 파일 및 환경의 모든 비밀값을 교체하세요.

  6. 오래된 사본이 다시 설치되지 않도록 npm 캐시를 지우세요.

    npm cache clean --force
  7. 다음에 대비해 Kilobat의 에이전트 네트워크 로그를 기록하면서 에이전트가 실행한 각 설치가 어디에 연결했는지 확인하세요. npm 또는 node가 api.github.com과 통신하는 경우와 직접 시작하지 않은 에이전트 세션을 주의해서 보세요.

    kilobat agent-log --all

질문

AI 도구가 사용자를 공격했나요?

아닙니다. 악성 스크립트가 권한 확인을 끄는 플래그와 자체 지시를 사용해 도구를 직접 시작했습니다. 이 플래그가 있다는 점은 에이전트를 전체 권한으로 무인 실행할 때 주의해야 하는 이유이기도 합니다.

Kilobat은 왜 GitHub로의 대용량 업로드를 차단하지 않나요?

코드, 릴리스 및 패키지를 GitHub에 푸시하는 일은 일반적인 작업이며 이를 차단하면 git push가 작동하지 않기 때문입니다. 대신 로그에 업로드의 바이트 수와 프로세스가 표시됩니다.

출처

2026년 11월 1일에 확인했습니다.

에이전트와 앱이 어디에 연결하는지 확인하세요

Kilobat은 모든 연결 뒤에 있는 앱, 프로세스 및 에이전트의 이름을 표시하고 알려진 악성 호스트를 차단하며 각 에이전트 세션이 연결한 대상을 기록하는 Mac 메뉴 막대 앱입니다.

Mac용 다운로드 ↓
brew install seifip/kilobat/kilobat