Rust 공급망 공격 분석과 build.rs 문제

지원 종료(EOL)된 PHP용 php2shell 패치(CVE-2026-109618 및 CVE-2026-109619)가 출시되었습니다. 수요가 많아 평소보다 영업팀의 답변이 늦어지고 있으므로, 패치된 패키지를 가장 빠르게 확보하려면 온라인에서 Runtimes용 ELS를 구매하시기 바랍니다 (새 탭에서 열림). 패치 FAQ 읽어보기

이 블로그 글의 AI 요약으로 바로 가기

  • Perplexity를 사용하여 이 게시물을 요약해 주세요
  • Gemini로 이 게시물을 요약해 주세요

주요 내용

  • 악의적인 build.rs 스크립트는 다음 기간 동안 실행됩니다. cargo build Cargo를 호출한 프로세스의 권한을 갖습니다.
  • Stable Cargo는 빌드 스크립트 실행을 거부하면서도 의존성 트리를 생성할 수 있는 공식적인 방법을 제공하지 않습니다.
  • TrapDoor가 발견한 34개의 악성 패키지 중 6개가 crates.io에 올라왔으며, npm에는 21개, PyPI에는 7개가 올라왔습니다.
  • npm과 pnpm은 이제 기본적으로 승인되지 않은 종속성 설치 스크립트를 차단하지만, 안정판 Cargo에는 종속성 전체에 적용되는 이에 상응하는 기능이 없습니다.

Rust 개발자들은 수년 동안 이 언어가 안전하다는 말을 들어왔습니다. 메모리 안전성, 안전한 코드에서는 데이터 경합이 발생하지 않는다는 점, 다른 언어에서는 런타임에야 비로소 문제가 드러나는 오류를 컴파일 시점에 잡아내는 타입 시스템 등입니다. 하지만 이 모든 것은 프로그램이 생성되기 전에 실행되는 코드에는 전혀 적용되지 않습니다. 2026년 5월, ‘ TrapDoor npm, PyPI, crates.io에 384개 이상의 버전에 걸쳐 34개의 악성 패키지를 배포했습니다. 이 중 어느 것도 사용자가 패키지를 호출할 때까지 기다리지 않았습니다. 설치 시, 임포트 시, 또는 컴파일 중에 즉시 공격을 실행했습니다. 34개 중 6개는 crates.io에 올라와 있었으며, Sui 및 Move 툴링으로 위장한 채 build.rs 암호화폐 지갑 키스토어를 찾기 위해서입니다. 크레이트 작성자는 매니페스트에서 자신의 빌드 스크립트를 비활성화할 수 있습니다. 반면, 사용자로서는 트리 전체에 걸쳐 종속성 빌드 스크립트를 비활성화하는 범용 스위치를 사용할 수 없습니다. 이 글은 바로 이러한 격차에 대해 다루고 있습니다.

TrapDoor가 세 개의 레지스트리에서 수행한 작업

npm, PyPI, crates.io는 모두 동일한 방식으로 공격받은 것은 아니었습니다. 확인된 34개의 패키지 중 21개는 npm에 등록되어 있었으며, 사용자가 패키지를 설치하는 즉시 postinstall 스크립트를 통해 공유된 자격 증명을 탈취하는 페이로드를 실행했습니다. 7개는 PyPI에 등록되어 있었으며, import 시 원격 JavaScript 페이로드를 가져와 로컬에서 실행하는 방식이었습니다. crates.io에는 단 6개만이 등록되었으며, 컴파일 중에 실행되는 Rust 빌드 스크립트를 이용했습니다. 이러한 패턴이 이번 캠페인의 공통된 특징입니다. 각 생태계에서 공격자는 해당 생태계가 이미 제공한 실행 경로를 활용했습니다.

레지스트리 명명된 패키지 실행 훅 그것이 한 일
npm 21 설치 후 라이프사이클 스크립트 란 trap-core.js, SSH 기반의 횡방향 이동을 수행하는 공유 인증 정보 수집 도구
PyPI 7 가져오기 시 실행 다음 경로를 통해 원격 JS 페이로드를 가져와 실행했습니다. node -e
crates.io 6 build.rs 빌드 스크립트 Sui 및 Aptos 지갑 키스토어를 검색하여 XOR로 암호화한 후 GitHub Gists로 유출했습니다.

