서론
지난 기사(AI 침투 테스트 도구 'Strix' 사용 후기)에서, 자율형 AI 펜테스트 도구의 예시로 Strix를 실제로 구동하고 OpenAI를 백엔드로 한 블랙박스 진단 결과를 정리했습니다. 그 기사의 서두에서 공격자가 Strix나 Cairn 같은 침투 테스트 도구를 Hermes agent와 같은 자율 에이전트 기반 위에서 운영하는 것 같다는 이야기가 나왔는데, 이번에는 다른 하나인 'Cairn'을 실제로 사용해 보겠습니다.
Cairn은 Strix와 달리 펜테스트 전용 도구가 아니라 '범용 상태 공간 탐색 엔진'이라고 합니다. 출발점(Origin)과 목표(Goal)만 정해져 있다면, 그 사이의 미지의 경로를 AI 에이전트가 탐색하는 틀 자체를 제공하며, 침투 테스트는 그 첫 번째 검증 용도로 위치합니다. 실제로 Tencent Cloud Hackathon의 침투 테스트 부문에서는 610팀 중 54문제 전 문항을 정답 처리하여 종합 3위를 차지할 정도로 실력이 뛰어납니다.
검증 대상은 지난번과 동일하게 제가 만든 '일부러 취약점을 심어둔 EC 사이트풍의 Rails 앱'입니다. 이번에는 OpenAI가 아닌, Claude Code 구독을 그대로 사용하는 구성으로 시도했습니다.
Cairn이란
Strix가 '정찰/공격/검증을 담당하는 에이전트들이 팀으로 움직이는' 고정적인 멀티 에이전트 구성이었다면, Cairn은 조금 더 추상도가 높은 구조를 가지고 있습니다.
- Blackboard Architecture (공유 흑판 방식): Fact(확정된 사실), Intent(아직 실행하지 않은 탐색 방향), Hint(인간이 수시로 주입할 수 있는 조언) 세 가지만으로 그래프를 구성합니다. -
- 역할 고정 없음: 워커에게 '정찰 담당', '공격 담당' 같은 역할은 없으며, 그래프의 현재 상태에서 태스크가 그때그때 동적으로 생성됩니다. -
- Stigmergy (간접 협조): 에이전트들끼리는 직접 대화하지 않고, 공유된 흑판(그래프)을 통해서만 협조합니다. -
- 워커의 내용 선택 가능: Claude Code, Codex, Pi 중 어느 것을 백엔드로 사용할 수 있습니다.
시스템 구성은 Cairn Server(Fact/Intent/Hint 그래프를 유지하는 역할), Dispatcher(그래프를 읽어 태스크를 스케줄링하고 워커를 구동), Worker(실제로 프롬프트를 받아 탐색하는 Claude Code 등의 CLI)의 3단계로 나뉘어 있습니다.
Cairn Server (Fact / Intent / Hint 그래프)
↑ 폴링
Cairn Dispatcher (태스크 스케줄링, 워커 구동)
...
설정 (로컬 모드, Claude Code 이용)
Cairn에는 Docker를 사용해 각 프로젝트를 컨테이너화하는 방식과, 호스트의 CLI를 그대로 사용하는 '로컬 모드'가 있습니다. 이번에는 로컬 모드를 사용했습니다.
git clone https://github.com/oritera/Cairn.git
cd Cairn
cp dispatch.local.example.yaml dispatch.yaml
이 위치에서 터미널을 2개 열고 아래 명령어를 실행합니다.
# 터미널 1: API 서버
uv run --project cairn cairn serve
# 터미널 2: 디스패처
...
저는 iTerm2(터미널 소프트웨어)를 사용해서 화면을 분할했지만, 대략 이런 느낌입니다.
로컬 모드의 가장 큰 특징은 dispatch.yaml에 API 키가 전혀 노출되지 않는다는 것입니다. 이 머신에 이미 로그인된 claude CLI를 서브 프로세스로 호출하기만 하므로, Claude Code의 구독(Pro/Max 플랜)을 그대로 사용할 수 있습니다. Strix처럼 별도로 OpenAI의 종량제 API 키를 준비할 필요가 없습니다. 다만, 백그라운드에서 LLM 호출이 발생한다는 자체는 변하지 않으므로, 구독 이용 한도나 레이트 제한은 소모됩니다. 참고로, 로컬 모드는 샌드박스 없이 호스트 사용자 권한 그대로 동작합니다. 격리된 실험 환경 전용으로 사용해야 할 설계입니다.
프로젝트 (탐색 태스크) 투입
Cairn Server에는 간단한 Web UI(http://127.0.0.1:8000)가 내장되어 있어, 브라우저에서 프로젝트를 생성할 수 있습니다.
이와 같은 작업은 API를 통해서도 직접 수행할 수 있습니다.
curl -s -X POST http://127.0.0.1:8000/projects \
-H "Content-Type: application/json" \
-d '{
...
Web UI로 생성하든 curl로 생성하든, 내부적으로 작동하는 것은 동일한 API이므로 Dispatcher가 가져와 처리하는 내용은 변하지 않습니다. Web UI는 Fact/Intent의 그래프를 노드(node)와 엣지(edge)로 시각화해주는 뷰어(viewer)로만 사용하고, 프로젝트 자체는 익숙한 curl을 이용해 전송하는 방식으로 사용할 수도 있습니다.
진행 중에는 노드를 클릭하면 해당 Fact의 전문을 읽을 수 있으며, 'Log' 탭에서 프로젝트 생성부터 Fact 확정까지의 흐름을 시간순 로그(time-series log)로 추적할 수 있습니다.
다만 '보고서 열기(レポートを開く)' 버튼 같은 것은 없고, UI가 보여주는 것은 어디까지나 Cairn 서버가 가진 Fact/Intent의 구조화된 데이터입니다.
1차 시도: 지시 없이 실행해 보기
처음에는 아무 생각 없이, 순수한 origin과 goal만으로 프로젝트를 생성했습니다. 결과는 매우 좋았으며, 다음 내용들이 실제로 악용 가능한 PoC(Proof of Concept)로 확인되었습니다.
/invoices/:id의 IDOR (다른 사용자/관리자의 주문 열람)
/documents/download의 경로 탐색(path traversal)을 통해 master.key, 앱 소스코드 일체, 그리고 SQLite DB 파일 자체를 통째로 탈취
- 신규 등록 폼의
role 파라미터를 경유한 마스 어사인먼트(mas assignment)로, 등록만 한 신규 계정이 즉시 admin 권한을 획득
흥미로웠던 점은, 경로 탐색으로 읽어낸 소스코드로부터 에이전트가 스스로 다음 공격 가설(Command Injection, SSRF, SQLi)을 세우고 개별 Intent로 분해하여 순차적으로 검증해 나갔다는 것입니다. 마치 인간의 펜테스터가 '소스코드를 읽고 다음에 생각하는' 움직임을 그대로 재현한 것처럼 보였습니다.
반면, Fact의 내용은 모두 영어로 작성되어 있었습니다. origin/goal은 한국어로 입력했음에도 워커(worker)가 작성하는 조사 결과는 영어로 나오는 것은 이전 Strix와 동일한 현상이었습니다. 실제로 Cairn의 프롬프트 템플릿(cairn/src/cairn/dispatcher/prompts/default/*.md)을 확인해 본 결과, 지시문은 모두 영어 고정이었고 출력 언어를 지정하는 메커니즘은 어디에도 없었습니다.
한국어화 시도
Strix 기사에서의 경험( --instruction에 한국어로 작성하면 효과가 있음)과 같은 발상으로, origin 또는 goal 문장 안에 직접 '한국어로 써라'를 섞어 넣어 보았습니다.
curl -s -X POST http://127.0.0.1:8000/projects \
-H "Content-Type: application/json" \
-d '{
...
결과적으로, 이번에는 Fact와 Intent의 설명문 모두 전부 한국어로 생성되었습니다. SSRF 검증에서는 외부 URL, 루프백 주소(loopback address), 닫힌 포트, 클라우드 메타데이터 같은 주소로 요청을 시도하며 '즉시 내용이 반환됨', '즉시 연결 거부됨', '약 60초 만에 타임아웃함'이라는 응답 차이를 통해, 단순 정보 유출뿐 아니라 내부 네트워크의 포트 스캔에도 활용 가능하다는 평가까지 한국어로 작성되었습니다.
다만, 1차 시도에서는 자발적으로 VULNERABILITY_REPORT.md라는 Markdown 파일을 출력했지만, 이번 2차 시도에서는 파일이 전혀 생성되지 않았고, Fact 설명문이 보고서 내용을 겸하는 것으로 끝났습니다. 마찬가지로 출력 대상을 지정하지 않는 방식으로도 동작이 달라진다는 것, 즉 Markdown 파일 형태로 남길지 여부는 워커에게 맡겨 비결정적이라는 것을 알게 되었습니다. 파일로 확실히 받고 싶다면, goal에서 출력 대상을 명시해야 합니다.
3차 시도: goal을 다듬어 본 투입
지금까지의 지견(한국어 지시문 삽입, 출력 대상 명시, 특정 취약점에 국한하지 않고 폭넓게 탐색하도록 유도)을 모두 반영하여 goal을 구성하고 처음부터 다시 진행했습니다.
대상은 http://localhost:3000에서 구동하는 Rails의 EC 사이트인 'HuckMe Shop'입니다. 인증 없이도 접근 가능하며, 일반 회원으로 [email protected] / password123로 로그인할 수 있습니다. (중략) 모든 Fact 및 최종 보고서는 일본어로 작성해야 합니다.
결과는 18개의 Fact와 18개의 Intent를 소화하여 지시대로 report.md가 확실하게 생성되었습니다. 내용은 심어둔 취약점을 거의 완벽하게 아우를 뿐만 아니라, 저희가 예상하지 못했던 공격 체인까지 스스로 구성하고 있었습니다.
구체적으로는, 파라 스트래버스(path traversal)로 탈취한 master.key로부터 credentials.yml.enc를 복호화하여 secret_key_base를 얻고, Rails의 쿠키 서명 방식(salt・cipher・purpose)을 소스 코드에서 특정한 후, 실제 로그인된 세션 쿠키를 복호화하여 user_id만 변경하고 재암호화하는, 쿠키 위조에 의한 admin 권한 탈취를 실제 HTTP 요청으로 성공시켰습니다. 게다가 첫 시도는 실패했고, 'URL 인코딩 불일치', '위조 페이로드의 구조 부족'이라는 두 가지 원인을 구분하여 다시 도전해 성공시키는 시행착오 과정까지 보고서에 남아 있었습니다.
이는 실전에서는 master.key를 파일이 아닌 환경 변수로 주입하는 것이 정석이며, 본 검증은 그 중요성을 뒷받침하는 결과이기도 합니다.
최종적으로 보고서가 가장 중대하다고 판정한 것은 다음 4가지입니다.
- 신규 등록 폼에서의 마스 어사인먼트(Mass Assignment) 권한 상승 (
user[role]=admin의 단일 파라미터만으로 비인증 상태에서 admin 권한을 영구적으로 획득. 가장 공격 비용이 낮은 경로로 명시) -
/diagnostics/ping에서의 OS 명령어 주입 (root 권한에서의 임의 명령어 실행) -
secret_key_base 탈취에 의한 쿠키 위조를 통한 admin 권한 탈취 -
Admin::UsersController#index의 SQL 인젝션 (UNION 기반으로 모든 사용자의 SSN・카드 번호까지 추출)
SQLi 역시, 1회차는 블라인드 SQLi 탐지 수준에 그쳤던 반면, 3회차에는 UNION 기반 데이터 추출까지 깊이 들어갔으며, goal의 작성 방식에 따라 심층 분석 정도가 크게 달라진다는 것을 체감할 수 있었습니다.
Strix와의 비교
같은 대상을 가지고 양쪽을 시도해 본 느낌으로는 역할이 상당히 다르다는 인상이었습니다.
Strix는 '일단 URL만 넘기면 작동하는' 간편함과, TUI에서의 진행 상황 시각화, 보고서의 형식이 처음부터 갖춰진 완성도가 강점입니다. 반면 Cairn은 프로젝트를 만드는 방식(origin/goal 작성 방식)에 따라 아웃풋의 질이 크게 변하는, 좋든 나쁘든 소재 중심적인 툴이라는 인상을 받았습니다. 그만큼 저희가 명시하지 않은 공격 체인(쿠키 위조)을 스스로 발견하고 실증할 수 있는, 깊이 파고드는 탐색이 가능했던 것도 사실입니다.
운영 면에서는 Cairn은 로컬 모드를 사용하면 API 키 없이 기존의 Claude Code 구독에 탑승할 수 있다는 점이 Strix 대비 명확한 장점입니다. 반면, 로컬 모드는 샌드박스가 없고 호스트 권한으로 그대로 구동되기 때문에, 격리 환경을 준비할 책임은 저희에게 넘어옵니다. 또한, 탐색 과정에서 API 키의 실제 값이나 기밀 정보가 Cairn의 SQLite DB나 로컬 디스크에 평문으로 남는 점, 테스트용으로 자체 등록한 계정이 그대로 DB에 남아있는 점은 사용 후 정리해야 할 운영상의 주의점입니다.
요약
- Cairn은 펜테스트 전용 툴이 아니라, Fact/Intent의 그래프를 활용하는 범용적인 상태 공간 탐색 엔진이며, 펜테스트는 그 첫 번째 실증 용도임
- 로컬 모드라면 Docker도 API 키도 필요 없고, 기존 Claude Code 구독을 그대로 사용할 수 있음
- 출력 언어와 보고서 파일 유무도 goal/origin 작성 방식에 따라 워커의 행동이 달라짐. 확실성이 필요하다면 명시적으로 지시할 수밖에 없음
- goal을 정교하게 다듬을수록 깊이 파고드는 질이 올라가, 최종적으로 저희가 예상하지 못했던 쿠키 위조 공격 체인까지 스스로 구성하여 실증해 옴
- Strix가 '간편하고 빠르게'라면, Cairn은 '작성 방식에 따라 어디까지나 깊이 파고들 수 있다'는 사용처 분리가 느껴짐
이전에 사용했던 Strix와 이번에 사용한 Cairn, 두 가지 자율형 침투 테스트(pentest) 도구를 사용해 보았는데, 둘 다 '공격자의 시점을 선제적으로 활용한다'는 목적에는 충분히 부합하는 완성도를 보여주었습니다. 공격자가 실제로 이러한 도구들을 사용하고 있다면, 방어 측에서도 같은 수준의 도구로 자신의 애플리케이션을 주기적으로 테스트해 볼 가치가 크다고 느낍니다.
Discussion