Live A&D CTF 대회 도전기
1. 서론: 화려한 시작, 그리고 냉혹한 현실
이번에 참가한 Live Attack & Defense (A&D) CTF는 실시간으로 우리 서비스를 방어하는 동시에 타 팀의 취약점을 찾아 플래그를 훔쳐 와야 하는 치열한 공방전이었습니다.
결론부터 솔직하게 고백하자면, 우리 팀(Team 5)은 이번 대회에서 단 하나의 공격도 성공시키지 못했습니다. 다른 팀들이 플래그를 주고받으며 점수를 올리는 동안 우리 팀의 점수는 하락하기만 했습니다.
하지만 공격을 성공해서 얻은 숫자가 0이라고 해서 우리가 보낸 시간과 노력이 0이었던 것은 아닙니다. 낯선 주최 측의 프레임워크와 에러 속에서 끝까지 돌파구를 찾으려 발버둥 쳤던 삽질 기록을 담아보려고 합니다.
2. 본론: 무수한 에러 코드와 피 말리는 디버깅의 기록
대회 템플릿 코드에는 LLM(대형 언어 모델)을 결합하여 자동 공격과 방어를 수행하는 자율 에이전트 프레임워크가 심겨 있었습니다. 이 시스템을 이해하고 구동하는 것부터가 거대한 벽이었습니다.
Phase 1: 429 Too Many Requests
에이전트를 실행하자마자 우리를 맞이한 것은 OpenRouter API의 429 Too Many Requests 속도 제한 에러였습니다. 기존 템플릿에는 예외 처리가 전혀 없었기 때문에 에이전트는 초기화 단계에서 즉시 기절해버렸습니다.
로그에 찍히는 무의미한 quick sqli와 status=502 문구들을 보며, 우리는 그대로 포기하는 대신 wrapper_client.py 코드를 붙잡고 끈질기게 늘어졌습니다. API 제한을 맞더라도 멈추지 않고 10초를 기다린 후 최대 5번까지 다시 연결을 시도하는 백오프(Backoff) 재시도 로직을 직접 구현해 밀어 넣었습니다.
Phase 2: 422 Unprocessable Entity
429 제한을 뚫어내고 정밀 타격 로직을 가동했을 때, 이번에는 PoC 제출 API에서 status=422 에러가 발생했습니다.
1
status=422 resp={"detail":[{"type":"missing","loc":["body","flag_id"],"msg":"Field required","input":null}]}
팀원들과 함께 엔드포인트를 분석하며 취약점 ID를 매핑하는 구조를 세웠으나, 정작 플랫폼 서버로 데이터가 넘어갈 때 flag_id가 null로 전송되는 데이터 유실 현상이 발생하고 있었습니다.
비록 유효한 공격 성공으로 인정받지는 못했지만, agent.py, logger.py, wrapper_client.py 간의 꼬여있던 데이터 파이프라인을 한 줄 한 줄 추적하며 구조를 수리해 나갔습니다. 코드가 어디서 끊기고 연결되는지 온전히 이해할 수 있었던 값진 디버깅 경험이었습니다.
Phase 3: 방어 에이전트의 자폭과 로컬 샌드박스 검증
공격이 막히는 와중에 우리 팀의 웹 서비스마저 다운되는 악재가 터졌습니다. 자율 방어 기능을 하라던 defense_agent가 소스코드를 LLM 패치(git apply)하는 과정에서 문법 오류를 만들어내어 서버를 죽여버린 것입니다. 배포를 다시 해도 즉시 오염이 반복되는 지옥 같은 루프였습니다.
3. 결론: 참여에 둔 의미
결과적으로 이번 대회에서 점수를 단 1점도 따내지 못했기에 겉보기에는 완벽한 패배일지도 모릅니다.
하지만 아무것도 모른 채 뒤에서 대회가 끝나기를 기다리지 않았습니다. 에러 메시지 하나가 뜰 때마다 팀원들과 머리를 맞대고 코드베이스를 뒤엎었으며, 마지막 라운드의 마지막 1초까지 로컬 서버를 열고 어떻게든 에이전트를 정상 구동시키기 위해 발버둥 쳤습니다.
비록 우리의 무기가 적의 성벽을 무너뜨리지는 못했지만, 이 치열했던 삽질 덕분에 FastAPI 인프라 제어, 도커 컨테이너 디버깅, 그리고 AI 자동화 프레임워크의 한계와 실전 예외 처리 기법을 뼈저리게 학습(?)할 수 있었습니다.
이번 대회의 ‘0’은 멈춰있는 숫자가 아니라, 다음 대회를 향해 더 높게 튀어 오르기 위해 바닥을 단단히 우리 팀의 강력한 성장과정이라고 생각합니다. 끝까지 포기하지 않고 함께 밤을 새우며 코드를 분석해 준 팀원들에게 존경과 감사를 전합니다.