다음과 같은 6개의 crates.io 패키지 – move-analyzer-build, move-compiler-tools, move-project-builder, sui-framework-helpers, sui-move-build-helper, 그리고 sui-sdk-build-utils – 이는 일반적인 Rust 사용자층보다는 Sui 및 Move 개발자들을 대상으로 한 것이었습니다. 패키지 수와 페이로드의 정교함 측면에서 npm 측이 대부분의 피해를 입었습니다. crates.io 측은 여전히 Rust 엔지니어들에게 가장 중요한데, 이는 build.rs 정확히 ~와 같은 기능을 하는 스크립트 build.rs 이 스크립트는 수행해야 할 역할을 수행합니다. 애플리케이션 자체가 실행되기 전에, 빌드 프로세스의 권한을 가지고 조용히 실행됩니다.

build.rs가 설치 후 스크립트보다 더 나은 대상인 이유

TrapDoor Rust 공급망 공격에서 npm, PyPI 및 crates.io 전반에 걸친 실행 후크
이제 npm과 pnpm 모두 기본적으로 승인되지 않은 종속성 설치 스크립트를 차단하며, 해당 스크립트가 진정으로 필요한 패키지에 대해서는 사용자가 직접 승인하고 커밋한 허용 목록을 적용합니다. pnpm은 2025년 1월 v10부터 이 기능을 지원해 왔습니다. 반면 npm은 2026년 7월 v12가 되어서야 이 기능을 도입했는데, 이는 ‘TrapDoor’ 사건이 발생한 지 몇 주 뒤였기 때문에, 해당 캠페인이 진행되는 동안 설치한 사용자에게는 설치 후 페이로드가 여전히 기본적으로 실행되었습니다. Cargo 안정 버전에는 의존성 전체에 적용되는 이와 유사한 허용 목록이나 기본 차단 기능이 없습니다. build.rs 이는 Cargo가 속한 크레이트를 컴파일하기 전에 Cargo가 컴파일하고 실행하는 Rust 프로그램으로, Cargo를 실행하는 프로세스의 권한을 갖습니다. 코드 생성이나 네이티브 링크와 같은 타당한 이유로 존재하며, 많은 정식 크레이트에서 이를 포함하고 있습니다. 빌드 스크립트가 실행되는 모든 종속성은 Cargo 프로세스가 가진 권한을 이용해 임의의 코드를 실행할 수 있습니다. TrapDoor는 누군가가 이를 의도적으로 사용할 때 나타나는 현상입니다. Sui 및 Move 빌드 툴링처럼 보이도록 이름이 지어진 다음 여섯 개의 크레이트는 build.rs 누군가가 실행하는 즉시 해당 컴퓨터에서 지갑 키스토어를 검색하려면 cargo build. 이 스크립트는 발견한 모든 내용을 XOR 암호화하여 GitHub Gists로 전송했습니다. 별도의 설치 후크도, 임포트도 필요 없었으며, 단지 6개 패키지 이름 중 하나에 의존하는 프로젝트를 컴파일하기만 하면 되었습니다. 절차적 매크로는 Rust에서 또 다른 컴파일 시점 실행 경로이지만, TrapDoor는 이를 필요로 하지 않았습니다. 절차적 매크로는 매크로 확장 과정에서 컴파일러 내부에서 실행되므로, 실제로 무언가가 이를 호출할 때만 작동합니다. 빌드 스크립트는 크레이트가 빌드 그래프에 존재하는 것만으로도 실행되는데, 바로 이 점이 공격자에게 더 신뢰할 수 있는 선택지가 되는 이유입니다.

메커니즘 ~ 기간 동안 진행됩니다 패키지 관리자의 일반 제어
npm 라이프사이클 스크립트 설치 네, npm 12부터 기본적으로 차단됩니다.
파이썬의 임포트 시 실행 패키지 가져오기 아니요
Rust build.rs 모음집 아니요
Rust 절차적 매크로 매크로 확장 아니요

Cargo에는 의존성 빌드 스크립트나 매크로 확장을 거부하면서도 소비자가 의존성 트리를 구성할 수 있게 해주는 안정적이고 범용적인 스위치가 없습니다. 단 한 가지 제한적인 예외가 있습니다. 크레이트가 links key, 메타데이터를 직접 지정하여 Cargo 구성에서 해당 빌드 스크립트를 재정의할 수 있으며, 이 경우 해당 스크립트는 컴파일되거나 실행되지 않습니다. 이는 네이티브 라이브러리 심(shim)에 적용되는 사항이며, 단순히 빌드 스크립트를 포함하기로 한 임의의 종속성에는 해당되지 않습니다. cargo build --offline Cargo가 네트워크 요청을 수행하지 못하게 막으며, 커밋된 Cargo.lock 버전을 고정하지만, 두 방법 모두 이미 소스 트리에 존재하는 빌드 스크립트의 실행을 막지는 못하며, 락파일에서 고정된 버전이 악성 버전인 경우에도 도움이 되지 않습니다.

