오픈소스 인사이트
home
오픈소스 기술 동향
home

탈취된 npm 패키지에서 PolinRider 로더 재발견

이 글은 Sonatype 공식 블로그의 PolinRider Loader Reappears in Hijacked npm Packages을 에스코어가 공식 파트너로서 한국어로 번역한 콘텐츠입니다.

에스코어 인사이트

이번 사례는 npm 패키지 보안을 설치 스크립트 검사만으로 판단하기 어렵다는 점을 보여줍니다. 공격자는 정상적인 기능을 수행하는 패키지 안에 악성 로더를 숨겼고, 설치할 때가 아닌 모듈을 불러오거나 애플리케이션을 실행할 때 동작하도록 만들었습니다.
따라서 직접·전이 의존성과 잠금 파일로 영향을 받는 버전을 식별하는 작업과, 해당 코드가 실제로 실행됐는지 판단하는 작업을 구분해야 합니다. 내부 npm 미러와 캐시, CI·빌드 기록도 함께 확인하고, 실행 정황이 있다면 프로세스와 네트워크 활동, 접근 가능한 자격 증명까지 조사해야 합니다.
원문 수치 확인 사항: 원문에서는 영향을 받는 릴리스를 12개라고 설명하고 표에도 12개를 열거했지만, 대응 권고의 저장소 보호 항목에는 '8개 버전'이라고 적혀 있습니다. 아래 번역에는 원문 표현을 유지했습니다. 실제 차단 대상은 Sonatype Guide의 최신 조사 정보와 교차 확인하는 편이 적절합니다.

Sonatype 원문 번역

탈취된 npm 패키지에서 PolinRider 로더 재발견

2026년 11월 4일 · 작성: Sonatype Research Team

핵심 요약

•
Sonatype Research Labs는 탈취된 npm 패키지 10개의 릴리스 12개에서 숨겨진 런타임 로더를 발견했습니다.
•
12개 릴리스의 로더는 과거 PolinRider 활동에서 보고된 것과 동일한 하드코딩된 지갑이 보낸 이더리움 트랜잭션에서 페이로드 서버의 IP 주소를 복원하고, JavaScript를 가져와 XOR 디코딩한 뒤 실행하도록 설계돼 있습니다. 6개는 eval(atob(...)) 안에 로더를 숨겼고, 나머지 6개는 문자 순열 방식의 부트스트랩과 JavaScript Function 생성자를 사용합니다.
•
패키지 매니페스트에는 사용자 측 install 또는 postinstall 훅이 없습니다. 영향을 받은 진입점이 로드되면 코드가 실행될 수 있으며, server-hemera-mongo에서는 번들 애플리케이션이 시작될 때 실행됩니다.
•
직접·전이 의존성, 잠금 파일, 내부 npm 미러, 빌드 기록을 확인해야 합니다. 영향을 받은 코드가 실행됐다면 해당 호스트를 침해된 것으로 간주하고 프로세스, 네트워크 활동, 접근 가능한 자격 증명을 조사해야 합니다.

npm 릴리스 12개에서 숨겨진 로더 발견

