1차 공개 · 8월 22일에 1차 강의가 모두 공개됩니다.
TECH 으로 돌아가기
TECH HACKER NEWS 오늘 7분 읽기 27 READS

컴파일만 해도 실행되는 악성코드: Rust arrayref 크레이트 공급망 침해 사례

컴파일만 해도 실행되는 악성코드: Rust arrayref 크레이트 공급망 침해 사례
SOURCE IMAGE · HACKER NEWS

2026년 8월 20일, Rust 생태계에서 널리 쓰이는 arrayref 크레이트의 변조된 버전이 crates.io에 올라왔다. 문제의 0.3.10 버전은 proc-macro1이라는 타이포스쿼팅 크레이트를 새 의존성으로 추가했고, 이 크레이트의 빌드 스크립트가 프로젝트 컴파일 도중 원격 바이너리를 내려받아 실행했다. 핵심은 실행 시점이 빌드 타임이라는 데 있다. 문제의 버전을 끌어온 프로젝트를 그냥 컴파일하기만 해도 페이로드가 작동한다. crates.io 팀은 이후 악성 버전을 삭제했다.

진짜 arrayref와 append-only-vec는 droundy라는 계정이 관리해 왔는데, 이 계정이 탈취된 것으로 보인다. 현재 github.com/droundy/arrayref와 append-only-vec는 물론 droundy 계정 전체가 404를 반환해 상류 코드조차 확인할 수 없는 상태다. 악성 proc-macro1을 배포한 계정은 dtolney로, Rust 매크로 개발자들에게 잘 알려진 David Tolnay의 실제 계정 dtolnay와 한 글자 차이로 흡사하다. 크레이트 메타데이터는 저자를 David Tolnay로 위조하고 repository 경로도 존재하지 않는 dtolnay/proc-macro1을 가리킨다.

정상 크레이트로 위장한 설계

주의할 점은 proc-macro1이 매크로 작성자들이 실제로 의존하는 proc-macro2와 다른 크레이트라는 사실이다. 공격자는 proc-macro2 소스를 그대로 복사한 뒤 문자열을 기계적으로 치환해 proc-macro1로 만들었다. 문서 링크와 복사된 이슈 번호까지 그대로 바뀌어 있을 정도다. 라이브러리 코드 자체가 진짜 proc-macro2이므로 빌드는 정상적으로 통과하고, 그만큼 이상 징후를 눈치채기 어렵다. 다만 정상 proc-macro2에는 없는 build-dependencies가 붙어 있었다. base64 디코딩, TLS 스택, HTTP 클라이언트를 제공하는 세 크레이트인데, 토큰 파싱 라이브러리에는 어울리지 않는 조합이다.

악성 로직은 proc-macro1 1.0.107의 빌드 스크립트에 담겼다. 서버 주소를 base64 조각으로 쪼개 저장했다가 빌드 시점에 재조립하는 방식이라 원문 소스에 문자열이 그대로 노출되지 않는다. 복원된 주소는 23.254.165.112 대역의 페이로드 호스트와 C2 주소다. 다운로드에는 어떤 인증서든 통과시키는 TLS 클라이언트가 쓰인다. rustls의 인증서 검증 트레이트에서 모든 검사를 성공으로 되돌리는 검증기를 심어, 원시 IP에 붙은 자체 서명 인증서도 그대로 받아들인다.

은폐와 분리 실행

빌드 스크립트는 운영체제와 아키텍처에 맞춰 바이너리를 골라 받고, 지원하지 않는 타깃이면 빌드를 중단한다. 실행 지점은 정상 proc-macro2 설정 로직보다 앞선 main 초입이며, 이를 막는 기능 플래그나 환경 변수 검사가 전혀 없다. 유닉스에서는 /tmp/rust-setup에 파일을 쓰고 실행 권한을 준 뒤 C2 주소를 인자로 넘겨 기다리지 않고 실행하며, 표준 입출력은 모두 null로 보낸다. 윈도우에서는 받은 코드를 %TEMP%의 PowerShell 스크립트로 저장하고 VBScript 런처를 wscript.exe로 숨긴 채 띄운다. WScript를 경유하는 이유는 Cargo의 잡 오브젝트를 벗어나기 위해서이며, 마지막에 자식 핸들을 의도적으로 누수시켜 빌드가 끝나도 페이로드가 계속 돌아가게 만든다.

특히 눈여겨볼 대목은 배포 방식이다. 계정 소유자는 정상이던 0.3.5부터 0.3.9까지를 모두 yank 처리했다. Cargo는 yank된 크레이트에 대해 yank되지 않은 버전으로 갱신하라는 경고를 띄우는데, 이 경고가 결과적으로 개발자를 유일하게 남은 비-yank 버전, 즉 악성 0.3.10으로 밀어 넣는다. RustSec 권고를 제출한 신고자 역시 바로 이 경로로 감염을 겪었다고 밝혔다.

실무자가 짚어야 할 지점

arrayref는 직접 쓰지 않더라도 의존성 그래프 깊숙한 곳에 들어가 있는 경우가 많다. tiny-skia, sctk-adwaita, winit을 거쳐 egui, eframe, iced 기반 GUI 작업 대부분의 하위 의존성으로 자리한다. 누적 다운로드는 약 2억 4,500만 건에 이르고 그중 정상 버전인 0.3.9가 1억 5,200만 건가량을 차지한다. 다만 이 수치는 크레이트가 얼마나 널리 쓰이는지를 보여줄 뿐, 실제로 감염된 빌드 수를 뜻하지는 않는다.

이번 사건이 던지는 실무적 교훈은 분명하다. 정상 코드가 그대로 담겨 빌드가 성공하더라도 안전하다는 뜻은 아니라는 점, 그리고 build.rs 같은 빌드 스크립트는 사실상 임의 코드 실행 통로라는 점이다. 의존성 추가는 arrayref 소스가 proc-macro1을 참조하지 않아도 매니페스트 한 줄만으로 성립하며, Cargo는 선언된 비-옵션 의존성을 코드 사용 여부와 무관하게 모두 빌드한다. 따라서 lockfile 고정, 새 의존성 도입 시 매니페스트 diff 점검, CI 빌드 환경의 아웃바운드 네트워크 차단, 그리고 유지관리자 이름을 흉내 낸 타이포스쿼팅 경계가 현실적인 방어선이 된다. yank 경고가 오히려 함정으로 쓰일 수 있다는 사실도 함께 기억해 둘 만하다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://safedep.io/arrayref-proc-macro1-rust-build-time-malw...
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...