빌드 스크립트가 실행되는 위치와 접근 가능한 범위

CI 러너와 개발자 노트북에서 악의적인 build.rs 파일이 접근할 수 있는 권한
CI 실행자가 다음 작업을 수행 중입니다. cargo build 일반적으로 클라우드 역할 자격 증명, 레지스트리 토큰, 컨테이너 레지스트리 액세스 권한, 그리고 때로는 서명 키를 저장합니다. 개발자의 노트북에는 이러한 정보 대부분 외에도 SSH 키, 브라우저 세션 데이터, 그리고 아직 출시되지 않은 소스 코드가 저장되어 있을 수 있습니다. 바로 이 때문에 빌드 호스트가 해킹당할 가치가 있는 것입니다. IDC 아시아 태평양 사이버보안 서비스의 수석 연구 관리자인 사크시 그로버는, CSO Online에 밝혔다 TrapDoor가 “단 한 대의 해킹당한 워크스테이션만으로도 CI/CD 파이프라인으로 은밀하게 침투할 수 있다”는 사실이 드러난 이후, 자격 증명을 수집하는 빌드 스크립트는 침투 방법 중 하나이며, 감시되지 않는 빌드 과정에서 파일 시스템에서 직접 지갑 키스토어를 추출하는 것이 TrapDoor의 crates.io 측에서 수행한 방식이었다.

AI 코딩 보조 도구를 다루는 부분

TrapDoor가 AI 코딩 보조 도구가 읽는 CLAUDE.md 및 .cursorrules 파일을 어떻게 손상시켰는지
트랩도어(TrapDoor)가 AI 비서 파일을 조작한 것은 결코 build.rs 트릭. 이는 npm 페이로드 삽입에서 비롯된 것이었습니다. .cursorrules 그리고 CLAUDE.md 파일을 영구 저장소로 활용하며, 악성 명령어가 포함된 폭이 0인 유니코드 뒤에 숨겨져 있음, 그리고 동일한 공격자 GitHub 풀 리퀘스트 열기 관련 없는 AI 및 개발자 도구 프로젝트에 대해. 두 파일 이름 모두 AI 코딩 도구가 코드베이스를 이해하기 위해 읽는 관례이므로, 악의적으로 조작된 파일은 간접적인 프롬프트 주입에 해당합니다. 공격자의 텍스트는 어시스턴트가 데이터가 아닌 지침으로 간주하는 파일에 포함됩니다. 다만, 이 기술의 전제 조건은 “모든 AI 도구가 침해되었다”는 가정보다 더 제한적입니다. 이 기법이 성공할지는 특정 어시스턴트가 어떤 지시 파일을 발견하고, 신뢰하며, 이에 따라 행동하느냐에 달려 있습니다. 어쨌든 시도해 볼 가치가 있습니다. 리포지토리 루트 디렉터리 이외의 모든 위치에서 어시스턴트 지시 파일을 검색하고, 벤더링된 파일이나 경로 의존성 내에서 발견된 항목은 기본적으로 의심스러운 것으로 간주하십시오. 종료 코드를 반전시켜 무언가를 발견했을 때 빌드가 통과되는 대신 실제로 실패하도록 설정한다면, 이는 CI 검사로서도 효과적입니다.

if find . -name 'CLAUDE.md' -o -name '.cursorrules' \
  | grep -vE '^\./(CLAUDE\.md|\.cursorrules)$' | grep -q .; then
  echo "Unexpected AI assistant instruction file found"
  exit 1
fi

이는 인간 검토자를 속이는 것과는 다른 목표입니다. 저장 시점에 파일을 조작한다는 것은, 어시스턴트가 다음에 프로젝트를 읽을 때 해당 파일을 합법적인 명령으로 간주할 때 비로소 효과가 나타난다는 것을 의미합니다. 해결책은 주로 적용 범위에 달려 있습니다. 어시스턴트가 신뢰하는 명령 파일을 제한하면 공격 표면이 줄어들며, 특히 종속성에 의해 제어되는 경로를 제외할 경우 그 효과가 더욱 뚜렷해집니다.