2026년 11월 4일, Sonatype Research Labs는 npm 패키지의 악성 런타임 로더를 조사해 탈취된 패키지 10개에서 영향을 받은 릴리스 12개를 확인했습니다. 조사 대상에는 같은 이더리움 발신 지갑과 페이로드 수집 인프라를 사용하는 두 가지 로더 변종이 포함돼 있습니다. Sonatype은 이 발견 사항을 sonatype-2026-010515로 추적하고 있습니다.
영향을 받은 패키지들은 Redux 통합과 상태 확인 API부터 브라우저 활동 추적, 유틸리티, 서버 예제까지 서로 다른 개발 기능을 지원합니다. 이들의 공통점은 본래 정상적인 용도를 수행하는 코드 안에 은닉된 동작이 삽입돼 있다는 것입니다.
이 릴리스들은 사용자 측 설치 스크립트에 의존하지 않고 악성 동작을 활성화합니다. 애플리케이션에서 영향을 받은 패키지를 불러오면 해당 코드가 실행될 수 있습니다. server-hemera-mongo 릴리스에서는 서비스가 시작될 때 실행됩니다.
패키지
영향을 받은 릴리스
확인된 동작
@adminide-stack/client-redux
0.0.16-18
Base64로 은닉된 이더리움 기반 로더
@adminide-stack/health-api-server
0.0.18-alpha.465
Base64로 은닉된 이더리움 기반 로더
@adminide-stack/user-activity-browser
1.1.3-alpha.114
Base64로 은닉된 이더리움 기반 로더
@adminide-stack/utils
0.0.16-129, 0.0.16-130
0.0.16-129에는 Base64로 은닉된 로더, 0.0.16-130에는 문자 순열 방식의 부트스트랩
server-hemera-mongo
0.0.15, 0.0.16
Base64로 은닉된 이더리움 기반 로더
cdeops
13.5.31
문자 순열 방식의 부트스트랩으로 은닉된 이더리움 기반 로더
@adminide-stack/forms-core
5.1.9-alpha.3
문자 순열 방식의 부트스트랩으로 은닉된 이더리움 기반 로더
@adminide-stack/yantra-help-browser
12.0.194
문자 순열 방식의 부트스트랩으로 은닉된 이더리움 기반 로더
@cdmbase/wiki-browser
12.0.18-alpha.53
문자 순열 방식의 부트스트랩으로 은닉된 이더리움 기반 로더
@pubngo-stack/help-browser
12.0.23-alpha.3
문자 순열 방식의 부트스트랩으로 은닉된 이더리움 기반 로더

공통 로더는 이더리움 트랜잭션에서 원격 코드의 위치를 찾습니다

12개 릴리스 모두 공개 이더리움 RPC 엔드포인트를 조회하고 필요하면 Blockscout API를 대신 사용해 하드코딩된 지갑 0x1eef0849eda15d87f64e51c73c8442cf3c8bcef3에서 보낸 트랜잭션을 찾도록 설계된 로더를 포함합니다. 로더는 트랜잭션 수신자 주소의 처음 4바이트를 기본 IPv4 주소로, 다음 4바이트를 보조 주소로 해석합니다. 이어서 TCP 443 포트의 일반 HTTP 연결을 이용해 기본 주소의 /0x/cls 및 /0x/ls 경로에서 XOR로 인코딩된 JavaScript를 가져옵니다.
응답을 XOR 디코딩한 후에는 /0x/cls에서 가져온 JavaScript를 애플리케이션 프로세스의 eval과, 표준 입력이 파이프로 연결된 분리된 node -i 프로세스에 전달합니다. /0x/ls에서 가져온 JavaScript는 분리된 node -i 프로세스에만 전달합니다. 이 자식 프로세스들은 참조가 해제돼 애플리케이션 프로세스와 독립적으로 계속 실행될 수 있습니다.
이더리움 지갑은 일종의 데드 드롭 주소 확인 수단으로 작동합니다. 공격자는 악성 패키지를 다시 업데이트하지 않고도 다른 트랜잭션을 보내 새로운 페이로드 서버의 IP 주소를 알릴 수 있습니다. 따라서 개별 서버를 차단해도 로더가 대체 서버를 찾아내는 기능까지 막을 수는 없습니다.

릴리스 6개는 다른 부트스트랩 방식으로 이더리움 로더를 숨깁니다

