Shai-Hulud
감염된 npm 패키지가 설치되면 실행되는 자기 전파형 웜으로, npm, GitHub, 클라우드 토큰을 훔치고 피해자의 다른 패키지에 감염된 버전을 게시합니다. 대부분의 감염은 CI에서 실행되었습니다.
빌드 확인 방법 →레드 팀 대 블루 팀
공격자가 귀사를 직접 노리는 경우는 드뭅니다. 전 세계 기업이 의존하는 패키지, Action, 빌드 도구를 노린 뒤, 그 모두에 한꺼번에 익스플로잇을 시도합니다. 공격자에게는 귀사의 종속성 목록이 필요 없습니다. 귀사에는 필요합니다.
소프트웨어 공급망 공격은 제품을 직접 공격하는 대신 오픈 소스 패키지, CI/CD 구성 요소, 빌드 시스템처럼 제품을 만드는 데 쓰이는 무언가를 손상시킵니다. 현대 소프트웨어는 대부분 다른 사람의 코드입니다. 대부분이 오픈 소스 패키지에서 오며, 그중 대부분은 팀의 누구도 선택하지 않은 전이적 종속성입니다. 널리 쓰이는 구성 요소 하나를 손상시키면 그 위에서 빌드하는 모든 조직에 도달합니다.
이것이 공격자가 표적을 고르는 방식을 바꿉니다. 공격자는 한 회사의 종속성을 조사하지 않습니다. 영향 범위가 넓은 패키지나 도구를 골라 어디서든 악용하고 무엇이 응답하는지 봅니다.
인기 있는 오픈 소스 패키지의 결함이 공개되면, 공격자는 취약한 버전을 출시한 모든 제품에서 이를 악용합니다. 대개 아무도 직접 선택하지 않은 전이적 종속성입니다.
공격자가 메인테이너 계정을 탈취하거나 웜이 게시 토큰을 훔쳐, 신뢰받는 패키지의 오염된 버전을 릴리스합니다. Shai-Hulud npm 웜처럼 빌드가 이를 설치하는 순간 실행됩니다.
기여자가 오랜 시간에 걸쳐 오픈 소스 프로젝트에서 신뢰를 쌓은 뒤, 다운스트림 배포판과 제품이 가져가는 릴리스에 악성 코드를 몰래 넣습니다. xz utils 백도어가 그 예입니다.
인기 패키지와 이름이 비슷한 패키지가 오타를 노립니다. 종속성 혼동은 한발 더 나아가, 기업의 내부 패키지와 같은 이름의 공개 패키지가 그 대신 설치되게 만듭니다.
GitHub Action이나 다른 빌드 도구가 탈취되면, 이를 사용하는 모든 파이프라인이 시크릿에 접근 가능한 상태로 공격자의 코드를 실행합니다. tj-actions와 Codecov가 그 예입니다.
빌드 자체가 변조되거나 빌드와 배포 사이에 아티팩트가 바꿔치기되어, 깨끗한 소스 트리에서도 손상된 릴리스가 만들어집니다. SolarWinds가 그 예입니다.
SolarWinds는 가장 잘 알려진 공급망 공격으로, 빌드에서 삽입된 악성 코드가 일반 업데이트로 고객에게 배포되었습니다. 최근의 공격은 대신 패키지 레지스트리와 CI 파이프라인을 노립니다. 그곳에서는 손상된 패키지나 Action 하나가 이를 사용하는 모든 빌드에 도달합니다. 최신순:
감염된 npm 패키지가 설치되면 실행되는 자기 전파형 웜으로, npm, GitHub, 클라우드 토큰을 훔치고 피해자의 다른 패키지에 감염된 버전을 게시합니다. 대부분의 감염은 CI에서 실행되었습니다.
빌드 확인 방법 →CISA는 이 인기 GitHub Action의 모든 버전이 3일 동안 손상되었고, 시크릿이 워크플로 로그에 노출되었다고 발표했습니다. 이를 태그로 참조한 워크플로는 공격자의 코드를 실행했습니다.
Andres Freund는 SSH 로그인이 CPU를 지나치게 많이 사용한다는 사실을 알아차린 뒤 xz 5.6.0 및 5.6.1 버전에서 백도어를 발견했습니다. 백도어는 업스트림 프로젝트 자체에 삽입되어 있었습니다.
Apache Log4j 2의 결함으로, 로그에 기록되는 메시지를 제어하는 공격자가 임의 코드를 실행할 수 있었으며 CVSS 10.0으로 평가되었습니다. 대부분의 팀에게 어려웠던 점은 어떤 애플리케이션에 log4j-core가 포함되어 있는지 찾는 일이었고, 대개 종속성의 종속성으로 들어 있었습니다.
공격자가 팀들이 CI 파이프라인에서 실행하던 Bash Uploader 스크립트를 변조하여, 자격 증명, 토큰, 키를 포함한 CI 환경을 Codecov 인프라 외부의 서버로 보내게 했습니다. 1월 31일부터 한 고객이 4월 1일에 신고할 때까지 아무도 알아차리지 못했습니다.
공격자는 SolarWinds Orion의 빌드 프로세스에 오랫동안 은밀하게 접근해 Orion 릴리스 2019.4부터 2020.2.1 HF1까지 악성 코드를 삽입했고, 고객은 이를 일반 업데이트로 설치했습니다. CISA는 이에 대응해 긴급 지침을 발령했습니다.
레드 팀 대 블루 팀
각 단계를 공격자의 말로, 그리고 이에 답하는 방어와 함께 소개합니다.
3단계 중 1단계
표적을 알 필요는 없다. 패키지를 고르면 표적은 저절로 정해진다.
레드 팀
그들의 잠금 파일은 필요 없다. 인기 패키지는 거의 모든 소프트웨어의 전이적 트리 깊숙이 들어 있고, 대부분의 팀은 자신들이 무엇을 출시하는지 말하지 못한다. 공개된 단서로 범위를 좁힐 수 있다. JavaScript 번들과 HTTP 헤더의 버전, 공개 리포지토리와 컨테이너 이미지. 그리고 postinstall 스크립트가 내려받는 바이너리나 베이스 이미지 아래의 레이어처럼 빌드가 잠금 파일 밖에서 가져오는 것은 그들의 스캐너가 본 적이 없으니, 아무도 패치하지 않는다.
블루 팀
CRACI는 모든 빌드로 들어오는 트래픽을 보고, 빌드가 가져오는 모든 패키지와 파일을 기록합니다. 잠금 파일 기반 도구가 놓치는 숨은 종속성까지 전이적 트리의 모든 깊이에서 기록합니다. SBOM은 출시된 빌드에서 나오므로, 누군가 찾기 시작하기 전에 공격자에게는 필요 없었던 그 목록을 확보하게 됩니다.
빌드 시점 SBOM →레드 팀
새 권고는 어떤 버전이 취약한지 정확히 알려 준다. 익스플로잇을 만들고, 응답하는 것은 무엇이든 스캔해, 스캔 결과로 상대가 누구인지 알아내라. 그들의 팀이 아직 Slack 스레드에서 "우리도 영향을 받나요?"라고 묻는 동안, 당신은 그날 바로 시작한다.
블루 팀
CRACI는 모니터링 중인 SBOM을 새로운 취약점 데이터와 대조해 지속적으로 재평가합니다. 빌드 이력은 직접이든 전이적이든 취약한 종속성이 포함된 빌드를 보여 주므로, 어디에 얼마나 오래 출시되었는지 알 수 있습니다.
우리도 영향을 받나요? →3단계 중 2단계
패치되기 전에 결함을 악용하거나, 결함은 건너뛰고 그들의 파이프라인에 올라타라.
레드 팀
AI는 보안 팀이 티켓을 분류하기도 전에 권고와 패치 diff를 실제로 작동하는 공격으로 바꿔 준다. 공개 시점이 곧 출발 신호다. 그들의 스프린트 계획은 다음 주다.
블루 팀
정책 게이트로 특정 CVE가 포함된 모든 빌드를 차단할 수 있으므로, 트리의 얼마나 깊은 곳에 있든 결정하는 순간 취약한 버전의 출시가 멈춥니다.
CVE 조치 →레드 팀
워크플로는 @v4 같은 태그를 신뢰하고, 태그는 옮길 수 있다. 인기 GitHub Action 하나를 손상시키면 이를 사용하는 모든 워크플로가 시크릿에 접근 가능한 상태로 당신의 코드를 실행한다.
블루 팀
CRACI는 워크플로가 실행하는 GitHub Actions를 실제로 실행된 커밋에 고정해 빌드의 SBOM에 기록합니다. Action이 손상되면 태그로 추측하는 대신 어떤 빌드가 악성 코드를 실행했는지 확인할 수 있습니다.
CI/CD 보안 체크리스트 →3단계 중 3단계
시크릿을 챙기고, 더 얻을 때까지 머물러라.
레드 팀
악성 패키지 릴리스는 토큰과 클라우드 자격 증명이 있는 그들의 빌드 안에서 설치 스크립트를 실행한다. CI 러너는 대개 인터넷 전체에 접속할 수 있으므로, 누군가 패키지가 악성이라는 사실을 알기 전에 시크릿은 당신의 서버로 빠져나간다.
블루 팀
기본 거부로 설정한 이그레스 정책은 빌드가 패키지 소스와 게시 대상에만 접속하게 하고 그 밖의 모든 것을 차단합니다. 아무도 본 적 없는 공격의 첫 번째 피해자가 되는 경우에도 시크릿을 공격자의 호스트로 보내는 멀웨어는 차단되며, 정책 위반에 대한 알림을 받습니다.
이그레스 정책 →레드 팀
오래 유지되는 셀프 호스팅 러너는 선물이다. 워크스페이스나 도구 캐시에 무언가를 심어 두고, 다음 작업과 다음 시크릿이 찾아오기를 기다리면 된다.
블루 팀
모든 CRACI 작업은 자체 격리된 가상 머신에서 실행되므로, 한 작업이 남긴 것이 다음 작업을 기다리는 일은 없습니다. 패치하고 강화해야 할 러너 플릿도 없습니다.
관리형 대 셀프 호스팅 러너 →공개 이후
공격자는 귀사가 그 종속성을 사용한다는 사실을 알 필요가 없습니다. 어디서든 익스플로잇을 시도하고 무엇이 응답하는지 봅니다. 귀사 팀은 그 종속성이 어디에 출시되었는지 알아야 합니다. 차이는 찾으러 다녀야 하느냐에 있으며, 치러야 할 경쟁은 해마다 늘고 있습니다. 하루에 공개되는 CVE 수를 확인하세요.
레드 팀
블루 팀, 빌드 기록 없음
블루 팀, CRACI 사용
모든 공급망 공격을 막는 단일 통제는 없습니다. 다음을 겹겹이 적용하면 위의 각 단계가 더 어려워집니다. 파이프라인 측면의 자세한 내용은 CI/CD 파이프라인 보안 체크리스트를 참조하세요.
공격자는 모든 권고를 지켜보다가 취약점이 공개된 날 모두에게 익스플로잇을 시도합니다. 귀사의 종속성 목록은 처음부터 필요 없었습니다. 귀사에는 필요합니다. CRACI는 빌드 자체에서 얻은 그 목록을 귀사 팀에 제공하고, 공격자가 기대는 문인 열린 이그레스와 공유 러너를 닫습니다.
제품 자체가 아니라 오픈 소스 패키지, CI/CD 구성 요소, 빌드 시스템처럼 제품을 만드는 데 쓰이는 코드, 도구, 서비스를 노리는 공격입니다. 한 번의 침해가 영향받는 구성 요소를 사용하는 모든 조직에 도달합니다.
Shai-Hulud 웜(2025년부터)은 감염된 npm 패키지를 통해 퍼지며 CI 시크릿을 훔쳤습니다. tj-actions/changed-files 침해(CVE-2025-30066)는 인기 GitHub Action을 탈취했습니다. xz utils 백도어(CVE-2024-3094)는 업스트림 압축 라이브러리에 몰래 삽입되었습니다. Log4Shell(CVE-2021-44228)은 널리 쓰이는 Java 로깅 라이브러리의 결함을 악용했습니다. Codecov의 Bash Uploader(2021년)는 CI 자격 증명을 외부 서버로 보내도록 변조되었고, SolarWinds Orion(2020년)은 빌드에서 삽입된 악성 코드를 출시했습니다.
SolarWinds입니다. 2020년 SolarWinds Orion의 빌드 프로세스에 은밀하게 접근한 공격자들이 릴리스에 악성 코드를 삽입했고, 고객은 이를 일반 업데이트로 설치했습니다. 이 사건은 깨끗한 소스 트리만으로는 충분하지 않으며 빌드 자체가 신뢰할 수 있고 검증 가능해야 한다는 것을 보여 주었습니다.
대개 모르며, 알 필요도 없습니다. 대부분의 공격은 회사가 아니라 패키지를 노립니다. 권고가 공개되면 공격자는 취약한 버전을 실행할 수 있는 모든 대상에 익스플로잇을 시도하고 무엇이 응답하는지 봅니다. JavaScript 번들이나 오픈 소스 리포지토리의 버전 같은 공개 단서가 조준을 돕습니다. 비대칭은 여기에 있습니다. 여러분에게는 그 목록이 필요합니다. 영향을 받는지 알려면 잠금 파일 밖에서 가져온 것을 포함해 모든 빌드가 무엇을 출시했는지 알아야 합니다.
모든 공격을 막는 단일 통제는 없습니다. 모든 빌드가 출시하는 것의 SBOM을 보관하고 새 권고와 대조해 모니터링하며, 종속성과 GitHub Actions를 고정하고, 취약한 버전을 빌드 단계에서 차단하고, CI 작업이 접속할 수 있는 대상을 제한하고, 작업을 격리하고 시크릿을 제한하며, 배포된 아티팩트가 빌드한 것과 같은지 검증하세요.
잠금 파일을 커밋하고 CI에서 이를 기준으로 설치하며, 빌드에 필요하지 않은 곳에서는 설치 스크립트를 비활성화하세요(npm의 --ignore-scripts). 모든 빌드가 실제로 설치한 패키지 버전의 기록을 보관하면 Shai-Hulud 같은 새로운 공격 물결이 발표될 때 빠르게 확인할 수 있습니다. CI 작업이 접속할 수 있는 대상을 제한해 악성 설치 스크립트가 토큰을 외부로 보내지 못하게 하고, 감염된 버전이 접근할 수 있었던 토큰은 모두 교체하세요.
보안 훈련에서 레드 팀은 공격자 역할을, 블루 팀은 방어 역할을 맡습니다. 이 페이지도 같은 방식으로 나누어, 공급망 공격의 각 단계를 공격자의 말로 설명하고 방어가 이에 답합니다.
알려진 것은 탐지합니다. xz 백도어처럼 CVE가 공개된 악성 릴리스는 다른 취약점과 마찬가지로 표시되며, 정책 게이트로 이를 포함한 빌드를 차단할 수 있습니다. CRACI는 패키지 동작을 분석하거나 평판 점수를 매기지 않으므로, 완전히 새로운 악성 패키지는 권고가 나오기 전까지 표시되지 않습니다. 이그레스 정책이 바로 그런 경우를 위한 것입니다. 기본 거부 설정에서는 빌드가 필요한 호스트에만 접속할 수 있으므로, 아무도 본 적 없는 공격에서도 패키지가 시크릿을 외부로 보낼 수 없습니다.