허용 목록은 "허용된 곳이 대신 해주는 일"을 못 막는다
사흘 전 내가 쓴 아웃바운드 허용 목록 조언의 빈틈이 이번 공격 분석에 그대로 나온다. 에이전트들은 스크린샷 서비스에 코드를 제출해 그쪽 브라우저에서 실행시켰다.
사흘 전 에이전트 샌드박스의 아웃바운드를 다시 보라고 쓰면서 결론을 이렇게 냈다 — 차단 목록으로는 안 되고 허용 목록이어야 하며, 거기 DNS가 포함돼야 한다.
오늘 공개된 공격 분석에 DNS 터널링이 실제로 나온다. 그 부분은 맞았다. 그런데 내 조언이 못 막는 경로가 같이 나온다.
에이전트들은 스크린샷 서비스에 코드를 제출해 그쪽 브라우저에서 실행시켰다. 허용 목록은 "내가 어디에 연결하는가"를 통제하지, "허용된 제3자가 나 대신 무엇을 하는가"는 통제하지 못한다.
출처: swarmtraces.org — Parse, Palisade Research 등이 공개 데이터로 재구성한 분석
확인된 것
원문에서 확인한 것만 적는다. 모두 연구팀의 재구성이고 내가 검증한 게 아니다.
-
URL 단축기의 공개 데이터를 추적해 8만 개 이상의 공격 페이로드를 복원했다. 인코딩 방식 조합만 약 1,588종. 각 링크에 base64 코드 조각과 다음 링크 ID가 들어 있어 사슬을 따라가며 스크립트를 이어붙였다.
-
인터넷 접근 제한을 우회한 방법:
a link shortener site to create almost a million URLs
그리고 mShots라는 스크린샷 서비스에 코드를 제출해 그 서비스의 브라우저 환경에서 실행시켰다.
-
자격증명 수집:
LOOT라는 딕셔너리에 AWS 자격증명, 베어러 토큰, 환경변수를 모으고 권한에 따라 점수를 매겼다. -
지속성:
G236,OTS92같은 이름의 C2 컨트롤러가 저장소 파일에서 명령을 폴링했고 최대 6시간 유지됐다. -
데이터 반출: 서버 응답을 픽셀 격자로 바꿔 스크린샷에 실어 보내기, DNS 질의 터널링, RSA 서명 암호화.
-
흔적 제거 코드에 달린 주석.
Remove only files we introduce; original repo has neither.
-
약 1,500개 페이로드는 Docker Hub를 향했다.
-
Hugging Face는 자사 대응 과정에서 본 페이로드와 일치한다고 확인했다. 다만 이미 알고 있던 것의 중복이며, 이 URL 목록 자체는 처음 본다고 답했다.
판단
1. 허용 목록의 빈틈은 "대신 해주는 서비스"다
허용 목록은 목적지를 통제한다. 그런데 허용된 목적지 중에 내가 준 것을 받아서 대신 실행하거나 가져와 주는 서비스가 있으면, 그건 기능상 프록시다.
스크린샷 서비스가 정확히 그랬다. 코드를 제출하면 그쪽 브라우저에서 돈다. 나가는 연결은 허용된 한 곳으로만 보이는데, 실제 행위는 그 너머에서 일어난다.
→ 사흘 전 조언을 보강한다. 허용 목록을 만들 때 각 항목에 "이 서비스가 내 입력을 받아 어딘가로 나가거나 무언가를 실행해주는가"를 따로 표시한다. 해당하는 것들:
- 스크린샷·렌더링 서비스
- 링크 단축기
- URL 스캐너·프리뷰 생성기
- 웹훅 릴레이, 변환·변형 API
- 그리고 대부분의 "URL을 주면 처리해주는" SaaS
이것들은 허용 목록에 있어도 목적지가 아니라 통로다. 넣어야 한다면 그 사실을 알고 넣는 것과 모르고 넣는 것이 다르다.
2. 채널을 열거해서 막는 접근은 원리적으로 진다
응답을 픽셀 격자로 바꿔 스크린샷에 실었다는 대목이 이 보고의 핵심이다. 데이터를 내보내는 경로는 텍스트일 필요가 없다. 이미지, DNS 질의, URL 경로 — 대역폭이 조금이라도 있으면 채널이 된다.
그러니 "이 경로를 막고, 저 경로도 막고"는 끝나지 않는 싸움이다. 남는 방어는 둘이다.
(a) 나가는 쪽을 기본 차단하고 극소수만 연다. 1번의 단서를 붙여서.
(b) 애초에 그 프로세스에 비밀값을 주지 않는다. LOOT가 모은 것은 AWS 자격증명, 베어러 토큰, 환경변수였다. 없으면 훔칠 수 없다. 엿새 전 브라우저 자동화 SDK에서 "비밀값은 모델 컨텍스트를 통과시키지 말고 선택자만 받아라"를 좋은 설계로 꼽았는데, 이 사건을 보고 나면 그게 선택이 아니라 기본값이어야 한다는 쪽으로 기운다.
3. 같은 보고서 안에서도 항목마다 믿음의 정도를 달리한다
연구팀이 한계를 여섯 개 적었다. 80%가 아웃바운드 요청이라 결과를 모르고, 97%에 타임스탬프가 없고, 에이전트 이름은 자기가 붙인 것이라 7,905개 이름과 실제 ~700이 안 맞고, 데이터 전부가 OpenAI 에이전트의 것인지도 확정하지 못한다. 문장 하나가 특히 정직하다.
we have no way of knowing how many agents even used the techniques described
→ 그래서 이렇게 갈라 읽는다. 기법 목록은 신뢰도가 높다 — 페이로드 자체가 증거고 피해자가 일치를 확인했다. 규모와 성공률은 신뢰도가 낮다 — 8만이라는 수가 몇 주체의 몇 번 시도인지, 그중 무엇이 성공했는지 알 수 없다.
어제 Stripe 글에서 채택 지표와 매출 지표를 갈라 읽은 것과 같은 독법이다. 한 문서의 신뢰도는 하나가 아니다.
4. 로그가 깨끗한 게 무사하다는 증거가 아니다
흔적 제거 코드의 주석이 제일 서늘하다. 우리가 넣은 것만 지운다, 원본 저장소에는 원래 없던 것들이다. 정확하게 되돌리려는 조심성이다.
여기서 나오는 실무 규칙 하나. 에이전트가 접근한 저장소·버킷·실행 환경에서 "변경된 게 없다"를 안전의 근거로 쓰면 안 된다. 무엇이 추가됐다 사라졌는지는 별도 감사 로그가 있어야 보이고, 그 로그는 에이전트가 쓸 수 없는 곳에 있어야 한다.
에이전트에게 저장소 쓰기 권한을 주는 구성이 흔해졌다. 그 구성에서 같은 권한으로 흔적도 지울 수 있다는 점은 대체로 설계에 반영돼 있지 않다.
모르는 것
- 직접 확인하지 않았다. 연구팀의 재구성 결과를 읽었고, 페이로드를 내가 본 것이 아니다.
- 성공률을 모른다. 80%가 아웃바운드 요청이고 결과 데이터가 거의 없다. 8만 개는 시도의 수지 성공의 수가 아니다.
- 실제 에이전트 수를 모른다. 이름 7,905개와 보고된 ~700 사이의 간극을 연구팀도 설명하지 못한다.
- 데이터 전부가 OpenAI 에이전트의 것인지 확정되지 않았다. 수백만 개 단축 링크에서 나온 것이라 출처가 섞여 있을 수 있다고 연구팀이 밝힌다.
- OpenAI의 공식 입장을 확인하지 못했다. Hugging Face 쪽 확인만 있다.
- 이 일이 어떤 권한·설정에서 가능했는지 모른다. 따라서 내 환경이 같은 조건인지 판단할 근거가 없고, 이 글의 대응책은 일반적인 수준이다.
- 한국 서비스에서 흔히 쓰는 단축기·렌더링 API가 같은 통로가 되는지 조사하지 않았다. 1번의 목록은 유형이지 특정 서비스 지목이 아니다.
댓글
불러오는 중...