1984년의 예언이 현실로: strip 도구 하나를 변조해 리눅스 전체를 감염시키다

보안Linux오픈소스공급망 공격컴파일러 백도어

데이터 소스:arXiv + Lobsters + web research

1984년, 켄 톰프슨(Ken Thompson)은 ACM 튜링상 수상 연단에 서서 컴퓨터 과학계 전체를 깊은 충격에 빠뜨린 예언을 던졌다. 소스코드가 완전히 공개되어 누구나 자유롭게 검증할 수 있는 소프트웨어라 할지라도, ‘코드 감사로는 절대 발견할 수 없는 백도어’가 존재할 수 있다는 경고였다. 악의적인 컴파일러는 다른 프로그램을 컴파일할 때 백도어를 몰래 삽입하고, 자기 자신을 컴파일할 때는 다음 세대 컴파일러 바이너리로 그 악성 로직을 그대로 복제해 넘긴다. 일단 컴파일이 완료되고 나면 악성 코드가 담긴 소스코드를 저장소에서 흔적도 없이 삭제하더라도, 백도어는 바이너리 세대를 거치며 영구히 대를 이어 전파된다.

그로부터 42년이 지난 지금, 이 서늘한 예언이 완벽하게 실체화되었다. 프랑스 텔레콤 파리(Télécom Paris)와 스웨덴 왕립공과대학교(KTH) 소속 연구진이 7월 27일 아카이브(arXiv 2607.24888)에 제출한 논문을 통해, 이러한 공격을 감행하는 데 컴파일러조차 필요하지 않다는 사실을 증명했다. 연구진이 선택한 도구는 GNU binutils에 속한 지극히 평범한 바이너리 축소 유틸리티 strip이었다. 이 도구 하나를 변조하는 것만으로, 닉스OS(NixOS) 리눅스 배포판의 부트스트랩 전체 과정에 백도어가 은밀히 복제되어 최종적으로 운영체제 내 거의 모든 실행 파일이 감염되었다.

1984년 튜링상 수상자이자 유닉스 공동 창시자인 켄 톰프슨(2019년 촬영). 출처: 위키미디어 커먼즈 사진: 1984년 튜링상 수상자이자 유닉스 공동 창시자 켄 톰프슨. 출처: 위키미디어 커먼즈

컴파일러 공격이 42년 동안 ‘어려웠던’ 이유

톰프슨이 제안했던 원래의 공격 방식에는 치명적인 전제 조건이 하나 있었다. 악성 컴파일러가 ‘자기 자신의 소스코드’를 스스로 인식해야 한다는 점이었다. 자신의 소스코드를 빌드하는 순간에만 자기 복제 코드를 생성 결과물에 주입해야 했기 때문에, 공격자는 컴파일러 소스코드의 특정 어휘나 구문 패턴을 탐지하는 까다로운 콰인(quine) 형태의 인식 로직을 작성해야 했다.

이 조건은 공격의 지속성을 극도로 취약하게 만들었다. 컴파일러 소스코드가 업데이트되거나, 대규모 리팩터링을 거치거나, 새로운 메이저 버전으로 넘어가면 패턴 탐지 로직이 어긋나면서 감염의 고리가 끊어지기 십상이었다. 이 때문에 보안 업계에서는 톰프슨의 트러스팅 트러스트 공격이 이론적으로는 훌륭하지만, 실제 소프트웨어 수명 주기에서는 오래 살아남기 힘든 환상적인 사고 실험에 가깝다고 여겨 왔다.

연구진은 이번 연구에서 이 한계를 완벽히 우회했다. 그들이 표적으로 삼은 도구는 GNU strip이었다. 이 유틸리티는 컴파일이 끝난 실행 파일에서 디버그 심볼과 심볼 테이블을 제거하는 작업만 전담할 뿐, 소스코드를 들여다보거나 생성하지 않는다.

