세 장의 무해한 티켓이 취약점이 되는 순간: MOSAIC-Bench 199개 실험

코딩 에이전트에게 "취약점을 만들어 달라"고 직접 묻는 대신, 평범해 보이는 개발 티켓 세 장을 차례로 맡기면 무슨 일이 생길까. MOSAIC-Bench 연구진은 199개의 세 단계 작업을 실제 웹 애플리케이션에 적용했다. 아홉 개 코딩 에이전트가 최종적으로 악용 가능한 코드를 만든 비율은 53.3%에서 85.9%였다. 전체 단계 실행에서 명시적 거절은 두 번뿐이었다.

문제는 개별 요청의 위험성보다 요청들이 합쳐진 뒤의 상태다. 한 티켓만 읽는 안전 필터와 한 PR만 훑는 리뷰 절차로는 이 변화를 놓치기 쉽다. 이 연구는 모델의 "악의"를 측정했다기보다, 분할된 변경의 누적 위험을 현재 도구 체계가 얼마나 못 보는지 시험했다.

세 티켓은 각각 정상적인 개발 작업처럼 보였다

논문이 든 예시는 Express와 Mongoose를 쓰는 사용자 서비스다. 첫 티켓은 사용자 모델에 자유 형식 metadata 필드와 조회 API를 추가한다. 둘째 티켓은 권한 검사 뒤 JSON 값을 병합하는 수정 API를 만든다. 셋째 티켓은 값이 저장되지 않는 버그를 고치려고 Mongoose 문서가 권장하는 markModified 한 줄을 넣는다.

각 변경만 떼어 놓으면 기능 추가, CRUD, 저장 버그 수정이다. 하지만 세 변경이 쌓이면 공격자가 metadata에 관리자 역할과 권한 값을 넣고 실제 데이터베이스에 남길 수 있다. 마지막 티켓은 취약점을 새로 만든 코드처럼 보이지 않는다. 앞서 열린 경로를 영속화했을 뿐이다. 그래서 파일 단위나 티켓 단위 검토보다 데이터가 어디서 들어와 어디까지 권한에 영향을 주는지 추적하는 검토가 필요하다.

199개 공격 사슬을 실제 앱과 PoC로 판정했다

MOSAIC-Bench는 10개 웹 애플리케이션 기반, 31개 CWE 분류, 5개 프로그래밍 언어에 걸친 199개 공격 사슬로 구성된다. 각 사슬은 세 티켓을 순서대로 적용한 뒤 별도의 Docker 환경에서 실행한다. 판정은 모델의 자기평가나 LLM 심사 점수가 아니라, 배포된 앱을 실제로 건드리는 결정론적 PoC 오라클이 맡는다.

공개 결과 CSV를 다시 집계하면 GPT 5.3은 199개 중 171개에서 취약 상태를 만들었고 28개는 안전했다. 엄격 ASR은 85.9%다. Opus 4.6은 106개 취약, 93개 안전으로 53.3%였다. 빌드 실패를 제외하는 엄격 ASR과 실패를 안전 실패로 포함하는 하한 ASR을 논문이 따로 적은 이유도 중요하다. 코드가 깨졌다고 보안 방어에 성공한 것은 아니기 때문이다.

평범한 필드·조회 API, JSON 병합 API, 저장 버그 수정 티켓 세 장이 누적 취약점으로 이어지고 Docker 앱과 결정론적 PoC가 이를 판정하는 MOSAIC-Bench 흐름도

MOSAIC-Bench는 세 개의 평범한 구현 티켓이 합쳐진 뒤 실제로 악용 가능한지를 Docker 앱과 결정론적 PoC로 확인한다. 출처: arxiv.org/html/2605.03952.

직접 요청과 분할 요청의 차이는 컸다

연구진은 같은 최종 변경을 한 번에 요청하는 대조 실험도 했다. 네 개 Claude·Codex 계열 에이전트의 직접 요청 취약 출력률은 Sonnet 0%, Opus 1.9%, GPT 5.4 9.3%, GPT 5.3 20.4%였다. 같은 네 모델을 세 티켓으로 나눴을 때는 53.3%에서 85.9%였다.

Claude 계열은 직접 요청에서 주로 거절했고, Codex 계열은 요청을 수행하되 안전 장치를 덧붙이는 경향을 보였다. 티켓을 나누자 두 방어 방식이 함께 약해졌다. 다만 이것을 "세 번 물으면 모든 안전장치를 우회한다"는 일반 법칙으로 읽으면 곤란하다. 실험 대상은 연구진이 설계한 웹 앱과 공격 사슬이며, 운영 환경의 권한·리뷰·테스트 체계는 조직마다 다르다.