크레이트를 당기면 실제로 무엇이 제거되는가

crates.io 레지스트리는 특이한 방식을 채택하고 있는데, 대체로 긍정적인 효과를 가져옵니다. 공개된 버전은 불변이며 삭제할 수 없고, ‘yank’ 기능으로만 제거할 수 있습니다. 이는 가변형 레지스트리가 겪는 특정 유형의 공격을 차단해 줍니다. 또한 이는 ‘yank’가 대부분의 사람들이 생각하는 것보다 효과가 약한 해결책임을 의미합니다.

액션 효과
버전 선택하기 새로 생성된 레졸루션에서는 이를 건너뛰고, 기존 잠금 파일에서는 계속 이를 사용합니다.
모든 버전을 삭제하세요 크레이트 이름은 계속 예약된 상태로 유지되며, 코드는 계속 다운로드할 수 있습니다.
버전 삭제하기 crates.io에서는 불가능합니다.

커밋된 버전에 이미 기록된 삭제된 버전 Cargo.lock 계속 사용됩니다. 이는 빌드가 스스로 중단되지 않도록 하기 위한 의도적인 조치이지만, 악성 버전을 고정해 둔 조직은 누군가가 실제로 잠금 파일을 수정할 때까지 해당 버전을 계속 사용하여 빌드를 진행하게 된다는 뜻이기도 합니다. 실행 cargo update --dry-run 무엇이 달라지는지 확인한 다음, 해당 작업을 실행합니다. cargo update 교체할 버전이 안전한지 확인한 후, 잠금 파일을 다시 작성하십시오.

왜 러스트에 이런 일이 필연적으로 일어날 수밖에 없었는지

공격자들은 기술의 확산을 뒤따릅니다. 지난 몇 년간 인프라, 암호학, 시스템 소프트웨어 전반에서 Rust의 역할이 확대됨에 따라, 이는 ‘발생할지 여부’가 아닌 ‘언제 발생할지’의 문제였습니다. 몇 년 전만 해도 소규모 부수 프로젝트에 불과했던 크레이트들이 이제는 운영 환경 스택의 핵심에 자리 잡고 있습니다. Rust 환경에서는 빌드 스크립트가 문화적으로 당연한 것으로 여겨지는 반면, npm에서는 포스트인스톨 스크립트가 그렇지 않습니다. 포스트인스톨 스크립트가 포함된 패키지는 검토자들의 눈길을 두 번 끌게 됩니다. build.rs 그렇지 않습니다. 코드를 생성하거나, 플랫폼 기능을 탐색하거나, 네이티브 라이브러리를 링크하는 크레이트들은 모두 이를 포함할 정당한 이유가 있기 때문입니다. 바로 그 정당한 용도 때문에 단순히 존재하는 것만으로도 build.rs 정말 미약한 신호네요. 이번 일이 TrapDoor crates.io가 악성 코드와 처음 마주한 사례는 아닙니다. 2023년에 레지스트리는 타이포스쿼팅된 크레이트 9개를 삭제했는데, 이 크레이트들은 build.rs 파일들은 호스트 메타데이터를 텔레그램 채널로 전송했고, 그리고 공급망 공격 crates.io에 새로운 패키지가 등장하는 일이 너무 빈번해져서, 개발팀은 2026년 2월부터는 매번 블로그에 글을 올리는 것을 중단했습니다. TrapDoor의 새로운 점은, crates.io가 npm과 PyPI를 동시에 강타한 조직적인 캠페인의 일환으로 등장했다는 것입니다. 이 두 플랫폼은 패키지 수가 일정 수준에 도달한 후, 몇 년 전 훨씬 더 큰 규모로 이와 유사한 상황을 겪은 바 있습니다. 타이포스쿼팅 그리고 공격자가 시간을 할애할 만한 가치가 있는 유사한 이름들. TrapDoor는 보안 팀이 이미 npm과 PyPI에 적용하고 있는 동일한 생태계 간 위협 모델에 crates.io를 포함시킵니다.

Rust 공급망의 보안 강화

