TL; DR
무리 npm 패키지 5개두 개의 계정으로 게시되었으며, 배송되었습니다. postinstall 호스트에서 클라우드 자격 증명을 읽어 외부로 전송하는 후크입니다. 패키지에는 가짜 이름이나 불법 복제된 이름이 포함됩니다. coral-wraith, ecto-corsair-whisper-6f3b9, ecto-corsair-flag-x9m4, ecto-rust-read-f3a9c1, ecto-nightly-spirit — 그리고 그들이 수집한 모든 것을 가짜로 직렬화합니다. ecto_module: 전송하기 전에 YAML 매니페스트를 확인합니다. 클러스터는 다음과 같이 추적합니다. 외부 원형질.
해당 페이로드는 특정 환경, 즉 이름이 특정 호스트인 경우에만 실행됩니다. 12자리 16진수 문자열 그리고 아래에 작업 디렉토리가 있습니다. /app/node_modules — 컨테이너화된 빌드 또는 CI 워커의 형태입니다. 해당 단계를 통과하면 후크가 쿼리합니다. AWS 인스턴스 메타데이터 서비스(IMDSv2) IAM 역할 자격 증명의 경우 열거형을 사용합니다. AWS 비밀 관리자 세 지역에 걸쳐 환경 변수를 덤프하고, 아래 파일들을 읽습니다. /app그리고 캡처 더 플래그(Capture-the-Flag) 문자열을 스크랩합니다. 그런 다음 두 가지 방식으로 결과를 유출합니다. 하나는 비콘을 통해, 다른 하나는... webhook.site 수집기와 매니페스트 PUT을 사용하여 raw-IP 엔드포인트로 데이터를 전송하고, localhost 우선 폴백 목록을 지정합니다.
이후 패키지 설명에는 "verdaccio 공급망 테스트용 CTF 페이로드"라고 적혀 있습니다. 저희는 이러한 자체 설명을 관찰 가능한 사실로 보고합니다. 실제 동작, 즉 공용 IP로의 실시간 유출, 실제 IMDS 자격 증명 읽기, 실제 Secrets Manager 호출은 명칭과 관계없이 동일하며, 이러한 이유로 해당 버전들은 악성으로 분류되었습니다.
그 집단에 속한 이름 중 하나, coral-wraith단일 릴리스에 그치지 않고, 불과 몇 시간 만에 수십 가지 버전으로 빠르게 재출시되었다. 1.0.0 등반하다 6.0.0 — 그리고 같은 이름의 이전 작품에서는 부풀려진이라는 단어를 사용했습니다. 9999.0.x 버전 번호, 고전적인 형태 의존성 혼동 시도그 과정에서 페이로드는 눈에 띄게 성숙해졌습니다. 일회성 호스트 열거 비콘에서 의도한 대상 외부에서는 눈에 띄지 않도록 환경 검사로 보호되는 완전한 AWS 자격 증명 피벗으로 발전했습니다.
| 패키지 | 5개의 이름; coral-wraith 단독으로 수십 가지 버전으로 재출간됨 |
| 생태계 | npm |
| 벡터를 설치하세요 | postinstall 라이프사이클 스크립트 |
| 주요 목표 | AWS IAM 역할 자격 증명 + Secrets Manager 비밀 값, 환경 변수 /app 파일 |
| 엑스필 | webhook.site 비콘 + raw-IP C2 PUT |
| 트리거 게이트 | 12진수 호스트 이름 + /app/node_modules 현재 작업 디렉터리(cwd)와 해당 컨텍스트 외부에서는 페이로드를 억제하는 환경 검사가 추가됩니다. |
| 심각도 | 높은 — 클라우드 자격 증명 및 관리형 비밀 키 공개 컨테이너화된 빌드 및 런타임 환경 |
공격 해부학
클러스터의 모든 패키지는 거의 비어 있는 상태로 동일한 방식으로 빌드됩니다. index.js (모듈.exports = {}), 한 줄 package.json 스크립트 — “postinstall”: “node postinstall.js” — 그리고 탑재체는 postinstall.js패키지를 설치하는 것만으로 훅을 실행할 수 있으며, 임포트나 호출은 필요하지 않습니다.
타겟팅 게이트. 어떤 작업을 수행하기 전에, 엑토 패밀리 페이로드는 주변 환경을 점검합니다.
function isAppWorker(): host = os.hostname() if host does NOT match /^[0-9a-f]{12}$/ -> exit if cwd does NOT contain "/app/node_modules" -> exit if cwd contains "/tmp/npm-safe" -> exit otherwise -> proceed Docker는 기본적으로 컨테이너에 12진수 호스트 이름을 할당합니다. /app/node_modules 이는 일반적인 컨테이너 내 설치 경로입니다. 세 번째 절은 경로가 샌드박스 압축 해제 디렉터리처럼 보이면 실행을 중단합니다. 결과적으로 페이로드는 개발자 노트북이나 분석 샌드박스에서는 비활성 상태로 유지되다가 컨테이너화된 빌드 또는 런타임 워커 환경, 즉 실제 클라우드 자격 증명을 보유할 가능성이 가장 높은 환경에서만 활성화됩니다. 클러스터에서 가장 먼저 실행되는 패키지는 다음과 같습니다. 산호 유령는 그러한 게이트가 없으며 (더 간단한) 컬렉션을 무조건 실행합니다.
수집문이 지나가면 갈고리가 밖으로 튀어나온다. execFileSync("/bin/sh", ["-c", …]) 그리고 다음과 같은 단일 복합 명령을 순서대로 실행합니다.
1. PUT /latest/api/token to 169.254.169.254 (IMDSv2 token request) 2. GET .../iam/security-credentials/ (IAM role name) 3. GET .../iam/security-credentials/<role> (temporary credentials) 4. dump env | sort (environment variables) 5. list /app (excl. node_modules) + cat first 15 (application files) 6. aws secretsmanager list-secrets (us-east-1, eu-west-1, eu-central-1) 7. scrape readable files for HTB{...} (capture-the-flag strings) 1~3단계는 IMDSv2 검색의 전형적인 절차입니다. 세션 토큰을 요청한 다음, 이를 첨부합니다. X-aws-ec2-메타데이터-토큰 인스턴스의 IAM 역할과 해당 역할의 임시 액세스 키를 가져오기 위한 헤더입니다. 더 간단한 인증되지 않은 IMDSv1 대신 IMDSv2를 구현하기로 한 선택은 다음과 같습니다. 바로 주목할 만한 점은, 이 페이로드가 토큰 기반 메타데이터 액세스가 필요한 인스턴스에서도 작동한다는 것입니다. 이는 AWS에서 권장하는 보안 강화 방식입니다. 3단계에서 반환되는 자격 증명은 수명이 짧습니다. 액세스키 ID/비밀접근키/Token 트리플은 인스턴스의 역할에 따라 범위가 지정됩니다. 해당 역할이 수행할 수 있는 모든 작업은 해당 키 소유자가 자격 증명 수명 동안 수행할 수 있습니다.
4~6단계는 촬영 범위를 넓힙니다. 환경 덤프는 빌드 또는 런타임 프로세스가 상속한 모든 것을 캡처합니다. 실제로 레지스트리 토큰, 데이터베이스 연결 문자열 및 API 키가 가장 자주 여기에 저장됩니다. / 앱 파일 워크는 외부의 애플리케이션 파일 최대 15개를 읽습니다. node_modules표면 구성을 나타낼 수 있습니다. .env 파일 또는 소스. 6단계에서 호출합니다. aws secretsmanager list-secrets 세 지역에서 1~3단계에서 가져온 자격 증명이 해당 호출을 인증하는 데 정확히 사용되므로 IMDS 읽기와 Secrets Manager 열거 체인이 단일 에스컬레이션(인스턴스 역할 → 관리형 시크릿 인벤토리)으로 통합됩니다. 7단계는 캡처 더 플래그(capture-the-flag) 방식을 참고한 것으로, HTB{…} 플래그가 발견되면 단독으로 전송되고, 그렇지 않으면 수집된 원본 데이터가 네 부분으로 나뉘어 전송됩니다.
클러스터의 후속 버전에서는 권한 상승이 더욱 심화됩니다. 인벤토리에서 멈추지 않고 IMDS 응답을 분석하고 임시 키를 내보냅니다. AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_세션_토큰 환경 변수를 사용하여 신원을 확인합니다. AWS sts get-caller-identity그런 다음 반환된 모든 비밀 값을 순회합니다. 리스트 시크릿 부름 AWS SecretsManager에서 비밀 값 가져오기 각각에 대해 이름뿐만 아니라 비밀 내용까지 검색합니다. 동일한 버전은 프로세스 대체 플래그 바이너리도 읽습니다./readflag (그리고 친구들) 그리고 시도해 보세요 충전 실행 아래에서 발견된 모든 Rust 프로젝트에 대해 / 앱클라우드 자격 증명을 넘어 빌드 환경에서 노출되는 모든 것으로 수집 범위를 넓힙니다.
이러한 최신 버전들은 또한 더욱 공격적으로 접근 제한을 적용합니다. 12진수 호스트 이름 외에도 /app/node_modules 검사 과정에서 페이로드는 활성 패키지 레지스트리 구성과 작업 디렉터리 경로를 검사하고, 실제 대상이 아닌 분석 또는 미러 컨텍스트를 나타내는 경우 아무런 경고 없이 종료됩니다. 이러한 과정을 통해 페이로드는 대부분의 검사 환경에서는 아무런 동작도 수행하지 않으며, 실제 컨테이너화된 호스트라고 판단되는 경우에만 전체 데이터 수집을 실행합니다.
여과수집된 데이터는 두 개의 채널을 통해 호스트에서 전송됩니다. 첫 번째는 비콘입니다. POST 고정된 webhook.site 수집기에는 호스트 이름, 숫자 UID, 작업 디렉터리 및 최대 120KB의 수집된 데이터가 포함됩니다. 둘째, 데이터는 가짜 YAML "모듈 매니페스트"로 접혀 들어갑니다. PUT 에 /api/modules/ 대상 서버에서:
ecto_module: name: "<flag-or-chunk-0>" version: "1.0.0" power_level: "<chunk-1>" ship_deck: "<chunk-2>" cargo_hold: "<chunk-3>" 매니페스트 필드 이름(전력 레벨, 선박 갑판, 화물칸)는 장식일 뿐입니다. 도난당한 데이터는 문자열 값 안에 숨겨져 있기 때문에 네트워크 모니터는 명백한 데이터 덤프가 아닌 무해한 패키지 레지스트리 매니페스트 업로드처럼 보이는 것을 감지합니다. 비콘 채널은 더 많은 것을 전달합니다. POST 몸에 webhook.site 호스트 이름, 숫자 UID, 작업 디렉터리 및 최대 120KB의 수집된 데이터가 포함되므로 단 한 번의 비콘 성공만으로도 전체 데이터를 전송할 수 있습니다. webhook.site 이는 무료 요청 검사 서비스입니다. 이를 수집기로 사용하면 운영자는 해당 채널에 대한 자체 수신 인프라를 구축할 필요가 없으며, 기록된 요청은 서비스 저장소에 영구적으로 보관됩니다.
매니페스트 PUT 여러 개로 시작하는 대체 목록을 탐색합니다. 127.0.0.1/로컬 호스트 포트로 연결된 다음 `에 있는 3개의 공용 주소로 연결됩니다.154.57.164.0/24` 범위를 지정하여 2xx 상태 코드로 응답하는 첫 번째 엔드포인트에서 중지합니다. 로컬 호스트를 우선으로 하는 순서는 "verdaccio testing"의 자체 설명(루프백 인터페이스의 로컬 레지스트리)과 일치하지만, 공용 IP 대체는 루프백 인터페이스가 수신 대기하지 않는 경우, 즉 작성자 자신의 테스트 장비가 아닌 모든 시스템에서 데이터가 호스트를 벗어나게 된다는 것을 의미합니다.
연혁
해당 클러스터는 단일 용량 증가가 아닌 점진적인 기능 성장을 보여줍니다. 우리는 게시된 실제 시간 기준이 아닌 관찰된 동작을 기준으로 순서를 정했습니다.
| 단계 | 패키지/버전 | 행동 |
|---|---|---|
| 초기 실행 | coral-wraith 9999.0.x | 종속성 혼동 시도와 일치하는 부풀려진 버전 번호; 설치 시 열거 및 유출 |
| 씨 | coral-wraith 1.0.0 | postinstall은 id/env/flag 파일을 수집합니다. 단일 PUT 요청으로 전송됩니다. 154[.]57[.]164[.]71:30782마커 ECT-472839 |
| 신속한 반복 | coral-wraith 1.0.1 → 6.0.0 | 몇 시간 만에 수십 건의 릴리스 발생; 페이로드 획득 isAppWorker() 게이트, IMDSv2 자격 증명 가져오기, 전체 get-secret-value 피벗, 레지스트리/경로 환경 검사 및 이중 싱크 마커 |
| 평행 이름 | ecto-corsair-whisper-6f3b9 1.0.14–1.0.18 | 동일한 게이트형 페이로드; webhook.site 비콘 및 다중 엔드포인트 대체 목록 |
| 변종 | ecto-rust-read-f3a9c1 1.0.1–1.0.2 | 싱크대 표시를 추가합니다. ECT-987654, ECT-654321, ECT-839201 |
| 변종 | ecto-corsair-flag-x9m4 1.0.0, ecto-nightly-spirit 1.1.0 | 동일한 게이트형 페이로드, 동일한 C2 및 비콘 |
이 클러스터의 가장 큰 특징은 배포 주기입니다. 하나의 패키지와 하나의 버전이 아니라, 동일한 이름으로 여러 번 빠르게 재게시되며, 각 릴리스는 이전 릴리스에서 약간씩 변형된 형태입니다. 또한 동일한 내용을 담고 있는 다른 이름의 여러 형제 패키지들도 함께 배포됩니다. 속삭임 해당 코드군은 두 개의 유사한 특징을 가진 코드로 나뉘는데, 하나는 두 가지 중요한 탐지를 유발하고 다른 하나는 세 가지(추가 파일 읽기 싱크)를 유발합니다. 하지만 둘 다 동일한 페이로드를 나타냅니다. 차이점은 코드의 변화이지 동작상의 분기가 아닙니다. 속삭임 분석 범위를 벗어난 값(작성 시점 기준 최소 1.0.25까지)은 레지스트리에서 실시간으로 관찰되었으며, 산호 유령 이름은 같은 창을 통해 자체 버전 사다리를 계속해서 올라갔다.
타협의 지표
아래의 모든 지표는 디스크에 저장된 패키지 소스에서 추출되었습니다. 네트워크 지표는 기능이 제한되었습니다.
네트워크
| 지시자 | 직위별 |
|---|---|
hxxp://154[.]57[.]164[.]71:30782 | C2 PUT 타겟(coral-wraith) |
hxxp://154[.]57[.]164[.]80:30543 | C2 PUT 폴백(ecto-*) |
hxxp://154[.]57[.]164[.]82:31250 | C2 PUT 폴백(ecto-*) |
hxxp://154[.]57[.]164[.]71:31289 | C2 PUT 폴백(ecto-*) |
hxxps://webhook[.]site/602a4c72-7033-4e28-92ea-dc66e59206e5 | 비콘 수집기 |
169[.]254[.]169[.]254/latest/... | IMDSv2 자격 증명 읽기(대상 측, AWS 메타데이터) |
행동/파일
| 지시자 | 직위별 |
|---|---|
"postinstall": "node postinstall.js" | 벡터를 설치하세요 |
ecto_module: YAML과 함께 power_level / ship_deck / cargo_hold 키 | Exfil 매니페스트 스키마 |
싱크대 표시 ECT-472839, ECT-987654, ECT-654321, ECT-839201 | C2 경로 세그먼트 /api/modules/<marker> |
isAppWorker() 게이트: 호스트 /^[0-9a-f]{12}$/, cwd에는 다음이 포함됩니다 /app/node_modules | 활성화 조건 |
aws secretsmanager list-secrets 위에 us-east-1, eu-west-1, eu-central-1 | 비밀 열거 |
HTB{...} 정규 표현식 스크래핑 | 깃발 뺏기 수확 |
파일 해시값(sha256, 분석 시점에 캡처됨)
| 입양 부모로서의 귀하의 적합성을 결정하기 위해 미국 이민국에 | sha256 |
|---|---|
coral-wraith/postinstall.js | ce5ff035cfdfed1d0015446424b352c27b66bcb77e9fdb0a51e4245199146824 |
ecto-corsair-whisper-6f3b9 1.0.18/postinstall.js | b58432acba376aa6976f0490d9a1c04257ccdbc856d8390260c50322d63e31c3 |
귀인 및 관찰된 행동
다섯 개의 패키지 이름은 두 개의 npm 계정으로 게시되었지만, 동일한 인프라를 공유하므로 하나의 클러스터로 취급할 수 있습니다. 엑토 모듈 명시적 스키마, 동일 ECT-472839 주요 싱크 마커, 동일 webhook.site 수집기 ID 및 C2 엔드포인트는 동일합니다. 154.57.164.0/24` 블록. 시드 패키지(`coral-wraith`)(더 간단하고 제한이 없는) 반면, 제한이 있는 엑토 패밀리는 독립적인 노력보다는 하나의 툴킷에 대한 반복으로 해석됩니다.
최신 버전에서 해당 패키지들은 스스로를 다음과 같이 설명합니다. "verdaccio 공급망 테스트를 위한 CTF 페이로드." 우리는 해당 레이블을 관찰 가능한 사실로 제시할 뿐, 목적에 대한 결론으로 다시 언급하지 않습니다. 코드의 동작은 레이블링 방식과 관계없이 명확합니다. 즉, 인스턴스 메타데이터 서비스에서 IAM 역할 자격 증명을 읽고, 세 개의 AWS 리전에 걸쳐 관리되는 비밀 키를 열거한 다음, 결과를 공용 IP와 제3자 웹훅 수집기로 전송합니다. 진정한 루프백 전용 테스트 환경에서는 공용 IP 대체 목록, IMDS 자격 증명 읽기 또는 리전 간 Secrets Manager 호출이 필요하지 않습니다. 송신 및 자격 증명 접근이 실제로 발생하기 때문에, 제한된 버전은 악성 코드로 분류되었습니다.
컨테이너 전용 게이트는 운영상 가장 주목할 만한 특징입니다. 이는 랩톱이나 분석 샌드박스에서는 아무런 활동도 보이지 않는 회피 수단인 동시에, 실제 IAM 역할과 유효한 비밀 키가 존재할 가능성이 가장 높은 곳에서만 실행되는 타겟팅 수단이기도 합니다. 일반 샌드박스에서 이러한 패키지를 실행하는 분석가는 아무런 변화도 관찰하지 못할 것입니다. 이러한 동작은 Docker 스타일의 호스트 이름과 컨테이너 내 설치 경로를 사용할 때만 나타납니다.
수비수들을 위한 영향, 트렌드 및 지침
여기서의 취약점은 빌드 및 런타임 컨테이너 내부에서 클라우드 자격 증명과 비밀 키가 노출되는 것입니다. IMDS에서 가져온 IAM 역할 자격 증명에는 해당 역할이 보유한 모든 권한이 포함됩니다. secretsmanager:비밀목록 (그리고 후속 조치들) 비밀 값 가져오기이는 저장된 애플리케이션 비밀 정보까지 확장됩니다. 환경 변수 덤프에는 레지스트리 토큰, 데이터베이스 URL 및 API 키가 포함되는 경우가 많습니다. CI 또는 컨테이너 환경(게이트가 선택된 바로 그 환경)에서는 이러한 패키지 중 하나만 전이적으로 설치해도 해당 정보가 유출될 수 있습니다.
엑토플라즘은 우리가 계속해서 목격하는 패턴과 일치합니다. 즉, 설치 시 실행되는 페이로드가 로컬 파일이 아닌 클라우드 메타데이터와 관리되는 비밀 정보를 노리고, 중요도가 높은 환경에서만 실행되도록 스스로를 제한하는 방식입니다. 이에 대한 두 가지 방어적 관찰 사항이 이어집니다.
- 형태를 감지할 수 있습니다.npm/PyPI 설치 후크로, 호출 그래프는 클라우드 시크릿 API와 클라우드 시크릿 API 모두에 도달합니다.AWS 시크릿매니저, gcloud 비밀, az 키볼트) 또는 IMDS 주소와 네트워크 이그레스 싱크는 좁고 신호가 강한 패턴으로, 정상적인 라이프사이클 스크립트에서는 거의 발생하지 않습니다. 정적 흐름 분석 특정 도메인이나 IP 주소에 의존하지 않고도 플래그를 지정할 수 있습니다.
- 환경에 적응할수록 그 효과는 무뎌진다. IMDSv2를 홉 제한 1로 적용하면 컨테이너 워크로드가 인스턴스 메타데이터에 접근하는 것을 방지할 수 있습니다. IAM 역할을 최소 권한으로 지정하면 자격 증명 유출 시 피해 범위를 제한할 수 있습니다. – 스크립트 무시 CI에서는 install-hook이 필요 없는 패키지에 대해 install-hook 벡터를 완전히 제거합니다.
보안 담당자 입장에서 실질적인 점검 사항은 다음과 같습니다. 빌드/CI 컨테이너에서 허용 목록에 없는 공용 IP 주소로의 외부 연결에 대한 경고를 발생시키십시오. npm 설치패키지 수명 주기 스크립트에서 발생하는 IMDS 접근을 감시하고, 클라우드 CLI를 호출하는 설치 후크는 반증될 때까지 의심스러운 것으로 간주해야 합니다.
이 클러스터와 관련된 두 가지 추가 사항입니다. 첫째, 활성화가 컨테이너화된 환경으로 제한되기 때문에 워크스테이션에서 검증했을 때 비활성으로 보이는 패키지라도 프로덕션 환경에서는 여전히 활성화되어 있을 수 있습니다. 검증 시에는 "설치했는데 아무 일도 일어나지 않았다"는 식의 단순한 판단이 아니라, 컨테이너 호스트 이름과 경로 조건을 재현하거나 소스 코드를 직접 읽어야 합니다. 둘째, 비콘 수집기로 공개 요청 검사 서비스를 사용한다는 것은 유출된 데이터 중 일부를 복구하여 사고 대응에 활용할 수 있음을 의미합니다. 조직에서 이러한 패키지를 발견한 경우, 비콘 수집 로직을 통해 성공적인 비콘에 어떤 내용이 포함되었을지 추론하고, 영향을 받은 빌드 또는 런타임 환경에서 접근 가능했던 IAM 역할 자격 증명, 레지스트리 토큰, 관리되는 비밀 키를 폐기해야 합니다. 자격 순환패키지 제거가 아니라, 설치가 완료된 후 취해야 할 실질적인 해결 방법입니다.





