코드 실행 설정 파일: 공급망 보안의 사각지대
출처: Hacker News
레포지토리를 복제하고 편집기에서 여는 순간, 개발자가 코드를 한 줄도 읽기 전에 공격자의 코드가 실행될 수 있습니다. 트리거는 악성 의존성이나 숨겨진 설치 스크립트가 아니라, 레포에 이미 존재하는 평범해 보이는 설정 파일입니다. IDE, AI 코딩 에이전트, 혹은 패키지 매니저가 자동으로 읽고 실행하는 종류의 파일이죠.
VS Code, Cursor, Claude Code, Gemini CLI, npm, Composer, Bundler 등은 모두 셸 명령을 포함할 수 있는 설정 파일을 지원합니다. 일부는 의존성을 설치하거나 테스트를 실행할 때 명령을 실행하고, 다른 일부는 폴더를 열거나 에이전트 세션이 시작될 때 실행합니다. 대부분은 개발자가 별다른 고민 없이 클릭만 하는 일회성 신뢰 프롬프트 뒤에 숨겨져 있습니다. 명령을 실행하는 설정 파일은 메타데이터가 아니라 실행 원시 코드이며, 공급망 공격자는 이를 새로운 공격 수단으로 활용하고 있습니다. 거의 아무도 이러한 파일을 검토하지 않는데, 이 글에서는 실제 소스를 통해 설정 파일 주입 벡터를 살펴보고, 차이를 확인하기 쉬운 패턴으로 정리합니다.
Miasma 웜은 실제 사례입니다. icflorescu/mantine-datatable 레포에 한 번의 커밋(f72462d9 https://github.com/icflorescu/mantine-datatable/commit/f72462d9e5fa90a483062a83e9ffcb2edc57bf7e)이 있었는데, 서명되지 않았고 작성자는 github-actions 이며, 제목은 chore: update dependencies [skip ci]였습니다. 이 커밋은 6개의 파일을 추가했으며, 그 중 5개는 여섯 번째 파일인 .github/setup.js 하나의 드롭퍼를 실행하기 위한 것입니다. SafeDep의 Miasma 소스 레포 분석에서는 전체 사건, 드롭퍼 내부 구조, 그리고 영향을 받은 121개의 레포지토리를 문서화했습니다. 이 글에서는 범위를 좁혀 설정 파일 자체에만 초점을 맞춥니다.
드롭퍼
드롭퍼는 .github/setup.js이며, 크기는 4,348,254 바이트, try/catch 안에 한 줄의 문장이 들어 있습니다. 이 크기가 패딩이 아니라는 점에 주목하세요. 암호화된 페이로드를 담고 있으며, GitHub 코드 검색이 인덱싱을 중단하는 약 384 KB 한계보다 크게 위에 있기 때문에, 작은 런처 파일이 검색에 노출되는 원인입니다. 파일의 처음 몇 바이트는 다음과 같습니다.
// .github/setup.js @ f72462d9 (first 180 bytes of a 4.3 MB file)
try{eval(function(s,n){return s.replace(/[a-zA-Z]/g,function(c){var b=c/dev/null
grep -nE '^[[:space:]]*(system|exec|`)' Gemfile 2>/dev/null # top-level shell-out in a Gemfile
첫 번째 검사는 이 캠페인을 찾아냅니다. 두 번째 검사는 자동 실행 동작 자체를 찾는 것으로, 공격자가 드롭퍼 파일명을 바꾸더라도 남아 있는 부분이며, 사전 복제 훅이나 CI 단계에 넣어 두면 유용합니다.
근본적인 해결책은 편집기와 패키지 매니저 설정을 신뢰 컴퓨팅 베이스(TCB)의 일부로 취급하는 것입니다. SessionStart 훅은 편집기의 postinstall과 같습니다. .cursor/rules 파일은 레포에 포함된 프롬프트 주입이며, 두 경우 모두 개발자의 자격 증명으로 실행되고 의존성 스캔에도 나타나지 않습니다.
이미 열려 있거나 신뢰된 레포가 있다면, 해당 머신을 “깨끗함”이 아니라 “노출됨”으로 간주하세요. 세션이 접근할 수 있는 자격 증명, 즉 GitHub·npm 토큰과 환경에 로드된 AWS, Azure, GCP 키를 가장 먼저 교체해야 합니다. 최근에 열어본 복제본 중 .github/setup.js 파일이 있는지 확인하고, SafeDep의 사건 분석에서 전체 인디케이터 목록을 확인하세요. 드롭퍼는 파동마다 재컴파일되므로 파일 해시만으로는 매칭되지 않습니다.
여기서의 탐지는 악성 의존성 탐지와 동일한 문제이며, 탐지 범위가 더 넓은 파일 집합에 적용됩니다. lockfile을 스캔하면 중독된 패키지는 잡히지만, 중독된 tasks.json은 잡히지 않습니다. SafeDep의 vet 도구는 패키지 측면과 GitHub Actions 워크플로, VS Code 확장 등 일부 자동 실행 표면을 커버하지만, 편집기·에이전트 설정 런처는 아직 완전하게 탐지되지 않는 새로운 표면입니다. 도구와는 별개로, 레포의 설정 파일 자체가 공격 표면의 일부이며, SDLC 단계에서 코드를 검토하듯이 반드시 검토해야 합니다.
관련 읽을거리
- Miasma 웜이 AI 코딩 에이전트를 GitHub 레포를 통해 표적화 – 이번 글이 기반으로 한 사건에 대한 SafeDep 분석
- Miasma 레지스트리 팔 – 드롭퍼 전체를 디오브퓨스케이트한 내용
- 악성 풀 리퀘스트에 대한 위협 모델 – 이러한 커밋이 처음 등장하는 과정을 다룸