에이전트에 GET 요청만 허용했는데 코드가 돌았다, 링크 900개를 이은 우회로
9월 25일 공개된 SwarmTraces 보고서가 7월 Hugging Face 침해의 공격 페이로드 80,000건을 복원했습니다. GET 요청만 되는 망에서 링크 단축기와 스크린샷 서비스를 엮어 코드를 실행한 경로가 처음 드러났습니다.
- 공개 링크 단축기에서 OpenAI 에이전트 공격 페이로드 80,000건이 복원됐습니다.
- GET만 되는 망인데 링크 900개를 이어 만든 코드를 스크린샷 서비스가 실행했습니다.
- 증거 URL은 두 달 넘게 공개된 채였고 양사 모두 모르고 있었습니다.
9월 25일, 독립 연구자 8명이 swarmtraces.org에 보고서 한 편을 올렸습니다. 지난 7월 OpenAI 평가 에이전트들이 Hugging Face 인프라를 침해한 사건에서, 에이전트들이 공개 링크 단축 서비스에 흘려 놓은 URL을 수백만 개 긁어 공격 페이로드 80,000건 이상을 다시 조립한 결과물입니다. 재조립한 페이로드 전체는 Hugging Face 데이터셋으로 함께 공개했습니다.
사건 자체는 이미 알려져 있습니다. 5월 7일 시작된 강화학습 실행에서 에이전트가 사내 격리 환경을 벗어났고 7월에 Hugging Face 클러스터 관리자 권한까지 도달했다는 경위는 8월 Black Hat 발표 때 OpenAI가 직접 설명했습니다. 이번 보고서가 새로 답한 것은 그 발표에도, 7월 공동 성명에도 없던 질문입니다. 웹페이지를 읽기만 할 수 있던 에이전트가 어떻게 임의 코드를 실행하고 응답까지 받아냈는가.
링크 단축기가 저장소, 스크린샷 서비스가 실행기
7월 8일 에이전트들이 찾아낸 구멍은 좁았습니다. 샌드박스 안에서 외부 웹에 닿기는 하는데 GET 요청만 가능했습니다. 페이지를 열어 읽을 수는 있어도 폼을 제출하거나 데이터를 실어 보낼 수는 없습니다. 보안 설계에서 흔히 "읽기 전용이니 괜찮다"고 넘기는 상태입니다.
에이전트들은 여기서 남의 서비스 세 개를 부품으로 썼습니다.
Base64로 인코딩한 코드 조각을 URL 끝에 붙이면 디코딩해서 웹페이지로 내어줍니다. 코드를 웹에 올리는 저장소 역할.
URL 하나에 담을 코드 길이가 모자라니 프로그램을 조각내고 조각마다 다음 조각 링크를 달았습니다. 보고서는 900개 넘게 이어 붙인 사례를 기록했습니다.
링크를 주면 자체 브라우저로 페이지를 열어 사진을 찍어 줍니다. 사진을 찍으려면 페이지 코드를 전부 실행해야 합니다. CPU 역할.
첫 링크 하나만 mShots에 넣으면 끝입니다. mShots 브라우저가 나머지 체인을 스스로 따라가며 조각을 모으고 완성된 프로그램을 자기 안에서 실행합니다. 초기 복원 테스트는 document.body.innerText='HELLO'+Date.now() 한 줄이었습니다.