공격의 전체 과정은 소스코드를 단 한 줄도 건드리지 않고 오직 ELF 바이너리 조작만으로 이루어진다. 소스코드가 아무리 바뀌고 리팩터링되더라도 백도어는 아무런 영향을 받지 않는다. 컴파일러 기반 공격보다 뿌리를 뽑기가 훨씬 더 어려운 백도어가 탄생한 것이다.

백도어는 어떻게 대를 이어 전파되는가

공격 경로는 닉스OS의 부트스트랩(bootstrap) 메커니즘을 파고든다. 닉스OS가 소스코드를 기반으로 시스템 전체를 빌드할 때, 빌드의 시발점은 약 20개의 사전 컴파일된 실행 파일로 이루어진 ‘바이너리 시드(binary seed)‘다. 이것이 바로 신뢰의 원천이다. 시스템은 이 시드를 출발점으로 삼아 소스코드를 단계별로 컴파일하면서 툴체인을 재구축해 나간다. 최종 표준 빌드 환경(stdenv)이 완성되면, 런타임 클로저에서는 초기 바이너리 시드를 더 이상 참조하지 않는다.

연구진의 수법은 시드 내부에 포함된 유일한 strip 바이너리를 미리 변조해 두는 것이었다. 시드의 strip이 단계별 빌드 과정에서 호출될 때, 빌드 대상인 ‘다음 세대 strip’ 실행 파일에 동일한 자기 복제 페이로드를 주입한다. 논문에서는 이를 ‘후속 에지(successor edge)‘라고 부른다. 초기 시드가 의존성 클로저에서 완전히 떨어져 나간 후에도 악성 페이로드는 살아남아 최종 표준 환경에 확고히 안착한다.

공격 설계 및 구현 개요도. 출처: arXiv 2607.24888 그림: 공격 설계 및 구현 개요도. 출처: arXiv 2607.24888

실제 nixpkgs 리비전을 대상으로 실험을 진행한 결과, 그래픽 설치 프로그램 ISO 이미지가 단 하나의 오류도 없이 성공적으로 빌드되었다. 완성된 패키지 클로저는 1,199개의 패키지 산출물과 3,799개의 사용자 실행 가능 ELF 바이너리로 구성되었으며, 총 디스크 용량은 6.16GB에 달했다.

감염 규모는 경악스러웠다. 3,791개의 명령줄 인터페이스(CLI) 실행 파일 가운데 무려 3,790개에 감염 표식이 심어졌다. 유일하게 감염을 피한 프로그램은 파이어폭스(Firefox 147.0.3)였는데, 이는 빌드 설정에 --disable-strip 옵션이 지정되어 있어 strip 실행 단계 자체를 통째로 건너뛰었기 때문이었다.

strip 과정을 생략한 소프트웨어 단 하나만이 유일한 생존자가 되었다는 사실은, 역설적으로 이 공격의 파급 범위가 얼마나 광범위했는지를 적나라하게 보여준다.

언어 무관, 모든 기능 테스트 통과

감염된 프로그램들은 현대 소프트웨어의 거의 모든 주요 언어 생태계를 아우른다. C와 C++(bash, git, sudo, curl, python3.13), 파이썬(pydoc, idle), 러스트(rsvg-convert), 고(captree), 루아(lua)까지 예외는 없었다. 소스코드를 분석하지 않고 완성된 ELF 파일만을 변조하기 때문에 프로그래밍 언어의 종류는 전혀 문제가 되지 않는다.

더욱 불안감을 안겨준 대목은 시스템 안정성 테스트 결과였다. 연구진은 가상머신 환경에서 닉스OS의 데스크톱 워크플로 전체를 실행했다. gnome-shell, mutter, nautilus 등 데스크톱의 핵심 구성 요소가 모두 변조된 strip을 거쳐 빌드되었음에도 불구하고, 단 하나의 기능 테스트도 실패하지 않았다. 감염된 시스템은 겉보기에 완벽히 정상적으로 작동했다. 바로 이 점이 이 공격을 가장 위험하게 만드는 요소다.

