CTF 에이전트를 위한 삼중 모달 지식 엔진 구축
Librarian 뒤에 있는 CTF 지식 검색 시스템을 어떻게 구축했는지: 정확한 페이로드를 위한 SQLite, 방법론을 위한 ChromaDB, 로컬 도구를 위한 JSON 인덱스 — 자동 폴백과 크로스 타입 향상을 통해 단일 게이트웨이로 통합됨.
Librarian이 tcache stashing에 대해 물어볼 때, 기본 Claude 모델이 알고 있는 것보다 더 유용한 것을 반환해야 합니다. 모델은 힙 익스플로이트에 대한 일반적인 이해를 가지고 있습니다. tcache가 무엇인지 설명할 수 있고, stash unlink 공격의 개념을 설명할 수 있으며, 익스플로잇의 형태를 암시할 수 있습니다. 하지만 지난주 팀원들이 사용한 특정 pwntools 관용구나 도전 바이너리에 고정된 libc 버전에서 free list 상태를 드러내는 정확한 GDB 명령을 알지 못합니다. 일반적 지식과 실행 가능한 구체적 정보 사이의 이러한 격차 — 이것이 지식 엔진이 존재하는 이유입니다.
”단순히 LLM에 물어보기”는 충분하지 않은 이유
기본 모델의 지식은 CTF 맥락에서 두 가지 실패 모드를 가집니다.
첫 번째는 오래된 정보입니다. CTF 도전은 종종 최근 CVE, 업데이트된 도구 버전, 또는 지난 해의 writeup에만 기록된 기법을 포함합니다. 학습 데이터 마감이 있는 모델은 이를 알지 못합니다. 두 번째는 정밀도입니다. GTFOBins가 nmap 권한 상승 기법을 문서화한다는 것을 아는 것은 정확한 --script=exec 구문을 붙여넣기 준비가 되어 있는 것과 같지 않습니다. 시간이 제한된 경쟁에서 “에이전트가 이론을 알고 있다”와 “에이전트가 정확한 명령을 가지고 있다” 사이의 차이는 풀이와 막힘 사이의 차이가 될 수 있습니다.
또한 컨텍스트 예산 문제가 있습니다. Librarian (Claude Haiku)은 도전 당 한 번 호출되고 고정된 컨텍스트 윈도우를 가집니다. 프롬프트에 모든 HackTricks를 임베드할 수 없습니다. 목표한 검색이 필요합니다: 이 특정 도전에 가장 관련 있는 세 가지, 빠르게 전달되고, 에이전트가 즉시 행동할 수 있는 형식으로.
삼중 모달 아키텍처
지식 베이스는 세 가지 근본적으로 다른 종류의 검색을 세 개의 별도 저장소로 분리합니다.
Type A — 근육 기억 (SQLite + FTS5)
Type A는 복사하여 붙여넣기를 원하는 명령에 대한 것입니다. 데이터베이스 (ctf_knowledge.db)는 두 개의 테이블을 포함합니다.
binaries는 GTFOBins와 LOLBAS의 ~2,739개 구조화된 레코드를 보유합니다 — 바이너리당 익스플로이트 방법당 한 행으로, 이름, 플랫폼(linux/windows), 함수(shell, sudo, suid, download 등)로 인덱싱됩니다. 이는 GTFOBins YAML 파일과 LOLBAS JSON 내보내기에서 제공됩니다. 스키마는 의도적으로 엄격합니다: name, platform, function, code, description. nmap + sudo 쿼리는 nmap이 할 수 있는 일에 대한 설명이 아닌 정확한 명령을 반환합니다.
tricks는 HackTricks와 PayloadsAllTheThings의 ~4,155개 레코드가 있는 SQLite FTS5 전체 텍스트 검색 테이블입니다. 빌드 파이프라인은 모든 마크다운 파일을 검색하고, 정규식을 사용하여 코드 블록을 추출하며, 주변 헤더를 컨텍스트로 기록합니다. PayloadsAllTheThings는 필터링됩니다: Intruder, Wordlists, Files, Images 디렉토리는 건너뛰고(이러한 자산은 Type C로 이동), 20줄보다 긴 코드 블록은 삭제됩니다 — 테이블을 스크립트 아카이브로 변환하기보다는 tricks를 복사하여 붙여넣기 준비로 유지하기 위한 의도적 선택입니다.
FTS5 쿼리는 검색 용어를 AND 연산합니다 (nmap AND sudo AND shell). FTS5가 실패할 때 — 구두점이 많은 쿼리에서 발생합니다 — 게이트웨이는 첫 번째 단어에 대한 LIKE 검색으로 폴백합니다.
Type B — Cortex (ChromaDB + BGE-M3)
Type B는 방법론, 개념, writeup을 위한 것입니다. BAAI/bge-m3 임베딩을 사용하는 ChromaDB를 사용합니다: 1024차원 벡터, 정규화됨, Apple Silicon에서 Metal (MPS) 가속.
빌드 파이프라인 (crawl_type_b.py)은 마크다운 구성 파일에서 대상 URL을 읽는 웹 크롤러로, 사이트당 최대 500페이지를 크롤링하고 텍스트를 methodology 컬렉션에 수집합니다. 페이지는 이중 줄바꿈으로 청크되고, 청크당 1,500자로 한정되며, 100자 미만의 청크는 삭제됩니다. 원본 HTML은 로컬에 캐시되므로 임베딩 모델 변경 후 인덱스를 재구축하면 재크롤링이 필요하지 않습니다.
현재 인덱싱됨: 0xdf의 블로그(머신 writeup, 실제 익스플로이트 기법) 및 pwntools 문서. ctf-wiki 저장소는 로컬로 복제되고 수집 가능하지만 별도 처리가 필요합니다. 한 가지 주의: trafilatura — 주 추출 라이브러리 — 는 docs.pwntools.com에 의해 차단되므로 크롤러는 해당 도메인에 대해 urllib로 폴백합니다.
임베딩 모델에 대한 참고: 빌드 중간에 384차원 모델에서 BGE-M3(1024차원)로 업그레이드했습니다. ChromaDB는 혼합 차원 컬렉션을 지원하지 않으므로 업그레이드에는 전체 데이터베이스 삭제 및 재구축이 필요했습니다. 빌드 스크립트는 이를 자동으로 처리하지만, 모든 임베딩 모델 업그레이드는 전체 재구축을 의미합니다.
Type C — Arsenal (JSON 인덱스)
Type C는 로컬 파일용입니다: wordlist, 웹 셸, 권한 상승 스크립트. 데이터베이스 대신 이름과 태그를 절대 파일 경로로 매핑하는 평면 JSON 인덱스 (asset_index.json)입니다.
인덱스에 있는 것: SecLists (암호 목록, 디렉토리 wordlist, 사용자명 목록, 퍼징 페이로드), PayloadsAllTheThings 웹 셸 (.php, .jsp 및 기타), PEASS-ng 사전 컴파일 바이너리 (linpeas.sh, winpeas.bat, winPEASany.exe). rockyou.txt wordlist는 사전 압축 해제되어 사용 준비가 되어 있습니다. 각 항목은 카테고리, 태그 목록, 절대 경로를 포함합니다 — Operator가 ffuf -w <path>를 실행해야 할 때, Librarian은 경로를 찾으라는 지시가 아닌 경로를 반환합니다.
pwntools와 ROPgadget과 같은 도구는 명시적으로 제외됩니다. 이는 Operator가 직접 호출하는 환경 도구입니다. 이들은 TypeD입니다 — 실행 환경에 있지만 여기서 인덱싱되지 않습니다. Type C는 실행하는 바이너리가 아닌 전송하거나 참조하는 파일을 위한 것입니다.
LibrarianGateway
게이트웨이 (librarian_gateway.py)는 세 가지 유형 모두에 대한 단일 인터페이스입니다. 쿼리 라우팅과 자동 폴백 및 향상 로직 적용이 작업입니다.
TypeA query:
FTS5 search binaries + tricks
├─ hit → return payloads
└─ miss → fallback: run TypeB semantic search, label as theory (no ready payload)
TypeB query:
ChromaDB semantic search
├─ hit → extract keywords (words >4 chars, first 3) → run TypeA lookup
│ return theory + concrete examples
└─ miss → nothing
TypeC query:
JSON substring/tag filter (name, tags, category)
→ return up to 5 matches with absolute paths
TypeA→TypeB 폴백은 실제로 가장 유용한 경로입니다. Operator가 SQLite 데이터베이스에 없는 정확한 명령을 위해 Librarian에 요청할 때, 게이트웨이는 단순히 아무것도 반환하지 않습니다 — “페이로드를 찾을 수 없지만 다음은 방법론입니다”라고 말하면서, Operator에게 처음부터 접근 방식을 재구성할 충분한 이론을 제공합니다.
TypeB→TypeA 향상은 반대 방향으로 작동합니다. 의미론적 검색이 방법론 결과를 반환한 후, 게이트웨이는 반환된 텍스트에서 키워드를 추출하고 FTS5 검색을 실행하여 이론을 설명하는 구체적 명령을 찾습니다. 이는 에이전트가 개념을 이해하지만 구문을 추측해야 하는 패턴을 피합니다.
키워드 추출은 원시적입니다: 4글자보다 긴 단어를 취하고, 처음 3개를 선택하고, FTS5를 실행합니다. 충분히 자주 작동하지만 “ROP”, “XSS”, “SQL”과 같은 짧지만 도메인 특화 용어나 nc와 같은 바이너리 이름을 놓칩니다. 이것이 가장 개선이 필요한 시스템 부분입니다.
빌드 파이프라인
처음부터 구축:
# Type A: GTFOBins YAML + LOLBAS JSON + HackTricks MD + PayloadsAllTheThings MD 파싱
python3 TypeA/build_db.py
# Type B: 구성된 사이트 크롤링, BGE-M3으로 임베드, ChromaDB에 저장
python3 TypeB/crawl_type_b.py
# Type C: SecLists / PayloadsAllTheThings / PEASS-ng 탐색, JSON 인덱스 생성
python3 TypeC/build_asset_index.py
Type A는 몇 초 내에 빌드됩니다. Type B가 느린 것입니다: BGE-M3은 모든 청크에 대해 추론을 실행하고, 0.5초 정책 지연이 있는 500페이지 사이트를 크롤링하는 데 시간이 걸립니다. 원본 HTML 캐시는 크롤링되면 캐시에서 벡터 인덱스를 재구축하는 것이 재크롤링보다 훨씬 빠르다는 의미입니다.
언급할 가치가 있는 종속성 제약: Python 3.14는 Pydantic 호환성 문제로 인해 ChromaDB 1.5.1을 중단합니다. 프로젝트는 Python 3.10–3.13을 요구합니다.
BearcatCTF에서 작동한 것
가장 명확한 신호는 카테고리 누적이었습니다. Operator가 여덟 번째 암호화 도전에 도달할 때쯤, Librarian은 충분한 인덱싱된 컨텍스트 — 이전 풀이와 자체 소스에서 — 를 가지고 있어서 그 요약은 첫 번째 도전에 주었던 것보다 훨씬 더 잘 조정되었습니다. 포렌식도 같은 패턴을 보였습니다: binwalk와 foremost는 초기 Librarian 응답에 나타났고, 도전 3 시점에 Operator는 시도 중간에 발견하는 것이 아닌 올바른 도구로 시작했습니다.
Type C는 웹 도전에 효과적이었습니다. Operator가 리버스 셸을 업로드하거나 디렉토리를 퍼징해야 할 때, Librarian은 파일을 찾으라는 지시가 아닌 절대 경로를 반환했습니다. 마찰 감소는 작지만 시간이 제한된 맥락에서 실제입니다.
아키텍처의 약점은 pwn이었습니다. Type B의 힙 익스플로이트 방법론 커버리지는 합리적입니다 — 0xdf의 writeup이 잘 커버합니다 — 하지만 Type A의 특정 pwntools 호출 커버리지는 미약합니다. 대부분의 GTFOBins 항목은 바이너리 익스플로이트가 아닌 권한 상승을 위한 것입니다. Operator는 인덱싱된 소스에서 검색하기보다는 문서에서 pwntools 보일러플레이트를 재구성해야 했습니다.
우리가 변경할 것
pwn에 대한 Type B 커버리지 개선. 0xdf 블로그와 pwntools 문서가 현재 소스입니다. CTF-wiki는 로컬로 복제되었지만 아직 수집되지 않았습니다. 이를 추가하고 잘 알려진 pwn writeup 아카이브의 목표한 크롤링을 통해 이론-페이로드 변환이 중요한 도전 카테고리에 대한 커버리지가 개선될 것입니다.
키워드 추출 수정. 현재 휴리스틱(4글자보다 긴 단어, 처음 3개)은 절대 교체되지 않은 자리 표시자입니다. 최소한의 개선은 길이 휴리스틱으로 폴백하기 전에 알려진 CTF 키워드 — CVE 번호, 바이너리 이름, 기법 이름 — 를 추출하는 것입니다.
TypeD 통합 힌트 추가. Librarian이 특정 도구 호출(ROPgadget, pwntools, gdb-peda)을 암시하는 방법론 결과를 반환할 때, 도구를 기록하고 인덱스에 없더라도 호출 패턴을 제안해야 합니다. 현재 Type B 이론 결과와 실행 환경의 TypeD 도구 간에 연결이 없습니다.
Type B에 대한 캐시 무효화. 원본 HTML 캐시에는 만료가 없습니다. 0xdf의 블로그는 새로운 writeup을 얻습니다. pwntools 문서는 새 릴리스로 업데이트됩니다. 현재 접근 방식은 변경 사항을 픽업하기 위해 캐시된 파일을 수동으로 삭제하도록 요구합니다. TTL 또는 콘텐츠 해시 확인이 이를 해결할 것입니다.
현재 형태의 엔진은 기능적이고 BearcatCTF에서 긍정적이었습니다. 또한 명확하게 첫 번째 버전입니다. 아키텍처는 올바릅니다 — 즉각적인 페이로드, 방법론, 로컬 자산 간의 3중 분할은 인간 CTF 플레이어가 실제로 다른 참조 자료를 사용하는 방식에 명확하게 매핑됩니다. 거친 모서리는 각 계층 내 채우기 및 검색 품질에 있지, 설계에 있지 않습니다.