실패한 국가 배후 추정 공격 해부
2 hours ago
1
- 캐나다의 한 개발자가 가짜 VC 인터뷰를 가장한 백도어 유도 공격을 받았고, 공격 흐름은 crates.io 패키지 관리자를 노린 것으로 의심될 만큼 개발자 업무에 맞춰져 있었음
- 미끼 저장소는 “Ticket Harbor”라는 TypeScript 앱처럼 보였지만, patch-package와 typescript+5.9.2.patch로 TypeScript 실행 경로에 악성 코드를 숨김
- 삽입된 스텁은 base64와 XOR 난독화를 풀어 new Function(...)으로 실행되고, operators/3.png의 숨은 청크와 WASM 스텁을 거쳐 1.68MB짜리 2단계 페이로드를 별도 Node 프로세스로 띄움
- 최종 페이로드 “PinpinRAT”은 RSA-2048 키쌍과 AES-256-CBC 세션 키를 만들고, 호스트 지문 수집, 파일 업로드·다운로드, 프로세스 실행, 파일시스템 조작, DNS 질의, 자가 제거를 지원함
- 저장소를 실행했다면 즉시 네트워크를 끊고 다른 기기에서 자격 증명을 교체해야 하며, 쿠키와 비밀번호 보호 비밀까지 탈취됐을 가능성을 전제로 대응해야 함
가짜 인터뷰로 시작된 개발자 표적 공격
- 공격자는 “Lua Ventures” 소속이라고 주장하는 가짜 인물로 접근함
- Lua Ventures는 싱가포르 기반 DeFi 분야 VC로 소개됐지만, 실제로는 이미 활동을 중단한 곳이었음
- 인물명은 실제 동명이인들과 혼동될 수 있어 공개되지 않음
- 이메일은 그럴듯했고, 평범하지만 정상처럼 보이는 LinkedIn 프로필 링크도 포함돼 있었음
- 자문을 찾는 투자사로 Lyrasing과 Roadpay를 언급함
- 두 회사는 기본적인 웹 존재감이 있어 가짜라기보다 초기 단계 회사처럼 보였음
- Roadpay 사이트의 archive.org 스냅샷도 남아 있음
- 이메일 왕복 뒤 Google Meet 통화까지 이어짐
- 통화 상대는 독일식 억양이 있는 남성이었고, 이동 중이라고 말함
- 통화 자체에는 뚜렷하게 이상한 점이 없었음
“테스트 과제”로 위장한 실행 트리거
- 통화 뒤 공격자는 “테스트”를 제안하며 저장소를 보냄
- 저장소는 “Ticket Harbor”라는 페리 티켓팅 앱처럼 꾸며져 있었음
- 포함된 task.txt는 지루하지만 그럴듯한 과제 목록을 담고 있었고, 마지막에 실행 지시가 들어 있었음
- 저장소의 typecheck, 테스트 스위트, 관련 desktop/server 빌드 명령을 실행하라는 내용
- 이 지시가 실제 감염 트리거였음
- npm run typecheck, build, dev처럼 TypeScript가 실행되거나 typescript.js를 import하는 순간 페이로드가 실행될 수 있었음
TypeScript 패치에 숨은 실행 체인
- 첫 경고 신호는 저장소가 TypeScript 과제처럼 보였다는 점이었음
- 요청 내용이 아키텍처 분석보다 TypeScript 채용 과제에 가까웠음
- 저장소를 Claude에 넣어 빠르게 확인한 결과, patch-package 관련 이상 징후가 드러남
- patches/ 디렉터리가 비정상적으로 많았음
- 일부 패치는 정상처럼 보여 실제 페이로드를 숨기는 노이즈 역할을 함
- 예시로 sumchecker+3.0.1.patch, @electron+get+2.0.3.patch, extract-zip+2.0.1.patch가 포함됨
- 핵심 악성 코드는 typescript+5.9.2.patch에 있었음
- typescript.js와 _tsc.js 맨 위에 즉시 실행 스텁을 삽입함
- 스텁은 base64 문자열을 디코드하고, 각 바이트를 키 73으로 XOR 복호화한 뒤 new Function(...)으로 실행함
- require, Buffer, WebAssembly, process, __dirname이 실행 함수에 전달됨
- 실행 체인은 여러 단계를 거침
- 네 개의 postinstall 훅이 patch-package를 실행함
- 그중 하나는 patch 파일에 git update-index --skip-worktree를 적용해 git status에서 숨김
- 로더는 operators/3.png 파일 뒤에 붙은 숨은 청크를 읽음
- 커스텀 wAsm 청크의 작은 WASM 스텁을 실행함
- 1.68MB짜리 난독화된 2단계 페이로드를 조용한 별도 Node 프로세스로 실행함
- 공격 코드는 실행 후 흔적을 줄이도록 설계됨
- git skip-worktree로 패치를 숨김
- 드로퍼가 첫 실행 뒤 patch 파일에서 자신이 삽입한 줄을 삭제함
- 2단계 임시 디렉터리는 실행 시 자가 삭제됨
PinpinRAT의 기능
- 최종 페이로드는 “PinpinRAT”으로 지칭됨
- 내부 문자열 때문에 붙은 이름이며, 다른 이름으로 알려졌을 가능성은 배제되지 않음
- 온라인에서 다른 참조는 확인되지 않음
- 페이로드는 여러 난독화 계층 안에 들어 있었음
- obfuscator.io
- 추가 base64 계층 두 개
- RAT는 시작 시 호스트 지문을 수집해 유출함
- 기본 IP 주소와 전체 IP 목록
- os.userInfo().username의 사용자명
- 호스트명
- OS type, release, platform, architecture
- 프로세스 PID와 전체 process.argv
- Node 버전
- 암호화 구조도 포함함
- 로컬에서 RSA-2048 키쌍 생성
- 무작위 AES-256 세션 키 aes_psk 생성
- 이후 트래픽은 AES-256-CBC로 암호화되고 HMAC-SHA256 무결성 태그가 붙음
- 지원 명령은 원격 접근 트로이목마 수준의 기능을 제공함
- env: process.env를 JSON 문자열로 만들어 전송
- upload: 임의 파일 경로를 읽어 유출
- download: 공격자가 제공한 바이트를 쓰기 가능한 경로에 기록
- spawn: 선택적 shell 확장과 함께 임의 프로세스 실행
- ls, cd, pwd, cp, mv: 일반 파일시스템 조작
- dns: 지정한 resolver를 통해 임의 이름 해석
- dismantle: 자가 제거
침해 지표와 즉시 대응
- 페이로드가 담긴 이미지는 VirusTotal에서 어떤 AV 엔진에도 탐지되지 않았음
- 실행한 경우 즉시 시스템을 네트워크에서 분리해야 함
- 자격 증명은 다른 기기에서 교체해야 함
- 쿠키와 비밀번호로 보호된 비밀도 침해된 것으로 간주해야 함
- PinpinRAT 관련 침해 지표는 다음과 같음
- C2: 89.124.107.161:80
- Windows 예약 작업: PinpinWrappedJs
- macOS 프로세스 위장: com.apple.WebKit.Networking
- 환경 변수: NODT_PAYLOAD_PATH, NODT_PAYLOAD_ARGS
- PNG 청크 가드: WASMPACK (wAsm)
- PINPIN_NO_AUTOSTART=1: persistence 중단
- mutex.js를 포함한 cronjob: RAT 권한이 있을 때만 존재할 수 있으며, macOS에는 없을 수 있음
- typescript.js 앵커 문자열: 12ff4b51, ticket-harbor-tsc-shim-anchor
- 페이로드가 포함된 typescript+5.9.2.patch
- 아티팩트 디렉터리:
- macOS: ~/Library/Caches/runtime-cache/.cache-<randomhex>/
- Linux: /tmp/.cache-<randomhex>/
- Windows: %TEMP%\.cache-<randomhex>\
- 내부에 payload.js와 mutex.js가 들어 있음
뒤늦게 보인 경고 신호
- 메시지에는 자세히 보면 LLM 흔적으로 보이는 표현이 있었음
- LinkedIn 프로필은 첫눈에는 정상처럼 보였지만, 학위·자격 나열이 어색했고 실제 활동도 없었음
- 웹사이트의 소셜 미디어 링크는 실제 이력이 있었지만, 이름이 2025년 11월에 바뀌었음
- 게시물은 구체성이 낮고 회사들에 대한 모호한 칭찬에 가까웠음
- 회사 웹사이트들은 화려했지만 실제 존재감은 거의 없었음
- 공격자는 정식 캘린더 초대를 보내지 않고 시간과 Google Meet만 전달함
- 통화 중 카메라는 계속 꺼져 있었고 이동 중이라고 말함
- 싱가포르 기반 VC, CEST 시간대 활동, 캐나다 개발자 대상 접근, 미국 고객을 겨냥한 .cc 도메인이 함께 나타남
- 멀리 있는 조직의 신뢰성을 확인하기가 더 어려웠음
- 개별 신호만으로는 명확하지 않았지만, 여러 노란불이 함께 쌓이면 빨간불로 볼 수 있는 패턴이었음
공격 배후와 범위
- 배후를 확정할 수는 없음
- 공격은 개발자를 겨냥했고, 가짜 인물, 설득력 있는 커버 스토리, 여러 가짜 웹사이트, 인내심 있는 일정, 정교한 git 함정을 포함함
- 2026년에 여러 행위자가 사용하는 “가짜 인터뷰 사기” 흐름과 맞닿아 있음
- Reddit의 Rust 커뮤니티에서도 비슷하게 표적이 됐다는 사례가 언급됨
- 같은 방식이 Rust 저장소의 함정 build.rs 스크립트로 구성됐다면 속아 넘어갔을 수 있을 만큼 개발자 워크플로에 밀착돼 있었음
-
Homepage
-
Tech blog
- 실패한 국가 배후 추정 공격 해부