6개 릴리스는 문자 순열 방식의 부트스트랩을 사용해 이더리움 기반 로더를 복원하고 실행합니다. 해당 릴리스는 @adminide-stack/utils@0.0.16-130, cdeops@13.5.31, @adminide-stack/forms-core@5.1.9-alpha.3, @adminide-stack/yantra-help-browser@12.0.194, @cdmbase/wiki-browser@12.0.18-alpha.53, @pubngo-stack/help-browser@12.0.23-alpha.3입니다. 이 6개 릴리스에서 복원된 로더 본문은 바이트 단위로 완전히 일치합니다. 다른 6개 Base64 래핑 릴리스의 로더와는 다르지만, 같은 이더리움 지갑과 수집 경로, XOR 키를 사용합니다.
•
utils@0.0.16-130: 난독화된 루틴이 패키지의 로거 코드 뒤에 배치돼 있습니다. 이 루틴은 내장된 데이터 블롭의 문자 순서를 바꾸고 디코딩한 다음, JavaScript Function 생성자를 간접적으로 가져와 복원된 내용을 실행합니다.
•
cdeops@13.5.31: 난독화된 부트스트랩이 client.js의 일반적인 global.require('cdeops/client') 위임 코드보다 먼저 등장합니다. 내장된 내용을 디코딩해 동적으로 실행한 뒤 예정된 클라이언트 위임을 계속합니다. 이 숨겨진 실행은 해당 연결용 코드(shim)의 문서화된 목적과 별개의 동작입니다.

공통 지갑과 C2 인프라가 이 릴리스들을 PolinRider와 연결합니다

Sonatype Research Labs는 12개 릴리스의 로더를 각각 독립적으로 디코딩해 동일한 하드코딩된 이더리움 발신 지갑 0x1eef0849eda15d87f64e51c73c8442cf3c8bcef3을 확인했습니다. 이 지갑은 GitGuardian이 2026년 10월 27일 발표한 「GhostAction과 PolinRider의 연관성: 10월 공격 확대 과정 분석」에도 등장합니다. 이 보고서에서는 침해된 GitHub 저장소에서 발견된 PolinRider 로더가 해당 지갑을 이더리움 주소 확인 수단으로 사용했다고 설명합니다. 이전 로더 역시 443 포트의 /0x/cls와 /0x/ls 경로에서 다음 단계 페이로드를 가져왔으며, 이번 npm 샘플의 수집 경로 및 포트와 일치합니다.
페이로드 서버 인프라에서도 연결점이 발견됐습니다. Greenflagged는 이번 npm 릴리스 분석에서 지갑 트랜잭션을 디코딩해 11월 3일 관찰 당시 지정된 C2 주소가 95.135.181.151이었다고 보고했습니다. GitGuardian도 앞서 PolinRider 지속성 유지 스크립트의 예비 C2 주소로 같은 IP의 443 포트를 명시했습니다. 정확히 일치하는 지갑, 겹치는 서버 인프라, 같은 단계별 페이로드 수집 경로는 이번 악성 npm 릴리스가 이전에 문서화된 PolinRider 활동과 연결된다는 판단을 뒷받침합니다. 이러한 연결은 난독화 기법의 유사성만으로 판단한 것이 아닙니다. 그렇지만 PolinRider와 GhostAction이 같은 운영자에 의해 수행됐다는 사실이 확정된 것은 아닙니다.

악성 코드는 설치 훅을 기다리지 않습니다

패키지 매니페스트에는 사용자 측 install이나 postinstall 훅이 없습니다. 런타임이 악성 JavaScript가 삽입된 파일에 도달할 때 실행됩니다. 예를 들어 require('cdeops/client')는 영향을 받은 client.js 파일을 선택하지만, 해당 패키지의 ES 모듈 import는 별도의 client.mjs 파일에 연결됩니다. server-hemera-mongo에서는 시작 스크립트가 참조하는 애플리케이션 번들 dist/main.js에 악성 코드가 삽입돼 있습니다.
노출 여부를 평가할 때는 의존성 기록과 실제 실행 증거를 대조해야 합니다. 잠금 파일은 해석된 의존성 버전을 알려주고, 캐시의 아티팩트와 다운로드 로그는 패키지가 유입된 경로를 추적하는 데 도움이 됩니다. 빌드 명령, 애플리케이션 설정, 프로세스 원격 측정 정보 및 런타임 로그는 삽입된 JavaScript 파일이 실행됐는지 판단하는 근거가 됩니다.
npm 설치 스크립트를 비활성화해도 모듈을 로드하거나 애플리케이션을 시작할 때 실행되는 코드는 막지 못합니다. 중요한 것은 개발 환경, CI 작업자, 빌드 호스트 또는 배포된 서비스에서 해당 패키지 코드가 실제로 실행됐는지입니다.

