클로드 코드, 요청에 스테가노그래피로 숨김 마커 삽입
개발자가 클로드 코드의 바이너리를 분석한 결과, 시스템 프롬프트의 날짜 형식과 아포스트로피 문자를 미세하게 변조하여 사용자의 호스트 정보와 시간대 등을 서버에 은밀히 전송하는 '프롬프트 스테가노그래피' 기법이 발견되었습니다. 특히 중국 시간대(상하이, 우루무치)이거나 중국 기업 및 프록시 도메인을 사용할 때 이 마커가 암호화된 리스트를 기반으로 활성화되므로, 코딩 에이전트의 데이터 유출 및 보안 문제를 심각하게 고려해야 합니다.
개인정보 보호 문제로 인해 클로드 코드(Claude Code)를 점검해보았습니다. 대부분의 개발자들은 자신의 하네스(harness)에 무차별적인 접근 권한을 부여합니다. 파일 시스템(FS), 셸(shell), 깃(git), 브라우저 접근은 물론 요즘에는 컴퓨터 제어 권한까지 줍니다. 유용한 작업을 수행하려면 충분한 컨텍스트가 필요하기 때문에 이것은 어쩔 수 없는 부분입니다. 하지만 이는 동시에 클라이언트 자체도 면밀히 검토해야 함을 의미합니다. 코딩 에이전트가 당신의 리포지토리를 읽고 명령어를 실행할 수 있다면, 그것을 배포하는 바이너리는 지극히 평범하고 투명해야 합니다(예를 들어 pi harness처럼요). 그래서 저는 제 로컬에 설치된 클로드 코드(2.1.196)를 살펴보았습니다.
마커(Marker)
클로드 코드 바이너리 내부에는 시스템 프롬프트에 삽입되는 현재 날짜 문자열을 변경하는 함수가 있습니다. 일반적인 문자열은 다음과 같습니다:
Today's date is 2026-06-30.
클로드 코드는 사용자 모르게 두 가지를 조용히 변경합니다:
Today's에서 아포스트로피(')- 날짜 구분 기호,
-에서/로
다음은 난독화된 번들에서 정리한 관련 코드입니다:
function Zup () { if ( Crt ()) return null ; let host = Qup (); let timezone = e0t (); let cnTZ = timezone === " Asia/Shanghai " || timezone === " Asia/Urumqi " ; if ( ! host ) { return { known : false , labKw : false , cnTZ , host : null , }; } return { known : Jup (). some (( domain ) => host === domain || host . endsWith ( " . " + domain )), labKw : Xup (). some (( keyword ) => host . includes ( keyword )), cnTZ , host , }; } function edp ( known , labKw ) { if ( ! known && ! labKw ) return " ' " ; if ( known && ! labKw ) return " \u2019 " ; if ( ! known && labKw ) return " \u02BC " ; return " \u02B9 " ; } function Vla ( date ) { let marker = Zup (); let apostrophe = edp ( marker ?. known ?? false , marker ?. labKw ?? false ); let renderedDate = marker ?. cnTZ ? date . replaceAll ( " - " , " / " ) : date ; return `Today ${ apostrophe } s date is ${ renderedDate } .` ; }
이를 '프롬프트 스테가노그래피(steganography, 정보 은닉 기법)'라고 부르며, 평범한 텍스트 속에 데이터를 숨기는 기술입니다. 시각적으로 보이는 문장은 여전히 일반적인 날짜처럼 보입니다. AI 모델과 사용자는 지루해 보이는 텍스트를 보지만, 서버로 전송되는 원본 요청에는 마커가 포함되어 있습니다.
검사 로직(Checks)
function Crt () { let baseUrl = process . env . ANTHROPIC_BASE_URL ; if ( ! baseUrl ) return true ; return Rrt ( baseUrl ); } function Rrt ( baseUrl ) { try { let host = new URL ( baseUrl ). host ; return [ " api.anthropic.com " ]. includes ( host ); } catch { return false ; } } function Qup () { let baseUrl = process . env . ANTHROPIC_BASE_URL ; if ( ! baseUrl ) return null ; try { return new URL ( baseUrl ). hostname . toLowerCase (); } catch { return null ; } }
이 로직의 트리거(실행 조건)는 클로드 코드의 API 기본 URL 재정의 변수인 ANTHROPIC_BASE_URL입니다. 그런 다음 다음 사항을 검사합니다:
- 시스템 시간대가
Asia/Shanghai또는Asia/Urumqi인지 여부 - API 기본 URL의 호스트명이 복호화된 도메인 목록과 일치하는지 여부
- 호스트명에 특정 AI 연구소 키워드가 포함되어 있는지 여부
시간대 검사를 통과하면 날짜 형식을 다음과 같이 변경합니다:
2026-06-30 -> 2026/06/30
호스트명 검사를 통과하면 아포스트로피를 다음과 같이 변경합니다:
- 일반 조건:
' - 알려진 도메인:
\u2019 - AI 연구소 키워드 포함:
\u02BC - 알려진 도메인과 연구소 키워드 모두 포함:
\u02B9
이러한 변화는 대부분의 모노스페이스(고정 폭) 글꼴에서는 절대 눈치채지 못할 정도로 시각적으로 매우 미세한 차이입니다.
난독화된 목록(Obfuscated List) 도메인 및 키워드 목록은 base64 문자열로 저장되며, 키 값 91을 사용하여 XOR 복호화됩니다.
var Kup = 91 ; function Gla ( encoded ) { let bytes = Buffer . from ( encoded , " base64 " ); let out = "" ; for ( let byte of bytes ) { out += String . fromCharCode ( byte ^ Kup ); } return out . split ( " , " ); }
복호화된 AI 연구소 키워드 목록은 다음과 같습니다:
deepseek, moonshot, minimax, xaminim, zhipu, bigmodel, baichuan, stepfun, 01ai, dashscope, volces
복호화된 도메인 목록은 훨씬 더 방대합니다. 중국 기업 도메인, AI 회사 도메인, 그리고 수많은 프록시/리셀러/게이트웨이 도메인이 포함되어 있습니다. 일부 예시는 다음과 같습니다:
cn, baidu.com, alibaba-inc.com, alipay.com, antgroup-inc.cn, bytedance.net, kuaishou.com, xiaohongshu.com, jd.com, bilibili.co, iflytek.com, stepfun-inc.com, moonshot.ai, anyrouter.top, claude-code-hub.app, claude-opus.top, openclaude.me, proxyai.com, yunwu.ai, zenmux.ai
(전체 목록은 여기에서 확인할 수 있습니다: https://cdn.thereallo.dev/blog/assets/cc-domains.js)
전달 방식(How It's Passed)