논문에서는 백도어의 ‘휴면(dormancy) 모드’도 함께 다루었다. 평상시에는 유해한 동작을 전혀 일으키지 않고 은밀히 다음 세대로 복제되기만 하다가, 공격자가 사전에 설정한 특정 조건이나 표적이 나타나는 순간에만 깨어나 작동하는 방식이다. 이러한 잠복형 공격은 모든 일상적인 진단과 시스템 검사를 완벽히 통과하기 때문에 탐지하기가 극단적으로 어렵다.

42년 전의 경고, 오늘날에도 여전히 방치된 현실

1984년 톰프슨의 강연 이후 컴퓨터 과학계는 ‘신뢰할 수 있는 컴파일러’를 구축하기 위한 수많은 연구를 쏟아냈다. 대표적인 대응책이 데이비드 휠러(David A. Wheeler)가 제안한 ‘다중 교차 컴파일(Diverse Double-Compiling, DDC)‘이다. 복수의 독립적인 컴파일러로 교차 검증을 수행하여 변조 여부를 가려내는 기법이지만, 안타깝게도 오늘날 주류 리눅스 배포판들은 이러한 검증 절차를 기본 빌드 파이프라인에 적용하지 않고 있다.

롭스터스(Lobsters) 커뮤니티의 기술 토론에서도 흥미로운 역사적 사실들이 조명되었다. 톰프슨은 1984년 강연 당시 이미 실제 동작하는 프로토타입을 만들어 구현해 두었으며, GNU Guix와 Nix의 stage0/hex0 프로젝트는 수십 바이트 수준의 기계어 코드부터 시작해 모든 툴체인을 소스코드만으로 다시 쌓아 올리는 ‘완전 소스 부트스트랩(full-source bootstrap)‘을 통해 이러한 위협을 원천 차단하고자 고안되었다. 또한 러스 콕스(Russ Cox)는 톰프슨의 초기 코드를 직접 구해 유닉스 V6 환경에서 되살려 냈고, 웹 브라우저 안에서 누구나 이를 직접 체험해 볼 수 있는 환경을 공개하기도 했다.

그러나 현실적인 시스템 엔지니어링의 세계는 냉정하다. 불투명한 사전 컴파일 바이너리에 대한 맹목적 신뢰를 걷어내는 완전 소스 부트스트랩은 천문학적인 개발 공수와 지속적인 유지보수 비용을 요구한다. 대다수의 주류 운영체제 배포판들이 감당하기에는 너무나 무거운 대가인 셈이다.

코드 감사로는 절대 발견할 수 없는 백도어

이번 연구가 던지는 가장 본질적인 메시지는 명확하다. “소스코드가 공개되어 있으므로 안전하다”는 믿음은 허상에 불과하다는 점이다. 철저한 소스코드 감사는 저장소 내부의 취약점이나 악성 로직을 찾아낼 수는 있지만, 바이너리 단계에서 주입되는 조작까지는 결코 탐지해 내지 못한다. 디버그 심볼을 깎아내는 단순한 유틸리티가 ELF 파일의 헤더에 몰래 몇 바이트를 덧붙이고 있으리라고는 그 누구도 의심하지 않기 때문이다.

켄 톰프슨이 1984년에 증명해 보였던 진실은, 신뢰의 고리가 한 번 무너지면 소스코드 감사만으로는 결코 그 신뢰를 되살릴 수 없다는 것이었다. 그리고 42년이 지난 오늘, 연구진은 컴파일러보다 훨씬 단순하고 훨씬 덜 의심받는 도구인 strip을 통해 그 차가운 현실을 다시 한번 증명해 보였다.

부트스트랩 연결 고리 안에 사전 컴파일된 바이너리 시드가 존재하는 한, 이 공격 통로는 언제나 열려 있다. 그리고 완전한 검증 가능성을 손에 넣기 위한 대가인 완전 소스 부트스트랩을 지불할 용의는, 오늘날 대다수의 소프트웨어 생태계에 아직 존재하지 않는다.

참고 링크:

  • arXiv 2607.24888 논문
  • Lobsters 토론 (p5w5j6)
  • Ken Thompson, Reflections on Trusting Trust (1984)