이번 주에 가짜 클라이언트가 제 편지함으로 악성 GitHub 저장소를 보냈고, 단순히 스타터 스크립트를 실행했다가 이틀 치 작업물이 날아가고 브라우저에 저장된 모든 비밀번호가 유출되었습니다.
이 "클라이언트"는 고액 연봉의 시니어 엔지니어 채용 공고를 올린 뒤, 빠르게 메시지를 보내며 그럴싸해 보이는 저장소를 보냈습니다. 요청은 간단했습니다. 저장소를 클론하고, npm run dev를 실행한 뒤, 데모가 작동한다는 것을 증명하기 위해 스크린샷을 보내달라는 것이었습니다. 계약서도, 신원 확인도 없었습니다. 개발 서버가 시작되는 순간, 설정 파일에 숨겨진 코드가 명령 제어(C2) 서버에 접속하여 2단계 페이로드를 가져왔고, 로컬 머신에서 자격 증명을 탈취하기 시작했습니다.
공격이 진행된 방식
악성 페이로드는 postcss.config.js 파일에 숨겨져 있었습니다. 이 파일은 보통 몇 가지 간단한 CSS 처리 규칙만 포함하고 있어 대부분의 개발자가 대충 훑어보고 지나치는 파일입니다. 이번 사례에서 공격자는 정상적인 문장 오른쪽 끝에 난독화된 JavaScript 한 줄을 추가했으며, 눈에 띄지 않도록 공백을 많이 넣어 숨겼습니다. npm run dev 명령어가 PostCSS 파이프라인을 실행할 때, 이 숨겨진 코드가 아무런 의심 없이 실행되었습니다.
이 악성코드는 세 단계를 신속하게 수행했습니다:
- C2 접속 – 공격자가 제어하는 서버로 네트워크 연결을 열고 감염된 호스트를 알립니다.
- 2단계 다운로드 – 실제 데이터 유출 로직이 포함된 추가 코드를 가져옵니다.
- 브라우저 자격 증명 탈취 – macOS의 경우 시스템 키체인에 저장된 Chrome Safe Storage 키를 조회합니다. 사용자가 키체인 접근 승인을 하면, 공격자는 Chrome에 저장된 모든 비밀번호를 수집합니다.
즉각적인 탈취 외에도, 페이로드는 VS Code, npm, Discord와 같은 여러 일반적인 개발 도구에 스스로를 기록하여, 해당 애플리케이션을 실행할 때마다 악성 코드가 다시 실행되도록 만들었습니다. 단순히 재부팅하는 것만으로는 감염을 해결할 수 없었습니다. 다음 npm install을 실행하거나 에디터를 여는 순간 백도어가 다시 살아났습니다.
흔히 간과하기 쉬운 위험 신호
- 계약 체결 전 코드를 실행하라는 요청. 정당한 채용 과정에서는 독점적인 작업물을 공유하기 전에 보통 공식적인 계약을 먼저 진행합니다.
- "프로젝트 개요"로 위장한 압축 파일. Zip 또는 RAR 아카이브는 실행 가능한 스크립트나 악성 바이너리를 숨길 수 있습니다.
- "플랫폼 필터를 우회하기 위해" 개인 이메일 주소를 요구하는 경우. 이러한 수법은 남용 사례를 신고할 수 있는 보호된 플랫폼 밖으로 대화를 유도합니다.
- 지원자에게 암호화폐 지갑에 자금을 입금하거나 테스트 토큰을 구매하도록 요구하는 직무 기술서. 이러한 요구는 실제 개발 업무에서는 매우 이례적입니다.
안전을 지키기 위한 실질적인 단계
- 검토 없이 타인의 코드를 절대 실행하지 마세요. 저장소를 읽기 전용 뷰(예: GitHub의 raw 파일 보기)로 열고, 모든 스크립트, 특히 설정 파일과
package.json의scripts항목을 검사하세요. - 모든 첨부 파일을 일반 텍스트로 취급하세요. Zip 파일이 전송되면 샌드박스 환경에서 압축을 풀고, 내용을 열어보기 전에 먼저 확인하세요.
- 브라우저 저장소 대신 전용 비밀번호 관리자를 사용하세요. 브라우저의 키체인이 침해되더라도 비밀번호 관리자의 금고는 격리된 상태로 유지됩니다.
- 신뢰할 수 없는 코드는 네트워크 접속이 차단된 격리된 가상 머신이나 컨테이너에서 실행하세요. 이를 통해 공격자가 C2 서버에 접속하는 것을 차단할 수 있습니다.
- 모든 계정에 2단계 인증(2FA)을 활성화하세요. 비밀번호가 도난당하더라도 2단계 인증이 있으면 무단 로그인을 막을 수 있습니다.
- 개발 도구를 최신 상태로 유지하고, 가능한 경우 자동 무결성 검사를 활성화하세요. 일부 에디터는 이제 핵심 파일이 예기치 않게 수정될 경우 경고를 표시합니다.
악성 코드를 실행한 것으로 의심된다면 시스템이 침해되었다고 가정해야 합니다. 중요한 데이터를 백업하고, 드라이브를 포맷한 뒤 운영 체제를 재설치하세요. 단순히 재부팅하는 것만으로는 일반 앱의 파일을 다시 쓰는 지속성 메커니즘(persistence mechanism)을 제거할 수 없습니다.
결론: 숨겨진 단 한 줄의 JavaScript가 일상적인 데모를 본격적인 자격 증명 탈취 작전으로 바꿀 수 있습니다. 검증하기 전까지는 모든 저장소를 신뢰할 수 없는 것으로 간주하고, 격리(isolation)를 작업 흐름의 기본 원칙으로 삼으세요. 잠시 서두른 대가는 파일을 재차 확인하는 데 드는 노력보다 훨씬 큽니다.
