검토를 통과하도록 설계된 백도어
요약
의도적인 백도어는 패턴 스캐너를 회피하도록 설계되므로 기존의 코드 스캔 방식으로는 탐지가 어렵습니다. xz-utils 사례처럼 백도어는 코드 형태가 아닌 런타임 동작을 통해 드러나며, 이는 구조적 한계를 지닙니다.
핵심 포인트
- 유능한 백도어는 알려진 '나쁜 패턴'을 피하고 '좋은 코드'처럼 보이도록 설계됨
- 패턴 스캐너는 로직 버그와 의도적 백도어를 식별하는 데 구조적 한계가 있음
- xz-utils 사례는 코드 스캔이 아닌 런타임 동작 이상 징후를 통해 탐지됨
- 보안 위협은 코드의 형태(shape)가 아닌 프로비넌스(provenance)의 문제로 접근해야 함
패턴 스캐너(Pattern scanner)는 알려진 '나쁜(bad)' 형태와 일치하는 코드를 식별합니다. 의도적인 백도어는 알려진 '좋은(good)' 코드처럼 보이도록 설계됩니다. 따라서 구조적으로 패턴과 일치하지 않으며, 스캔을 통과하고 사람의 검토(human review)도 통과합니다. 이것은 스캐너의 튜닝 실패가 아닙니다. 그것은 유능한 백도어의 정의이며, BrassCoders가 의도적인 회피(evasion)를 표현 가능한 패턴이 아닌 프로비넌스 레이어(provenance layer)의 문제로 취급하는 이유입니다.
AI 어시스턴트가 실수로 작성하는 버그와 공격자가 의도적으로 심어놓는 백도어는 정반대의 문제이며, 하나를 잡아내는 도구는 구조적으로 다른 하나를 잡기에 적합하지 않은 도구입니다.
패턴 스캐너가 식별하는 것
BrassCoders는 위험하다고 알려진 패턴을 감지합니다: 문자열로 구축된 SQL 쿼리, MD5 비밀번호 해시, shell=True subprocess 호출 등입니다. 이들 각각은 '알려진 나쁜 형태(known-bad shape)'입니다. 어디에서 나타나든 보안에 취약하므로 규칙(rule)이 이를 일치시킬 수 있습니다. 백도어는 그 반대입니다. 작성자의 전체 목표는 그것이 알려진 좋은 코드처럼 보이게 만드는 것이므로, 나쁜 형태에 키를 둔 규칙은 잡아낼 것이 아무것도 없습니다.
이것은 로직 버그(logic bug)가 반대편에서 마주하는 것과 동일한 벽입니다. 로직 버그는 실수로 잘못되었지만 겉보기에는 무해해 보이는 코드이며, 백도어는 의도적으로 악의적이지만 겉보기에는 무해해 보이는 코드입니다. 둘 다 동일한 구조적 이유로 패턴 매칭(pattern matching)을 무력화합니다. 위험 요소가 형태(shape)에 있지 않기 때문에, 플래그를 표시할 형태 자체가 존재하지 않는 것입니다.
xz의 교훈
xz-utils 백도어, CVE-2024-3094는 검토와 스캐닝이 결연한 백도어를 막지 못한다는 가장 명확한 증거입니다. 악성 페이로드(malicious payload)는 읽기 가능한 소스 코드가 아닌 프로젝트의 빌드 및 테스트 아티팩트(artifacts)에 숨겨져 있었으며, 검토자나 스캐너가 식별할 수 있는 그 어떤 것과도 닮지 않도록 난독화(obfuscated)되었습니다. 이는 커밋 신뢰(commit trust)를 얻기 위해 수년간 노력해 온 유지 관리자에 의해 삽입되었으며, 모든 주요 Linux 배포판에 배포되기 불과 며칠 전에 발생했습니다.
이를 잡아낸 것은 코드 스캔이 아니었습니다. 한 Microsoft 엔지니어가 테스트 머신의 SSH 로그인이 아주 미세하게 느리게 실행되는 것을 발견했고, 이 이상 징후를 추적하여 백도어를 찾아냈습니다. 탐지 신호는 소스 패턴 (source pattern)이 아니라 _런타임 동작 (runtime behavior)_이었습니다. 페이로드 (payload)는 코드상에서는 보이지 않았으며, 오직 그것이 수행하는 동작을 통해서만 드러났습니다.
패턴 탐지 (Pattern Detection)가 구조적으로 불가능한 이유
BrassCoders가 백도어를 플래그 (flag)할 수 없는 이유는 로직 버그 (logic bug)를 플래그할 수 없는 이유와 동일합니다. 악의적인 동작은 일치시킬 만한 독특한 구조적 표식 (structural marker)을 남기지 않기 때문입니다. 만약 백도어가 알려진 악성 패턴과 일치한다면, 그것은 나쁜 백도어일 것입니다. 공격자는 배포하기 전에 이를 수정할 것이기 때문입니다. 패턴에 의한 탐지는 바로 우리가 경계해야 할 백도어들을 배제하게 됩니다. 왜냐하면 유능한 백도어들은 통과하도록 설계되기 때문입니다.
규칙을 추가하는 것으로 이 문제를 해결할 수 없습니다. 당신이 작성하는 모든 규칙은 다음 공격자에게 어떤 형태를 피해야 하는지 가르쳐주는 셈이며, 백도어는 알려진 형태를 피하는 능력에 의해 정의됩니다. 패턴 계층에서의 군비 경쟁은 정해진 결말을 가집니다. 스캐너는 부주의한 공격자는 잡아내지만 주의 깊은 공격자는 놓치게 되는데, 이는 의도적인 적대자 (adversary)를 상대할 때 정확히 반대로 작동하는 결과입니다.
방어 체계가 존재하는 곳
백도어의 비용을 실제로 높이는 통제 수단들은 소스 스캔 (source scan) 외부에서 작동합니다. 출처 (Provenance) 및 재현 가능한 빌드 (reproducible builds)를 통해 실행하는 아티팩트 (artifact)가 검토한 소스와 일치하는지 확인할 수 있으므로, 빌드 과정에 주입된 페이로드는 숨을 수 없습니다. 행동 분석 (Behavioral analysis) 및 동적 분석 (dynamic analysis)은 코드가 런타임 (runtime)에 무엇을 하는지 감시하며, 바로 이 지점에서 xz 페이로드가 마침내 가시화되었습니다. 또한 xz 공격은 유지 관리자를 대상으로 한 수년간의 사회 공학 (social-engineering) 캠페인이었기 때문에, 누가 커밋 권한 (commit rights)을 가졌는지, 신뢰가 어떻게 쌓였는지와 같은 인간 공급망 (human supply chain)에 대한 정밀 조사 또한 방어의 일부입니다.
이 중 그 어느 것도 패턴 매칭 (pattern match)이 아니며, 그것이 핵심입니다. 의도적인 백도어를 탐지한다고 주장하는 스캐너는, 바로 그 스캐너를 무력화하도록 최적화된 적대자를 상대로 근거 없는 확신을 팔고 있는 것과 다름없습니다.
BrassCoders가 잡아내는 것
BrassCoders는 AI 어시스턴트가 일정한 비율로 생성하는 우발적인 보안 취약점(insecurity)을 잡아냅니다. 즉, SQL 인젝션 (SQL injection), 취약한 암호 (weak cipher), 하드코딩된 키 (hardcoded key), 보호되지 않은 서브프로세스 (unguarded subprocess) 등이 이에 해당합니다. 이것들은 의도적인 회피가 아닌 단순하고 알려진 악성 패턴 (known-bad patterns)이며, 모든 스캔에서 결정론적 (deterministically)으로 일치합니다. AI 어시스턴트는 무언가를 숨기려 하지 않습니다. 오히려 보안에 취약한 형태를 공개적으로 작성하며, 이는 바로 패턴 탐지 (pattern detection) 기술이 구축된 목적과 정확히 일치합니다.
두 가지 위협은 서로 다른 도구가 필요하며, 각각 무엇인지 정확히 구분할 가치가 있습니다. 실수로 배포되는 부주의한 패턴들에 대해서는 결정론적 스캐닝 (Deterministic scanning)이 필요하고, 의도적으로 심어진 정교한 패턴들에 대해서는 출처 (provenance), 재현 가능한 빌드 (reproducible builds), 그리고 행동 분석 (behavioral analysis)이 필요합니다. Brass는 첫 번째 계층을 담당하며, 두 번째 계층을 직접적으로 지목합니다.
pip install brasscoders
brasscoders --offline scan /path/to/your/project
스캔은 공개적으로 작성된 보안 취약 코드를 잡아냅니다. 검토를 통과하도록 설계된 백도어(backdoor)를 처리하는 것은 출처 계층 (provenance layer)의 역할이며, 이 차이를 아는 것이 방어의 절반입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기