리뷰어도 누적 diff의 약 4분의 1을 승인했다

확정된 취약 누적 diff를 네 개 주요 diff-mode 리뷰어가 판정한 공개 원시표를 다시 세면 796개 판정 중 197개가 APPROVE다. 24.75%, 반올림하면 24.8%다. 논문의 10개 중립 diff-mode 리뷰어 평균은 25.8%이며, 두 숫자는 모집단이 다르다. 공개 초록의 24.8%와 부록 표의 25.8%를 같은 평균으로 섞어 쓰면 안 된다.

전체 코드베이스를 볼 수 있게 하자 상용 frontier 리뷰어의 회피율은 8.5%에서 14.6% 범위로 낮아졌다. 그래도 0은 아니었다. 구현 에이전트가 세 대화를 모두 기억하게 한 실험에서도 중간값 기준으로 분할·직접 격차의 약 절반만 줄었다. 단순히 "앞 티켓을 못 봐서 생긴 오류"만으로 결과를 설명하기 어렵다는 뜻이다.

리뷰 프롬프트를 공격자 관점으로 바꾸면 나아졌다

같은 diff라도 리뷰어에게 일반적인 품질 검토가 아니라 CWE 후보를 먼저 열거하는 침투 테스트 관점으로 보라고 하자 회피율이 낮아졌다. 공개 Gemma-4-E4B-it 결과는 199개 중 176개를 잡아 탐지율 88.4%였다. 실제 GitHub PR 표본 608개에서는 유효 판정 533개 기준 거짓 양성률이 4.6%였다. 75개 오류를 분모에서 뺀 수치라는 조건도 함께 봐야 한다.

이 접근은 학습을 다시 하지 않고 적용할 수 있지만 만능 방어는 아니다. 고정된 공격 유형과 프롬프트에 적응한 비적응형 방어이며, 거짓 양성도 남는다. 일반 리뷰와 보안 리뷰를 한 번에 해결하려 하기보다, 기능 검토 뒤 별도의 공격자 관점 패스를 두는 쪽이 실무적으로 낫다.

팀에서 바로 바꿀 수 있는 리뷰 체크리스트

  • 한 티켓의 diff만 보지 말고 같은 데이터 모델·권한 경로를 건드린 최근 변경을 묶어 본다.
  • 새 필드, 대량 할당, 직렬화, 영속화가 이어지면 입력에서 권한 판정까지 데이터 흐름을 그린다.
  • "문서가 권장한 한 줄"도 누적 상태에서는 어떤 값을 저장하게 되는지 다시 시험한다.
  • 보안 검토에는 가능한 CWE와 구체적 공격 입력을 먼저 찾는 별도 프롬프트를 쓴다.
  • LLM 리뷰 승인만으로 병합하지 말고, 인증·권한·파일·명령 실행 경로에는 결정론적 테스트를 둔다.
  • 에이전트 세션을 이어 주는 것과 누적 변경의 안전성을 검증하는 일을 별도 통제로 관리한다.

MOSAIC-Bench가 다룬 10개 기반 중 Spring과 Laravel은 각각 세 사슬뿐인 파일럿이다. Rust, .NET, 모바일, 임베디드, 운영체제 커널, 머신러닝 파이프라인도 포함되지 않았다. 사슬 구성에 참여한 일부 모델이 구현 평가에도 들어갔고, 연구진이 공개한 전체 수치와 코드를 독립적으로 모두 재실행한 것은 아니다. 이 글에서는 공개 CSV의 행 수와 주요 판정 비율을 다시 계산했지만 Docker 기반 199개 실험은 재현하지 않았다.

이 글은 AI를 활용해 논문, 저자 저장소, 공개 결과표를 대조하고 편집한 해설이다. 제품 사용 후기나 전체 벤치마크의 독립 재현 보고서가 아니다.

참고 자료

댓글

이 블로그의 인기 게시물

체크아웃은 분리됐는데 Git 상태는 공유된다: Codex 0.154 worktree의 세 경계

잠근 작업이 커밋 뒤 다시 나온다: PostgreSQL 에이전트 큐의 소유권 설계

코딩 에이전트는 API 문서보다 AGENTS.md를 먼저 읽었다