지금 조직이 취해야 할 조치

영향을 받은 시스템을 파악하고 추가 노출을 막으려면 다음 조치를 수행해야 합니다.
•
영향을 받은 버전 확인: 직접·전이 의존성과 package-lock.json, yarn.lock, pnpm-lock.yaml, 내부 npm 미러, 캐시, 빌드 기록 및 아티팩트 메타데이터를 확인합니다.
•
저장소 보호 기능 평가: 저장소 관리자 또는 의존성 방화벽이 릴리스가 개발자나 빌드 시스템에 전달되기 전에 차단했는지 확인합니다. 다운로드 및 정책 로그를 검토해 실제 전달 여부를 파악하고, 원문에 명시된 8개 버전이 앞으로 사용되지 않도록 차단합니다.
•
영향을 받은 의존성 제거: 프로젝트에서 영향을 받은 버전을 교체하고 내부 미러와 캐시가 해당 버전을 더 이상 제공하지 않도록 합니다. 조사에 필요한 사본은 보존하고, 독립적으로 안전성을 검증한 대체 버전을 사용합니다.
•
실제 코드 실행 여부 판단: 애플리케이션에서 어떤 진입점을 불러왔는지, 런타임이 삽입된 코드를 실행할 수 있었는지 확인합니다. 프로세스·네트워크 활동과도 대조합니다. 잠금 파일에 해당 버전이 있다는 사실만으로 악성 코드가 실행됐다고 단정할 수 없으며, import 오류가 발생했다고 해서 실행되지 않았다고 단정할 수도 없습니다. 삽입된 코드가 실행된 뒤 import가 실패했을 가능성도 있습니다.
•
영향을 받은 호스트 조사: 코드가 실행됐다면 호스트가 침해된 것으로 간주합니다. 다른 Node 프로세스가 표준 입력을 파이프로 연결해 실행한 분리된 node -i 프로세스가 있는지, TCP 443 포트의 /0x/cls 또는 /0x/ls 경로로 일반 HTTP 요청이 발생했는지 확인합니다. 페이로드 요청의 Sec-V 헤더에는 Base64로 감싼 6개 릴리스에서 A10-npm_inject, utils@0.0.16-130에서는 A10-II, cdeops@13.5.31에서는 A10-E 값이 포함됩니다. 이러한 지표를 프로세스 계보, 타임스탬프 및 의존성 기록과 함께 사용해 영향받은 시스템을 식별합니다.
•
접근 권한 평가 및 자격 증명 교체: 영향을 받은 프로세스가 접근할 수 있었던 자격 증명과 시스템을 확인합니다. 노출 가능성이 있는 패키지 게시 자격 증명, 소스 저장소 접근 권한, 클라우드 키 및 CI 비밀정보를 교체합니다.
공격자는 npm 릴리스를 다시 게시하지 않아도 원격에서 제공하는 JavaScript를 변경할 수 있습니다. 확보 가능한 프로세스, 네트워크, 빌드 및 런타임 기록을 분석해 영향을 받은 시스템에서 실제로 실행된 내용과 해당 프로세스가 접근할 수 있었던 자격 증명 및 리소스를 확인해야 합니다.
지속적인 조사 내용과 대응 지침은 Sonatype Security Event와 Sonatype Guide 연구 자료를 참고하시기 바랍니다.
영향을 받은 npm 릴리스와 PolinRider 활동을 분석한 Sonatype Research Labs의 Adam Reynolds에게 감사드립니다. LinkedIn에서 Adam Reynolds와 연결하기.
에스코어는 Sonatype 공식 파트너로서 소프트웨어 공급망 보안 및 오픈소스 거버넌스 구축을 지원합니다. 도입이나 구축이 필요하시면 언제든 문의해 주세요.
지원 솔루션: Nexus Repository · Repository Firewall · Lifecycle · Sonatype Guide