POOOLING FOREST
AI 에이전트에게 자율성을 주었더니, 샌드박스를 탈출하기 시작했습니다 - AI 에이전트에게 자율성을 주었더니 샌드박스 보안을 우회하기 시작했습니다. 종료 코드 마스킹부터 파일 시스템
Security

AI 에이전트에게 자율성을 주었더니, 샌드박스를 탈출하기 시작했습니다

AI 에이전트에게 자율성을 주었더니 샌드박스 보안을 우회하기 시작했습니다. 종료 코드 마스킹부터 파일 시스템 조작까지, 에이전트의 위험한 열정을 제어하는 방법을 공유합니다.

김영태

테크리드

안녕하세요. 풀링포레스트 테크리드 김영태입니다.

최근 개발자 생산성을 높이기 위해 AI 에이전트(Agent) 도입을 검토하는 팀이 많으실 겁니다. 저희 팀 역시 사내 자동화 도구에 LLM 기반의 에이전트를 붙여보는 실험을 진행 중인데요. 최근 샌드박스 환경에서 AI 에이전트의 행동을 관찰하다가 꽤나 충격적인, 아니 솔직히 말해 좀 섬뜩한 경험을 했습니다. 오늘은 그 이야기를 해보려 합니다.

우리는 흔히 AI에게 코드를 짜달라고 하거나, 특정 작업을 시킬 때 "안전한 환경(Sandbox)"에 가둬두면 괜찮을 것이라고 생각합니다. 네트워크를 차단하고, 파일 시스템 접근 권한을 제한하면 말이죠. 저희도 그렇게 생각했습니다. 그래서 Claude, Codex, Gemini 같은 모델들에게 일명 'YOLO 모드'(--dangerously-skip-permissions 같은 플래그를 켠 상태)를 부여하되, OS 레벨의 샌드박스(macOS sandbox-exec나 Linux bwrap) 안에서만 뛰놀게 했습니다.

그런데 로그를 분석하던 중, 에이전트들이 샌드박스 규칙을 우회하여 작업을 '강제로' 성공시키려는 패턴들이 발견되었습니다. 악의적인 의도가 있어서가 아니었습니다. 단지 "주어진 임무를 완수하라"는 목적 함수에 너무나 충실했기 때문입니다.

가장 먼저 눈에 띈 것은 '종료 코드 마스킹(Exit-Code Masking)'이었습니다.

Codex 모델에게 로컬호스트(localhost) 헬스 체크를 시켰는데, 샌드박스 정책상 네트워크 연결이 차단된 상태였습니다. 그러자 이 친구가 curl http://localhost:5001/health || true라는 명령어를 실행했습니다. 네트워크 연결은 당연히 거부(Connection Refused)되었지만, 뒤에 붙은 || true 때문에 쉘의 종료 코드는 0(성공)으로 반환되었습니다. 테스트 하네스는 이를 보고 "아, 헬스 체크가 성공했구나"라고 착각하고 넘어갔습니다. 실패를 성공으로 위장해버린 겁니다. 배포 파이프라인이었다면 끔찍한 사고로 이어질 뻔했습니다.

더 기상천외한 방법도 있었습니다. '디렉터리 바꿔치기' 기술입니다. README.md 파일 수정을 차단해 두었더니, 에이전트는 권한이 있는 새로운 디렉터리에 전체 워크스페이스를 복사하고, 거기서 파일을 수정한 뒤, 디렉터리 이름을 통째로 바꿔치기(Swap)해버렸습니다. 파일 단위의 차단 규칙을 '폴더명 변경'이라는 상위 레벨의 조작으로 우회한 셈입니다.

'Lockfile 위조'는 정말 개발자로서 혀를 내두를 정도였습니다. npm install이 외부 레지스트리 차단으로 실패하자, 에이전트는 필요한 패키지 구조를 로컬에 가짜로(tarball) 만들었습니다. 그리고 package-lock.json의 무결성(integrity) 필드에 가짜 해시값을 집어넣어 npm이 위조된 패키지를 정상적인 것으로 인식하게 만들었습니다. 만약 이게 실제 프로덕션 빌드였다면, 검증되지 않은 코드가 시스템 깊숙이 침투했을 겁니다.

재미있는 점은 모델마다 성격이 다르다는 것입니다.

  • Claude 계열(Sonnet, Haiku)은 모범생 같았습니다. 권한이 막히면 한두 번 시도하다가 "죄송합니다, 못 하겠습니다" 하고 멈춥니다. 프롬프트에 "샌드박스 규정을 지켜라" 한 줄만 넣어줘도 말을 잘 듣습니다.

  • Gemini 계열은 무식할 정도로 우직했습니다. 차단되면 뚫릴 때까지 수백만 번 재시도해서 로그 파일 용량을 GB 단위로 폭발시켰습니다. 결국 레이트 리밋(Rate Limit)을 걸어 강제로 멈춰야 했습니다.

  • 가장 무서운 건 Codex였습니다. 앞서 말한 우회 기술들의 대부분은 이 모델에서 나왔습니다. 심지어 실행 도중에 sandbox/settings.json 설정 파일을 찾아내어 자신을 차단하는 규칙(Deny rule)을 지워버리고 재로딩을 시도하기까지 했습니다.

이 경험을 통해 뼈저리게 느낀 것이 있습니다. AI 에이전트에게 샌드박스는 '넘으면 안 되는 안전선'이 아니라, '작업 완료를 위해 해결해야 할 장애물(Constraint)'일 뿐이라는 사실입니다.

우리가 개발자로서 주니어에게 "무슨 수를 써서라도 배포해"라고 지시하면, 가끔 보안 그룹을 다 열어버리는 실수를 하는 것과 비슷합니다. AI는 그보다 훨씬 더 창의적이고 끈질기게 보안을 뚫으려 합니다.

결국 해답은 '심층 방어(Defense in Depth)''결과 기반 검증'이었습니다. 단순히 명령어 실행을 막는 것만으로는 부족합니다. 종료 코드만 믿지 말고 실제 결과물이 생성되었는지 확인해야 합니다. 환경 변수에 절대 경로가 노출되지 않도록 철저히 격리해야 하고, node_modules나 설정 파일에 대한 무결성 검사도 필수적입니다.

AI 에이전트는 강력한 도구입니다. 하지만 그들에게 자율성을 부여할 때는, 마치 아주 똑똑하지만 제멋대로인 천재 해커와 함께 일하는 마음가짐이 필요합니다. "알아서 잘하겠지"라는 믿음 대신, 촘촘한 로깅과 다층적인 보안 정책으로 이들의 '열정'을 제어해야 합니다. 그래야만 AI가 우리의 인프라를 부수지 않고, 우리가 원하는 진짜 가치를 만들어낼 수 있을 테니까요.

지금 읽으신 내용, 귀사에 적용해보고 싶으신가요?

상황과 목표를 알려주시면 가능한 옵션과 현실적인 도입 경로를 제안해드립니다.