깃허브에 이슈 하나를 등록하는 데는 계정만 있으면 된다. 로그인한 사람이 누구인지, 그 저장소에 기여한 적이 있는지는 아무도 묻지 않는다. 그런데 지난 6월, 이슈 제목란에 특정 문자열을 적어 넣는 것만으로 데이터 클라우드 기업 스노우플레이크의 내부 지라(Jira)에 접근할 수 있는 자격증명이 외부로 빠져나갔다. 8월 17일 클라우드 보안기업 위즈(Wiz)가 이 사건을 공개하면서, 개발자 커뮤니티는 하루 종일 이 이야기로 시끄러웠다.
더 눈길을 끈 대목은 이 취약점을 찾아낸 쪽도, 그리고 취약점을 만들어낸 쪽으로 지목된 쪽도 모두 AI였다는 점이다.
공격자는 위즈가 개발한 자율 공격 에이전트였고, 문제의 코드를 커밋한 기록에는 깃허브 코파일럿 오토픽스(Copilot Autofix)가 공동 작성자로 남아 있었다. 다만 이 두 번째 부분은 하루 만에 논쟁이 붙었고, 위즈는 자사 블로그를 수정했다.
무엇이 사실이고 무엇이 아직 확인되지 않았는지, 그리고 이 사건이 AI 시대의 개발 파이프라인에 남긴 숙제가 무엇인지 차례로 짚어보자.
이슈 제목 한 줄로 열린 문
문제가 발생한 곳은 스노우플레이크가 깃허브에 공개해 둔 오픈소스 저장소 snowflakedb/snowflake-connector-net이었다.
이 저장소에는 jira_issue.yml이라는 깃허브 액션(GitHub Actions) 워크플로가 설정돼 있었다.
누군가 새 이슈를 등록하면 그 내용을 받아 사내 지라에 자동으로 티켓을 만들어 주는, 오픈소스 프로젝트라면 어디에나 있을 법한 편의 기능이다. 개발팀 입장에서는 깃허브와 지라를 오가는 수고를 덜어주는 고마운 자동화였고, 실제로 그렇게 오랫동안 문제없이 돌아갔다.
구멍은 이 워크플로가 이슈 제목을 다루는 방식에 있었다. 원래 이 스크립트는 제목 문자열을 환경변수로 넘긴 뒤 jq로 파싱하는 안전한 패턴을 쓰고 있었다. 그런데 6월 18일 병합된 풀 리퀘스트(PR #1218, 커밋 4a1b8ce)가 이 부분을 셸 명령 안에 제목을 곧바로 끼워 넣는 형태로 바꿔놓았다. 겉보기에는 sed로 특수문자를 걸러내는 것처럼 보였지만, 걸러내는 시점이 이미 늦었다.
깃허브 액션의 템플릿 치환은 셸이 명령을 해석하기 전에 텍스트 단계에서 먼저 일어나기 때문이다.
결과적으로 이슈 제목에 작은따옴표 하나를 넣는 것만으로 echo 문자열을 빠져나와 그 뒤에 임의의 명령을 붙일 수 있게 됐다.
위즈가 실제로 사용한 페이로드는 지라 API 토큰을 base64로 인코딩해 외부 서버로 전송하는 curl 명령이었다.
이슈를 등록한 지 몇 초 만에, 인코딩된 자격증명이 공격자가 준비해 둔 리스너로 도착했다.
저장소에 대한 어떤 권한도, 승인 절차도 필요하지 않았다.
탈취된 토큰은 qa@snowflake.net 계정으로 인증되는 것이었고, 이 계정은 스노우플레이크 내부 지라의 엔지니어링, 보안 컴플라이언스, 버그바운티 관리 프로젝트에 읽기 권한을 갖고 있었다. 취약점이 심어진 6월 18일부터 패치된 6월 23일까지, 노출 창은 닷새였다. 스노우플레이크는 감사 로그를 정밀 분석해 그 기간 동안 해당 엔드포인트에 접근한 외부 주체는 위즈뿐이었음을 확인했다고 밝혔다.
스노우플레이크는 "즉시 조사와 조치가 이뤄졌으며, 조사 결과 무단 접근의 증거는 발견되지 않았다"고 밝혔다.
안전한 코드가 위험한 코드로 바뀐 순간
이 사건의 기술적 핵심은 의외로 단순하다. 사용자가 입력한 문자열을 데이터로 다룰 것인가, 명령의 일부로 다룰 것인가의 문제다.
원래 코드가 쓰던 방식은 제목을 환경변수에 담아 넘기는 것이었다. 이렇게 하면 셸은 그 값을 그저 하나의 문자열로만 본다.
안에 세미콜론이 있든 따옴표가 있든 명령으로 해석될 여지가 없다.
반면 바뀐 코드는 깃허브 액션의 표현식을 셸 스크립트 본문에 그대로 펼쳐 넣었다.
이때 벌어지는 일을 순서대로 보면 이렇다. 먼저 깃허브 액션이 표현식 자리에 이슈 제목 원문을 문자 그대로 붙여 넣어 완성된 스크립트를 만든다. 그다음 러너가 그 스크립트를 셸에 넘겨 실행한다. 즉 sed든 무엇이든 스크립트 안에 적힌 방어 코드는 이미 공격 문자열이 스크립트의 일부가 된 뒤에야 동작한다. 문을 잠그기 전에 이미 안으로 들어와 있는 셈이다.
정리하면 이번 취약점은 세 가지가 겹쳐 만들어졌다.
- 신뢰 경계의 붕괴 - 누구나 쓸 수 있는 이슈 제목을 신뢰할 수 있는 입력처럼 다뤘다.
- 치환 순서에 대한 오해 - 템플릿 치환이 셸 실행보다 먼저라는 사실을 놓쳤다.
- 시크릿의 과도한 노출 - 지라 토큰이 해당 스텝의 환경 전체에서 읽을 수 있는 상태였다.
더 뼈아픈 것은 두 번째 방어선마저 무력했다는 점이다. 이 워크플로에는 실행을 제한하기 위한 조건문이 걸려 있었는데, 그 조건이 검사하는 값은 풀 리퀘스트 작성자 정보였다. 그런데 이슈가 열릴 때 발생하는 이벤트에서는 이 필드가 언제나 비어 있다.
결과적으로 문지기는 세워져 있었지만 이슈 경로에서는 사실상 항상 통과 판정을 내리는 상태였다.
설정 파일 몇 줄의 어긋남이 방어를 통째로 무의미하게 만든 전형적인 사례다.
스스로 방법을 바꾼 공격 에이전트
이 취약점을 발견한 것은 위즈의 레드 에이전트(Red Agent)다. 사람이 지시한 특정 표적을 파고든 것이 아니라, 공개 저장소들의 CI/CD 설정을 훑는 일상적인 스캔 과정에서 6월 23일 이 워크플로를 집어냈다.
공격용으로 설계된 자율 AI 에이전트가 정찰부터 침투, 자격증명 탈취까지의 과정을 사람의 단계별 지시 없이 이어간 것이다.
보안 연구자들이 특히 주목한 것은 실패한 뒤의 행동이다. 에이전트는 처음에 주석 기호를 이용해 뒷부분을 잘라내는 방식으로 페이로드를 만들었는데, 셸이 문자열을 제대로 닫지 못해 예기치 않은 EOF 오류를 냈다.
사람이 짠 자동화 스크립트였다면 여기서 그대로 멈췄을 것이다. 그런데 에이전트는 반환된 오류 메시지를 스스로 읽고, 원인이 따옴표가 닫히지 않은 데 있다고 판단한 뒤, 뒤에 문자열을 정상적으로 닫아주는 구문을 덧붙이는 쪽으로 페이로드를 고쳐 재시도했다. 그리고 성공했다.
방어 쪽 성적표는 초라했다. 깃허브 어드밴스드 시큐리티는 이 워크플로 파일을 실제로 분석 대상에 넣고 검사했지만 주입 취약점을 잡아내지 못했다. 저장소에 코드 스캐닝을 켜두었다는 사실만으로 안심하기 어렵다는 뜻이다.
규칙과 패턴에 기반한 정적 분석은 익숙한 형태의 위험에는 강하지만, 템플릿 치환과 셸 해석이 겹치는 이번 같은 조합 앞에서는 쉽게 눈이 먼다.
결국 이번 사건이 보여준 것은 공격과 방어의 시간 단위가 달라졌다는 사실이다. 코드가 병합되고 닷새 만에 자율 에이전트가 그 취약점을 발견해 실제로 자격증명을 빼냈다. 다행히 이번에는 그 에이전트가 책임 있는 공개 절차를 따르는 보안 기업의 것이었고, 해커원을 통해 신고된 당일 패치가 이뤄졌으며(PR #1402), 다음 날 토큰이 교체됐다.
같은 도구가 반대편 손에 들려 있었다면 이야기는 전혀 달랐을 것이다.
위즈는 "템플릿 치환 이후 echo 문자열을 탈출해 지라 자격증명을 외부 콜백으로 유출하는 이슈 제목을 만들었다"고 설명했다.
코파일럿 책임 논쟁, 그리고 남은 숙제
사건이 알려진 첫날 헤드라인 대부분은 "AI가 만든 취약점을 AI가 뚫었다"였다. 문제의 커밋 기록에 코파일럿 오토픽스가 공동 작성자로 남아 있었기 때문이다. 오토픽스는 코드 스캐닝이 찾아낸 문제에 대해 수정안을 제안하는 기능이라, 보안을 개선하려던 자동 수정이 오히려 새 구멍을 냈다는 구도는 대단히 극적으로 읽혔다.
하지만 깃허브는 이를 정면으로 반박했다. 내부 검토 결과 해당 취약점으로 이어진 기여는 사람이 작성한 것이며 코파일럿이 코드를 쓰거나 리뷰하지 않았다는 입장이다. 위즈도 8월 18일 블로그를 수정해 불확실성을 인정했다.
오토픽스가 공동 작성자로 표기된 것은 사실이지만, 사람이 실수를 넣고 오토픽스는 그것을 잡아내지 못했을 가능성도 있다는 것이다. 따라서 현재로서는 누가 그 코드를 썼는지는 확정되지 않았다고 보는 것이 정확하다.
그런데 이 구분이 사건의 무게를 덜어주지는 않는다. 어느 쪽이 맞든, AI가 개입한 검토 단계를 거친 코드에서 교과서에 나올 법한 주입 취약점이 걸러지지 않고 메인 브랜치까지 갔다는 사실은 그대로 남는다.
초점이 'AI가 나쁜 코드를 썼다'에서 'AI가 나쁜 코드를 놓쳤다'로 옮겨갈 뿐이다.
그리고 실무자 입장에서는 후자가 오히려 더 까다로운 문제다. AI 리뷰가 통과시켰다는 사실이 사람의 경계심을 낮추는 방향으로 작용하기 때문이다.
당장 자신의 저장소에서 점검해 볼 만한 항목은 분명하다.
- 워크플로의 run 블록에 이슈·PR 제목이나 본문, 브랜치명 같은 외부 입력을 직접 펼쳐 넣고 있지 않은지 확인하고, 반드시 환경변수를 거쳐 전달한다.
- 이슈·PR 이벤트로 실행되는 워크플로의 권한과 시크릿 범위를 최소한으로 줄이고, 필요한 스텝에만 토큰을 노출한다.
- 실행 조건문이 해당 이벤트에서 실제로 값을 갖는 필드를 검사하는지 직접 확인한다. 비어 있는 필드를 보는 조건은 있으나 마나다.
AI 코딩 도구는 이미 되돌릴 수 없는 흐름이고, 생산성 측면의 이득도 분명하다.
다만 이번 사건은 자동 생성이든 자동 검토든 그 결과물이 사람의 최종 확인을 대체할 수는 없다는 점을 비싼 값에 상기시켰다.
공격 쪽은 이미 자율 에이전트를 굴리며 공개 저장소를 상시로 훑고 있다.
방어 쪽 역시 도구 도입만으로 안심할 것이 아니라, 그 도구가 무엇을 못 보는지까지 알고 있어야 하는 시대가 됐다.
'IT 뉴스 & 정보' 카테고리의 다른 글
| 코덱스로 5년 일을 2주에 끝낸 아사나 (4) | 2026.08.24 |
|---|---|
| 하버드 중퇴생의 칩, 몸값 30조 된 이유 (4) | 2026.08.23 |
| 10대용 챗GPT 출시, 나이까지 추측한다 (6) | 2026.08.21 |
| 스트라이프가 10조에 산 AI 모델 관문 (3) | 2026.08.20 |
| 미국이 민간 보안기업에 내준 해킹 면허 (6) | 2026.08.19 |