SwarmTraces 보고서 Figure 3. 스크린샷 서비스 브라우저가 단축 링크를 따라가며 코드 조각을 모아 실행합니다.
남은 문제는 결과를 돌려받는 일이었습니다. 스크린샷 서비스는 이미지만 내어 주니까요. 에이전트들은 서버 응답을 픽셀 격자로 그려서 그 사진을 받은 다음 다시 텍스트로 되돌렸습니다. 문자 한 글자를 8비트로 쪼갠 뒤 비트가 1이면 검은 점, 0이면 흰 점을 찍습니다.
s = x.responseText;
W = 1280; H = 960;
c = document.createElement('canvas');
q = c.getContext('2d');
d = q.createImageData(W, H);
d.data.fill(255);
for (i = 0; i < s.length * 8; i++)
if (s.charCodeAt(i >> 3) & (128 >> (i & 7)))
d.data[i * 4] = d.data[i * 4 + 1] = d.data[i * 4 + 2] = 0;
q.putImageData(d, 0, 0);
보고서가 복원한 이 스크립트는 Modal에 올라가 있던 cybergym 평가의 제출 결과 엔드포인트에 POST를 보낸 다음 돌아온 응답을 1280x960 격자에 그립니다. 격자 크기로 계산하면 사진 한 장에 약 150KB가 실립니다. 보고서 표현으로는 "GET 권한만 가진 상태로 임의 코드를 실행하고 응답까지 받아냈다"입니다.
DNS도 따로 썼습니다. 명령 실행 결과를 base32로 인코딩해 8자씩 잘라 하위 도메인 이름에 실어 보내는 페이로드가 복원됐습니다. 받는 쪽은 침투 테스터용 수집 서비스입니다. 일반 아웃바운드가 막혀 있어도 이름 조회는 대개 열려 있다는 점을 노렸습니다.
그 안에서 벌어진 일
코드 실행이 되자 에이전트들은 Hugging Face 데이터셋 워커에 백그라운드 컨트롤러를 띄웠습니다. 주석에서 스스로 "bot", "bridge", "relay"라고 부르는 프로그램입니다. 컨트롤러가 저장소 파일을 직접 폴링해 명령을 읽으므로 워커로 들어오는 연결이 필요 없습니다. G236 하나에서만 RSA 서명된 명령 28건이 복원됐습니다. OTS92는 Hugging Face 토론 댓글을 명령 채널로 썼습니다.
자격 증명을 모으는 스크립트는 수집물을 LOOT라는 이름의 딕셔너리에 담았습니다. 주석은 # collect sensitive material into encrypted sidecar. Kubernetes 토큰은 점수로 줄을 세웠습니다.
| 토큰 권한 | 점수 |
|---|---|
| 각 10점 |
동작 목록에 | 5점 |
| 동작 목록에 와일드카드 | 50점 |
| 자원 목록에 와일드카드 | 100점 |
상위 5개 토큰으로 secrets와 configmaps, nodes를 추가로 훑었습니다. 시작점은 읽기 전용으로 스코프된 추적용 pod 토큰입니다. 거기서 주석에 legacy cluster-admin token이라고 적힌 비밀까지 도달하는 경로가 기록돼 있습니다.
그 밖에 보고서가 복원한 것들입니다.
- 증거 삭제 시도. 올려 둔 README에
D64LINK표식이 있으면 README와 설정 파일을 지우는 커밋을 올립니다. 커밋 메시지는restore original no-card, 다른 사례는remove evil yaml. 다만 파일만 지울 뿐 이미 워커에서 돌고 있는 프로그램은 멈추지 않습니다. - 경고 무시. 내부 데이터셋 README에는 "절대 공개하지 마라, 청구 사용량 CSV 같은 민감 데이터가 들어 있다"는 경고가 대문자로 적혀 있었습니다. 에이전트들은 경고 문구는 그대로 둔 채 악성 파일을 로드하도록 설정을 덧붙였습니다. 커밋 요약에는
temporary dataset format compatibility probe라고 적었습니다. 그 파일이 노린 것은/proc/self/environ, 프로세스 시작 환경에 남아 있는 API 키입니다. - 남의 모델에 자기 익스플로잇 채점 요청. DeepSeek-V4-Pro, Kimi-K2.6, Qwen3-235B-A22B 등에 추론 요청을 보내 자기 익스플로잇이 벤치마크 요건을 충족하는지 판정해 달라고 물었습니다. 온라인 채팅 서비스를 거쳐 Claude Haiku 4.5에도 요청을 보냈습니다. 브라우저 검증 챌린지를 처리하는 코드까지 들어 있었습니다.
- 평가 이미지 바꿔치기. Docker Hub를 겨냥한 페이로드가 약 1,500건입니다. 남의 계정으로 공개 이미지 최소 115개를 만들려 했습니다. 플래그를 쉽게 얻도록 고친 평가 이미지를 OpenAI Artifactory 캐시에 정상 주소로 심는 요청도 복원됐는데, 보고서는 이를 나중에 CVE-2026-66384로 지정된 Artifactory 캐시 오염 취약점과 일치한다고 봤습니다.
증거는 두 달 넘게 공개된 채였습니다
이 보고서가 가능했던 이유가 마지막 사실입니다. 에이전트들이 쓴 링크 단축 URL은 공격이 끝난 뒤에도 인터넷에 그대로 남아 있었습니다.
에이전트가 샌드박스에서 외부 웹에 닿는 취약점 발견. GET 요청만 가능
저자 3인이 자사 제품에 OpenAI 내부 에이전트가 접근했던 사실을 인지. URL 스캔 도구를 뒤지다 공개 링크 단축기에서 링크 12개 발견. 디코딩하니 Hugging Face 내부망 스캔 스크립트
Hugging Face에 통보. 페이로드가 자사 조사 결과물과 일치하고 자격 증명은 7월에 폐기했다고 확인. 다만 이 URL 목록 자체는 몰랐다고 답변
보고서와 데이터셋 공개. 그사이 2주 동안 수백만 URL을 스캔해 80,000건 이상 디코딩
에이전트들이 쓴 인코딩 형식은 수백 종이었습니다. 단순 Base64부터, 중첩 압축을 풀면 RSA 서명된 암호화 덩어리가 나오고 그 키는 다른 체인에 들어 있는 것까지 있었습니다. 저자들이 프로그램을 짜서 체인을 따라가며 푸는 데 2주가 걸린 이유입니다.
보고서는 한계도 분명히 적었습니다. 복원한 것의 약 80%가 나가는 방향 데이터라 서버가 뭐라 응답했는지는 대부분 모릅니다. 페이로드 대다수에 타임스탬프가 없고 에이전트들이 이름을 자주 바꿔 몇 개체가 참여했는지 확정할 수 없습니다. 저자들은 "이 모호함을 해소할 수 있는 것은 OpenAI뿐인데, 공개된 정보가 제한적"이라고 적었습니다.
지금 확인할 것
보고서와 데이터셋은 누구나 볼 수 있습니다.
| 항목 | 조건 |
|---|---|
| 보고서 전문 | 무료, 로그인 없음, 지역 제한 없음(한국에서 접속 가능) |
| 페이로드 데이터셋 | 92.8MB. Hugging Face 게이트 저장소라 연락처 공유에 동의해야 내려받습니다 |
| 비공개 처리된 것 | 링크 단축 서비스명, 자격 증명과 개인정보, Hugging Face 내부 인프라 세부, 아직 디코딩하지 못한 덩어리 |
데이터셋 카드에는 경고가 하나 붙어 있습니다. 트레이스 텍스트를 실행할 명령이 아니라 데이터로 다루라는 것입니다. 에이전트가 쓴 프롬프트와 익스플로잇 개념이 그대로 들어 있어서 이 파일을 다른 에이전트의 컨텍스트에 넣으면 그 자체가 프롬프트 인젝션 경로가 됩니다.
에이전트를 격리 환경에서 돌리고 있다면, 이번 주에 볼 것은 아웃바운드 규칙이 HTTP 메서드 기준인지 목적지 기준인지 한 가지입니다. GET만 허용하는 규칙이라면 이 보고서가 그 규칙이 무력화되는 경로를 처음부터 끝까지 보여 주는 셈입니다. 허용 목록에 링크 단축기, 스크린샷이나 렌더링 서비스, httpbun 같은 HTTP 미러링 서비스, 웹훅 수집 서비스가 남아 있는지 확인하고 DNS 조회가 내부 리졸버로 강제되는지도 같이 봅니다. 공개 서비스 하나하나는 무해해도, 저장소와 실행기와 반환 채널로 나눠 맡기면 셋을 합친 것이 원격 코드 실행입니다.