LLM을 파이프라인에 끼우고 나서 실제로 달라진 것
헌팅은 결국 커버리지 싸움입니다. 손으로 훑을 수 있는 엔드포인트 수에는 상한이 있고, 그 상한은 생각보다 낮습니다. 우리가 LLM을 파이프라인에 넣은 이유는 새로운 종류의 버그를 찾기 위해서가 아니라, 이미 아는 종류의 버그를 더 넓은 표면에서 찾기 위해서입니다.
이 구분이 중요합니다. LLM이 사람이 못 보는 취약점을 찾아 주지는 않았습니다. 사람이 볼 시간이 없던 자리를 후보로 올려 주었습니다. AI를 끼운 워크플로우로 실제 바운티 $10,000 이상을 받았지만, 그 금액의 원인은 통찰이 아니라 처리량입니다.
파이프라인 구조
세 단계로 나눠 두고, 각 단계의 출력이 다음 단계의 입력이 됩니다.
[1] 정찰 → 엔드포인트·파라미터·인증 경계 목록
[2] 코드 감사 → 의심 지점 + 근거 + 확인 방법
[3] 변형 탐색 → 확인된 패턴이 남아 있는 다른 위치
1. 정찰 — 구조화가 전부
LLM에게 “취약점을 찾아라” 라고 하면 쓸 수 없는 결과가 나옵니다. 대신 분류 작업만 시킵니다.
입력은 프록시 로그, OpenAPI 스펙, 클라이언트 번들에서 추출한 라우트 목록입니다. 출력은 고정된 스키마입니다 — 엔드포인트, HTTP 메서드, 필요한 인증 수준, 조작 가능한 식별자, 이 엔드포인트가 다른 서비스를 호출하는지 여부.
출력을 스키마로 강제하는 게 핵심입니다. 자유 서술을 받으면 검증할 수 없고, 검증할 수 없으면 신뢰도를 매길 수 없습니다. 스키마 출력은 프로그램으로 대조할 수 있습니다. “인증이 필요 없다” 고 표시된 엔드포인트에 실제로 토큰 없이 요청을 보내 보면 됩니다. 이 대조를 자동으로 돌리면 모델의 주장 중 절반 가까이가 여기서 걸러집니다.
2. 코드 감사 — 근거를 함께 요구한다
화이트박스 타겟, 즉 소스가 열려 있는 대상에서 가장 효과가 큽니다. 소스를 통째로 넣지 않고, 정찰 단계가 지목한 엔드포인트의 핸들러부터 호출 그래프를 따라가며 조각으로 넣습니다.
요구하는 출력 형식은 항상 세 칸입니다.
| 칸 | 내용 |
|---|---|
| 의심 지점 | 파일·함수·줄 |
| 근거 | 왜 문제일 수 있는지 (코드를 인용해서) |
| 확인 방법 | 이게 실제 결함인지 판별하는 구체적 절차 |
세 번째 칸이 없는 출력은 버립니다. “확인 방법” 을 쓰지 못하는 지적은 대개 모델이 코드를 읽은 게 아니라 일반론을 말한 것입니다. 이 한 칸을 요구하는 것만으로 검토할 항목이 크게 줄어듭니다.
3. 변형 탐색 — 여기가 실제로 돈이 되는 자리
결함 하나를 확인한 뒤가 가장 생산적인 구간입니다. 같은 실수는 같은 코드베이스 안에서 반복됩니다. 한 사람이 쓴 코드는 한 가지 방식으로 틀리기 때문입니다.
우리가 반복해서 긁어낸 패턴 두 가지가 홈에도 적혀 있습니다.
- 인증 프록시 패턴 — 앞단이 인증을 처리하고 뒷단은 앞단을 신뢰하는 구조. 뒷단에 직접 닿는 경로가 하나라도 남아 있으면 인증이 전부 무의미해집니다. 이 형태는 한 번 이해하면 다른 서비스에서도 같은 모양으로 보입니다.
- operation_id 리플레이 — 작업 식별자가 추측 가능하거나 재사용 가능할 때, 남의 작업 컨텍스트에 올라탈 수 있는 형태.
변형 탐색에서 LLM이 하는 일은 “이 패턴과 구조적으로 같은 코드를 찾아라” 입니다. grep으로는 안 됩니다. 변수명과 표현은 다르고 구조만 같기 때문입니다. 이 작업은 LLM이 정직하게 잘합니다.
바이너리 타겟에서는 헤드리스 디컴파일 출력을 같은 파이프라인에 태웁니다. 디컴파일 결과는 사람이 읽기 괴롭고 양이 많아서, 직관으로 찍어서 볼 자리를 고르던 작업이 자동 순회로 바뀝니다.
오탐을 어디서 걸러내는가
이게 이 글의 핵심입니다. 오탐률은 낮지 않습니다. 낮아진 적도 없습니다. 달라진 건 오탐을 사람이 읽기 전에 버릴 수 있게 된 것입니다.
순서대로 세 개의 체를 놓습니다.
- 스키마 검증 — 출력이 요구한 형식을 벗어나면 버립니다. 모델이 형식을 못 지킬 때는 내용도 못 지키는 경우가 많습니다.
- 기계적 대조 — 검증 가능한 주장은 코드로 확인합니다. “이 파라미터는 서버에서 검증하지 않는다” 는 주장은 실제로 요청을 보내면 판별됩니다. 이 단계가 가장 많이 걸러냅니다.
- 재현 요구 — 남은 후보는 사람이 재현을 시도합니다. 여기서 살아남은 것만 제보 후보가 됩니다.
2단계를 자동화하지 않으면 파이프라인 전체가 손해입니다. 검증 없이 모델 출력을 사람에게 넘기면, 사람은 원래 하던 코드 읽기 대신 모델의 서술을 검증하는 일을 하게 됩니다. 그건 더 느립니다. LLM 도입이 실패하는 지점이 거의 항상 여기입니다.
모델이 특히 자주 틀리는 것
- 도달 가능성. 함수 안의 로직 결함은 잘 찾지만, 그 함수에 외부에서 닿을 수 있는지는 자주 틀립니다. 호출 그래프 전체를 보지 못한 상태에서 추측합니다.
- 암묵적 방어. 프레임워크나 미들웨어가 이미 막아 놓은 것을 결함으로 보고합니다. 프레임워크 기본 동작을 컨텍스트에 명시하면 줄어들지만 사라지지는 않습니다.
- 인증과 인가 혼동. “인증된 사용자만 접근 가능” 을 안전하다고 판단합니다. 인가가 빠진 자리가 정확히 그런 모양입니다.
- 없는 것을 있다고 말하기. 존재하지 않는 함수명·설정 키를 자연스럽게 지어냅니다. 그래서 근거로 코드 인용을 요구합니다. 인용문이 실제 파일에 없으면 그 항목은 통째로 버립니다.
사람이 반드시 들어가야 하는 지점
자동화하지 않는 단계를 명시적으로 정해 두고 있습니다.
범위 판단. 무엇을 테스트해도 되는지는 사람이 정합니다. 프로그램 범위 문서를 모델에게 읽히고 대상을 고르게 하지 않습니다. 범위를 벗어난 테스트는 되돌릴 수 없습니다.
실제 요청 전송. 파이프라인은 후보와 절차를 만들고, 실행은 사람이 합니다. 특히 상태를 바꾸는 요청(쓰기·삭제)은 전부 수동입니다. 모델이 만든 절차를 검토 없이 돌리다가 운영 데이터를 건드리면 그건 취약점 발견이 아니라 사고입니다.
영향 판단. 결함이 실제로 무엇을 가능하게 하는지, 심각도가 어느 정도인지는 사람이 씁니다. 모델은 심각도를 일관되게 과대평가합니다.
제보문 작성. 제보문은 사람이 씁니다. 재현 절차가 실제로 재현되는지 확인한 사람만 쓸 수 있고, 민감정보 제거도 사람이 합니다. 이 부분은 라이트업 작성 규격에 따로 적어 두었습니다.
AI 제품 자체를 보는 경우
방향을 뒤집어서, LLM과 에이전트 시스템이 타겟인 경우도 봅니다. 프롬프트 인젝션, 도구 오남용, 권한 경계 우회. 보는 방식은 다르지 않습니다.
에이전트 시스템에서 흥미로운 자리는 모델이 아니라 모델과 도구 사이의 경계입니다. 모델이 도구를 호출할 때 인자를 누가 검증하는지, 도구의 출력이 다시 모델 컨텍스트로 들어갈 때 그게 데이터로 취급되는지 지시로 취급되는지. 후자가 섞이면 외부 입력이 그대로 명령이 됩니다. 결국 인가 경계 문제이고, 우리가 웹에서 보던 것과 같은 종류의 실수입니다.
정리
- LLM은 새로운 버그 종류를 찾아 주지 않습니다. 아는 종류를 더 넓게 찾게 해 줍니다.
- 출력을 스키마로 강제하고, 검증 가능한 주장은 기계로 대조하세요. 이걸 안 하면 순손실입니다.
- 오탐률은 그대로입니다. 오탐이 사람에게 도달하기 전에 죽는 비율이 달라집니다.
- 가장 이득이 큰 단계는 변형 탐색입니다. 첫 결함을 확인한 다음이 진짜 시작입니다.
- 범위 판단, 상태 변경 요청, 영향 평가, 제보문 작성은 자동화하지 않습니다.