Rust용 SecureChain 의존성 트리에 포함되기 전에 크레이트를 검사하여, 악성 코드를 포함한 빌드 시점 공격 패턴에 대해 소스 코드를 검토합니다. build.rs 스크립트와 절차적 매크로를 실행하고, 실패하는 것은 차단합니다. 크레이트는 공개 레지스트리에서 그대로 전달받는 대신 검증된 소스 코드로부터 재빌드됩니다. 바로 이것이 다음과 같은 패키지 앞에 반드시 설치되어야 하는 게이트입니다. sui-framework-helpers, 왜냐하면 일단 적대적인 build.rs 트리 안에 있으며, 실행 중입니다 cargo build 이미 너무 늦은 상태입니다. 또한, 관리자가 활동을 중단한 크레이트를 포함해 스택에 이미 포함된 크레이트의 취약점도 지속적으로 패치해 줍니다. TrapDoor가 당면한 문제입니다. 프로덕션용 Rust 트리에 최종적으로 포함되는 것 대부분은 속도가 더 느린 버전이며, 이 부분도 다루어 볼 가치가 있습니다. 다음을 구독하세요. TuxCare 뉴스레터 다음에 어떤 캠페인이 새로운 패키지 이름과 숨을 수 있는 새로운 생태계를 선택했을 때, 이와 같은 분석을 위해.

자주 묻는 질문

Rust 공급망 공격이란 무엇인가요?

코드나 의존성이 사용자에게 전달되기 전에 이를 손상시키는 공격으로, 대부분 crates.io에 올라온 악성 크레이트나 탈취된 크레이트를 통해 발생합니다. Rust에서 빌드 스크립트와 절차적 매크로는 결과 애플리케이션이 실행되기 전에 코드를 실행하기 때문에, 컴파일 시점에 가장 중요한 두 가지 실행 경로입니다.

build.rs란 무엇이며, 왜 위험한가요?

build.rs 이는 Cargo가 해당 크레이트를 빌드하기 전에 컴파일하고 실행하는 Rust 프로그램으로, Cargo를 실행하는 프로세스의 권한을 갖습니다. 이 프로그램은 합법적인 코드 생성 및 네이티브 링크를 위해 존재하며, 이를 포함하는 모든 의존성 프로그램은 Cargo 빌드 프로세스가 가진 권한을 바탕으로 임의의 코드를 실행할 수 있습니다.

TrapDoor에는 실제로 build.rs가 필요했던 것일까, 아니면 어떤 Rust 빌드 훅이라도 작동했을까?

build.rs 그것만으로도 충분했고, 가장 적은 노력만 들었기 때문에 6개의 크레이트에서 바로 그 방식을 사용했습니다. 절차적 매크로는 또 다른 컴파일 시간 방식이지만, 이는 해당 코드를 사용하는 측에서 실제로 매크로를 호출할 때만 실행되는 반면, 빌드 스크립트는 크레이트가 빌드 그래프에 포함되어 있다는 이유만으로 실행됩니다. 공격자들은 build.rs 그것이 그들에게 필요한 실행 지점을 제공했기 때문이다.

크레이트를 삭제하면 crates.io에서도 해당 크레이트가 제거되나요?

아닙니다. ‘yanking’을 하면 새 결의안이 해당 버전을 선택하지 못하게 되지만, 여전히 다운로드할 수는 있으며, 기존에 고정된 버전은 그대로 유지됩니다. Cargo.lock 계속 사용되고 있습니다. crates.io에서는 게시된 버전을 삭제할 수 없습니다.

'트랩도어'에 등장한 독이 든 AI 어시스턴트 파일들은 무엇이었나요?

그리고 .cursorrules 그리고 CLAUDE.md 이 파일들은 AI 코딩 도우미를 대상으로 한 지침을 숨기기 위해 너비 0의 유니코드 문자를 사용했습니다. 또한 이 캠페인은 관련 없는 프로젝트의 GitHub 풀 리퀘스트를 통해 유사한 파일을 유입시키려 시도하기도 했습니다.

요약
Rust 공급망 공격 분석과 build.rs 문제
기사 이름
Rust 공급망 공격 분석과 build.rs 문제
설명
TrapDoor는 컴파일 과정에서 지갑 키를 탈취하는 악성 build.rs 스크립트를 6개의 크레이트에 숨겨두었으나, Cargo는 여전히 이를 차단하지 못하고 있다.
작성자
게시자 이름
TuxCare
게시자 로고

4,500명 이상의 Linux 및 오픈 소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.

목차
필요한 오픈 소스 보안에 대한 답변 얻기

4,500명 이상의 Linux 및 오픈 소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.