실패한 국가 배후 추정 공격 해부
(grack.com)- 캐나다의 한 개발자가 가짜 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가 들어 있음
- macOS:
- C2:
뒤늦게 보인 경고 신호
- 메시지에는 자세히 보면 LLM 흔적으로 보이는 표현이 있었음
- LinkedIn 프로필은 첫눈에는 정상처럼 보였지만, 학위·자격 나열이 어색했고 실제 활동도 없었음
- 웹사이트의 소셜 미디어 링크는 실제 이력이 있었지만, 이름이 2025년 11월에 바뀌었음
- 게시물은 구체성이 낮고 회사들에 대한 모호한 칭찬에 가까웠음
- 회사 웹사이트들은 화려했지만 실제 존재감은 거의 없었음
- 공격자는 정식 캘린더 초대를 보내지 않고 시간과 Google Meet만 전달함
- 통화 중 카메라는 계속 꺼져 있었고 이동 중이라고 말함
- 싱가포르 기반 VC, CEST 시간대 활동, 캐나다 개발자 대상 접근, 미국 고객을 겨냥한
.cc도메인이 함께 나타남- 멀리 있는 조직의 신뢰성을 확인하기가 더 어려웠음
- 개별 신호만으로는 명확하지 않았지만, 여러 노란불이 함께 쌓이면 빨간불로 볼 수 있는 패턴이었음
공격 배후와 범위
- 배후를 확정할 수는 없음
- 공격은 개발자를 겨냥했고, 가짜 인물, 설득력 있는 커버 스토리, 여러 가짜 웹사이트, 인내심 있는 일정, 정교한
git함정을 포함함 - 2026년에 여러 행위자가 사용하는 “가짜 인터뷰 사기” 흐름과 맞닿아 있음
- Reddit의 Rust 커뮤니티에서도 비슷하게 표적이 됐다는 사례가 언급됨
- 같은 방식이 Rust 저장소의 함정
build.rs스크립트로 구성됐다면 속아 넘어갔을 수 있을 만큼 개발자 워크플로에 밀착돼 있었음
댓글과 토론
Lobste.rs 의견들
-
제목에서 국가 배후 공격자일 수 있다고 추측한 게 혼란스러움. 여기엔 그 정도의 준비나 복잡성이 꼭 필요해 보이는 부분이 없음
가능성은 상상할 수 있지만, 다른 시나리오와 비슷한 정도로만 그럴듯해 보임- 이런 정교한 사기는 자원이 많이 들어서 꽤 드물기 때문인 듯함. 정교한 사기를 보면 국가 배후일지도 모른다고 느끼기 쉬움
그래도 거의 확실히 국가 배후 공격자는 아닐 것 같음. 이런 유형의 공격은 이제 그렇게 어렵지 않음
- 이런 정교한 사기는 자원이 많이 들어서 꽤 드물기 때문인 듯함. 정교한 사기를 보면 국가 배후일지도 모른다고 느끼기 쉬움
-
일주일 전 내 블로그 글에서 설명한 가상의 사례와 이번 공격이 비슷한 건 정말 우연임: the hypothetical I describe in my blog post from a week ago
기술에 밝은 전문가도 속을 수 있는 예시로, 그럴듯해 보이는 몇 가지 공격 중 하나를 골랐을 뿐임. 사람을 노린 여러 정교한 사기가 늘어나는 건 봤지만, 면접 사기라는 흐름이 진행 중인지는 몰랐음. 그래서 이번 공격을 알게 되니 좀 섬뜩했음- 정말 섬뜩함. 그 글에 대한 Lobsters discussion 링크도 있음
-
지난주 Lua Ventures의 D____ S_____에게서 똑같은 식의 면접 제안을 받았음. 대부분의 채용 담당자 스팸처럼 무시했는데, 그러길 잘했음
- Rust 쪽 사람인지 궁금함. Rust 커뮤니티의 주요 인물들이 Lua의 표적이 됐는데, Rust에만 국한된 건지 더 넓은지 알고 싶음
-
며칠 전이나 몇 주 전에 이런 제출글이 있었던 것 같음
- 아마 이 글일 수도 있음: https://blog.daviddodda.com/how-i-almost-got-hacked-by-a-job-interview?
비슷한 글이 여러 개 있었던 느낌임
- 아마 이 글일 수도 있음: https://blog.daviddodda.com/how-i-almost-got-hacked-by-a-job-interview?
-
“진짜 이메일처럼 보였다”는 게 놀라움. 너무 명백히 LLM 생성 문장이라서 두 번째 문장부터 의심했을 것 같음
- 내게는 그렇게 명백하지 않았음. 그냥 LinkedIn식 문체처럼 느껴졌음. 그것도 종종 LLM이 쓴 것처럼 들리지만, 냄새가 너무 비슷해서 둘을 구분할 능력이 있는지 잘 모르겠음