가이드

스캐닝 & Issues

gori에는 수동 테스트와 나란히 돌아가는 자동 분석 기능이 있습니다. Probe는 트래픽에서 이슈를 감시하고, Param Miner는 숨은 입력을 발견하며, Issues는 결과를 트리아지하는 곳입니다.

Probe: 스캐너

Probe는 보안 이슈를 유형과 심각도로 묶습니다. 패시브 체크는 브라우징하는 동안 실행되며(추가 요청은 전혀 없이) History 플로우와 Repeater 전송 결과를 검사합니다.

액티브 체크는 의도적으로 light-touch로 설계되었습니다. 이미 캡처한 트래픽에 대해 안전하고 저용량인 프로브 몇 개를 보낼 뿐입니다. 기본적으로 안전한 메서드(GET / HEAD)만 프로브하고, 고유한 표면마다 한 번씩만 테스트하며, 액티브 모드를 활성화하기 전에는 아무것도 나가지 않습니다. 흔적을 최소로 남기면서 빠른 직감을 확인하도록(파라미터가 반사되는지, origin이 허용되는지) 만들어졌습니다.

안전하지 않은 메서드(POST / PUT / PATCH / DELETE)를 다시 보내면 서버 상태가 변경될 수 있으므로 항상 명시적으로 켜야 합니다. 플로우별 Run active scan 팝업에서 unsafe methods를 체크해 한 번만 의도적으로 재전송하거나, Probe를 AGGRESSIVE 모드로 전환하면 안전하지 않은 메서드도 자동으로 프로브하고 룰별 상한을 높입니다(더 넓은 파라미터 집합, 더 넓은 forbidden-bypass 헤더 집합). 두 경우 모두 프로젝트 스코프 안에서만 동작하므로 스코프를 벗어난 호스트는 절대 건드리지 않습니다.

심각도와 범주로 묶인 패시브 이슈를 나열하는 gori Probe 스캐너: 허용적 CORS, 누락된 CSP와 HSTS, 쿠키 플래그 문제, 캐시 가능한 응답, 각각 영향받는 호스트 표시
Probe는 브라우징하는 동안 패시브 이슈(CORS, 쿠키 위생, 누락된 보안 헤더, 정보 노출)를 심각도와 범주로 묶어 드러냅니다.
범주 다루는 내용
headers 보안 헤더(HSTS, CSP·report-only-only, XFO, Permissions-Policy 등), 평문 Basic 인증, http://로 제출된 비밀번호 또는 http://로 제공된 로그인 폼, 혼합 콘텐츠, 캐시 가능한 API 응답, 공유 캐시가 저장할 수 있는 Set-Cookie, MIME 타입 혼동(nosniff 없이 스니핑 가능한 타입으로 나가는 HTML 본문, text/html로 제공되는 JSON), JWT 취약점(alg:none, 비표준 alg, exp 없음), integrity 없는 크로스 오리진 서브리소스
cookies Secure / HttpOnly / SameSite 및 관련 쿠키 위생
tech 기술 및 프로토콜 핑거프린트(Project 탭에도 표시)
infoleak 본문 노출, URL / WS 프레임의 비밀 값, GraphQL introspection, 프로덕션 스크립트에 딸려 나간 소스맵, 디렉터리 리스팅, JWT 페이로드의 민감한 클레임, 클라이언트에 노출된 설정·진단 파일(.env, .git/config, phpinfo(), .htpasswd, wp-config 자격증명, Spring actuator env), 프로덕션에서 닿는 프레임워크 디버그 모드와 대화형 디버거(Symfony, Werkzeug/Flask, Django, Laravel, Rails, ASP.NET), 의심되는 서브도메인 테이크오버, 응답 헤더에 적힌 내부 호스트명 또는 RFC 1918 주소, 그리고 쿠키·파라미터·hidden 필드에 실린 네이티브 직렬화 블롭(Java, .NET BinaryFormatter/ViewState, PHP) — 안전하지 않은 역직렬화 표면
cors 와일드카드 / null origin / 자격 증명 관련 오설정; Vary: Origin 없이 캐시된 반사 origin; 액티브 origin 반사
client 페이지·번들 스크립트의 클라이언트 사이드 의심 지점: DOM 기반 XSS(소스가 싱크로 흐름), DOM 클로버링, 프로토타입 오염, postMessage 취약점. 휴리스틱이므로 확인이 필요한 단서로 다루세요
active light-touch 프로브로 확인됨: 반사되는 파라미터, backslash-powered 주입 지점, 오픈 리다이렉트, CRLF/응답 헤더·호스트 헤더 인젝션, 접근 제어 우회(위조된 클라이언트 IP / 경로 정규화 / URL-rewrite 헤더), NGINX alias·파라미터 경로 탐색, 서버 사이드 템플릿 인젝션(SSTI), Next.js 서버 액션 인가 누락(Next-Action 요청을 세션 쿠키/Authorization 제거 후 재전송 — 액션은 POST라 unsafe/AGGRESSIVE 필요). 에러 기반 SQL 인젝션(쿼리 파라미터마다 구문을 깨는 페이로드를 붙이고, 깨끗한 baseline에는 없는 데이터베이스 오류 서명이 프로브 응답에만 나타나면 보고), 그리고 대역 외 로 확인하는 블라인드 SSRF — URL 파라미터를 OAST 페이로드로 향하게 하고 서버가 콜백을 걸면 발견으로 올립니다. 블라인드 OS 커맨드 인젝션도 같은 방식으로 확인합니다 — 명령/진단 파라미터(cmd, ping, host 등)에 셸 브레이크아웃 페이로드를 덧붙이고, 서버의 셸이 OAST 리스너에 콜백을 걸면 발견으로 올립니다. GraphQL introspection도 액티브로 확인됩니다(infoleak에 기록)

액티브 룰 둘은 나머지와 다르게 동작하며, Rules 서브탭이 그 행에 그렇다고 적어 둡니다.

심각도는 info, low, medium, high, critical 순입니다. 헤드리스 gori run probe는 기본적으로 패시브 체크를 실행하며, --active 플래그를 추가하면 액티브 체크도 함께 수행합니다.

탐지 결과는 체크와 호스트로 묶이므로 한 행이 수십 건의 히트를 대표할 수 있습니다. 행을 열면 AFFECTED URLS 목록이 그 근거입니다. /로 목록을 이동하고 Enter를 누르면 해당 URL이 캡처된 플로우가 History와 같은 상세 화면으로 열립니다. o는 그 탐지의 샘플 플로우를 열고, r은 Repeater로 보내며, y는 선택한 URL(선택이 없으면 전체)을 복사합니다.

분석은 헤드리스로도 실행할 수 있습니다. 기본값은 이미 캡처된 것(History + Repeater 응답)만 읽고 아무것도 보내지 않으며, --active를 주면 프로브 요청을 보냅니다.

gori run probe                       # passive issues
gori run probe --active              # include active checks (sends probe requests)
gori run probe --active --unsafe     # also re-send unsafe methods (may mutate server data)
gori run probe --active --aggressive # wider caps + unsafe methods (authorized targets only)
gori run probe --severity high       # only high-severity
gori run probe --category cors       # a single category
gori run probe -q 'host:example.com' # filter History with QL (Repeater still scanned)

Param Miner

Miner는 서버가 받아들이지만 드러내지 않는 파라미터를 발견합니다. 플로우를 지정하면 쿼리 문자열, 폼 본문, multipart/form-data, JSON(중첩 객체와 배열 루트 포함), 헤더, 쿠키 등 여러 위치에서 후보 이름을 프로브하고, 추측을 효율적으로 버킷으로 묶어 응답을 변화시키는 것들을 보고합니다. multipart도 대상이지만 기본은 꺼져 있습니다(캡처된 파일 파트가 요청마다 다시 전송되기 때문). --locations multipart 또는 해당 체크박스로 켜세요.

gori run mine <flow-id> \
  --locations query,headers \
  --wordlist params.txt \
  --bucket 50

마이닝은 CPU가 아니라 지연 시간에 묶여 있습니다. 버킷을 보내고 기다리고, 이분 탐색하고 또 기다리므로 비용의 대부분이 왕복 횟수입니다. 이를 줄이는 장치가 두 가지입니다. 프로브 사이에 연결을 재사용하고(프로브마다가 아니라 워커마다 TCP, https라면 TLS 핸드셰이크를 한 번씩만 치릅니다. 대상이 연결 단위로 동작한다면 reuse connections 체크박스나 --no-keep-alive, keep_alive: false로 끕니다), 모든 location을 하나의 워커 풀에서 함께 처리합니다. 그래서 location 세 개가 하나의 세 배가 되지 않고, 이분 탐색의 끝자락이 혼자 돌지도 않습니다.

Miner 탭은 기본적으로 숨겨져 있습니다. 필요할 때 커맨드 팔레트(Ctrl-P)에서 활성화하세요.

Discover: 스파이더 & 브루트포스

Miner가 숨은 입력값을 찾는다면, Discover는 숨은 엔드포인트를 찾습니다. 링크를 따라가며 사이트를 스파이더링하고(직접 눌러보지 않은 링크까지), 링크되지 않은 디렉터리와 경로(/admin, .git/config, /api/v2)를 브루트포스합니다. 새로 생긴 Target 탭 아래 Sitemap 옆의 서브탭으로 존재하며, 찾아낸 엔드포인트는 모두 그 Sitemap으로 바로 반영됩니다.

지금 있는 자리에서 바로 실행하세요. Sitemap 노드나 History 플로우에서 Space를 눌러 Discover here를 고르면 됩니다. 작은 팝업에서 탐색 방식(spider, bruteforce, 또는 기본값인 둘 다), 최대 깊이, 크롤 스코프, 동시성을 선택합니다. 실행은 백그라운드에서 진행됩니다. 하단 바에서 상태를 확인하고, Discover 서브탭에서 일시중지하거나 멈추며(^X 중지, p 일시중지), 완료 알림에서 결과로 바로 이동할 수 있습니다.

실행 결과는 URL 목록만 남지 않습니다. 각 발견 항목은 Discover가 보낸 요청과 서버가 돌려준 응답까지 함께 저장되므로, FINDINGS 표에서 Enter(또는 o)를 누르면 History와 동일한 상세 화면에서 그 교환을 그대로 볼 수 있습니다. 헤더, 본문, JSON 정렬 보기까지 같고, 거기서 ^R로 Repeater에 바로 보낼 수도 있습니다. 헤드리스 실행과 MCP 실행도 같은 바이트를 저장하므로 gori run showget_flow로도 열립니다.

Discover는 실제 사이트에서 오탐/미탐을 낮추도록 설계했습니다:

각 실행은 FP/FN 수치를 보고합니다. 캘리브레이터가 억제한 프로브 수, 트랩 가드가 잘라낸 탐색량, 그리고 남긴 결과의 신뢰도 분포입니다.

헤드리스로는 gori run discover이며, MCP로 에이전트에도 노출됩니다(discover_start / discover_status / discover_results / discover_stop):

gori run discover --target https://target.example \
  --max-depth 3 \
  --extensions php,json,bak \
  --format jsonl

Discover는 대상에 실제 요청을 보냅니다. 테스트 권한이 있는 시스템에만 실행하세요.

Sitemap에서 SpaceSend to Repeater도 제공합니다. 선택한 엔드포인트의 캡처된 요청을 Repeater 워크벤치에서 엽니다.

Issues

Issues는 트리아지 목록입니다. 추적할 가치가 있는 것이라면 무엇이든(Probe, Fuzzer, Miner, 또는 직접 검사한 결과에서) 심각도와 상태를 붙여 이슈로 승격하고, 증거 플로우로 바로 되돌아갈 수 있습니다. 이슈는 리포트용으로 익스포트할 수 있습니다.

gori run issues --format markdown --export report.md
gori run issues --format sarif --export issues.sarif   # GitHub code scanning / CI 대시보드용

TUI에서는 ⇧E가 형식을 먼저, 저장 경로를 그 다음에 묻습니다. SARIF result가 무엇을 담는지는 리포트 내보내기를 참고하세요.

⇧X(또는 SpaceX)는 탭을 비웁니다 — 프로젝트의 모든 이슈를 노트·CVSS 점수·증거 링크까지 함께. 먼저 확인을 묻고 총 개수를 밝히며, 그 개수는 필터가 보여주는 행도 표시(mark)한 집합도 아닌 프로젝트 전체입니다. ⇧X는 History·Probe·Authorize·ACTIVITY 피드가 각자의 탭에서 답하는 그 clear-all 키와 같습니다.

이슈 표시하기 (다중 선택)

History와 같은 방식으로 표시합니다. t를 누르면 커서의 이슈를 **표시(mark)**하고 아래로 한 칸 이동하므로, t를 연달아 누르면 연속된 행이 표시됩니다. Shift-↑ / Shift-↓는 시작점에서 연속 범위를 확장하고, Shift-T는 현재 필터가 보여주는 전부를 표시하며, Esc는 표시를 모두 해제합니다. 표시된 행은 거터 막대가 굵어지고, 필터 줄에 3 marked 카운트가 실시간으로 표시됩니다.

Shift에서 손을 떼면 범위 선택이 끝납니다. 그냥 / (또는 PgUp / PgDn, 다른 행 클릭)를 누르면 GUI 목록이 하이라이트를 접듯 범위를 되돌려 줍니다. tShift-T로 직접 찍은 표시는 남으므로, 떨어진 항목들을 골라 담는 것도 가능합니다. 마우스 휠은 스크롤만 하므로 표시를 지우지 않습니다.

표시는 space 메뉴가 무엇에 작용하는지를 바꿀 뿐, 동작 목록 자체를 바꾸지 않습니다.

실제 대상은 표시가 있으면 표시된 항목 전부, 없으면 커서 행입니다.

그래서 / status:open severity:lowShift-TSpacecFalse positive로 한 번에 전체를 재분류할 수 있습니다. 메뉴 제목은 SPACE · 3 MARKED로 바뀌고 항목 이름도 함께 바뀌므로(Delete 3 issues), 일괄 작업이 예상 밖으로 일어나지 않습니다.

동작 표시된 항목에 대해
심각도 설정 Space s 한 번 고르면 표시된 모든 이슈에 기록
상태 설정 Space c 한 번 고르면 끝 — false positive / resolved 일괄 처리
삭제 Space d 전체 집합에 확인 창 하나

표시는 필터 변경, 재정렬(직접 바꾼 심각도로 인한 재정렬 포함), 탭을 떠났다 돌아오는 동안에도 유지됩니다. 카운트 칩이 지금 화면 밖에 몇 개가 있는지 알려줍니다. 이슈를 열면 동작은 그 이슈 하나에 고정됩니다 — 표시는 목록 차원의 개념입니다. 익스포트는 항상 전체 리포트를 쓰므로, 표시가 있을 때 메뉴 항목에 (all)이 붙습니다.

Notes & Comparer

분석을 거드는 도구가 두 가지 더 있습니다.

동작
/ 요청끼리 / 응답끼리 비교 전환
n / ⇧N 다음 / 이전 변경 행으로 점프 (순환하며, 푸터에 3/8 표시)
f 동일 구간을 ⋯ N unchanged lines ⋯로 접기 (변경 지점 주변 3줄은 유지)
/ , ⇧↑ / ⇧↓ 행 커서 이동 · 행 단위 선택 확장
y 선택 영역 — 없으면 diff 전체 — 을 unified 텍스트로 복사
⇧← / ⇧→ 두 열을 함께 가로 스크롤
s A ⇄ B 교환

변경된 행에서는 실제로 다른 부분만 빨강/초록으로 강조되고 양쪽이 공유하는 부분은 흐리게 표시됩니다. 재서명된 토큰이나 JSON 값 하나가 바뀐 경우를 줄 전체를 읽지 않고도 찾을 수 있습니다.

Diff: 지난번 대비 뭐가 바뀌었나

Comparer는 메시지 두 개를 비교합니다. 리테스트는 같은 질문을 한 단계 위에서 던집니다 — 지난 엔게이지먼트 이후 뭐가 바뀌었나 — 그리고 그게 Target 아래 Diff 서브탭입니다. 폴딩을 그대로 빌려 쓰는 Sitemap 바로 옆에 있습니다.

슬롯에는 플로우가 아니라 프로젝트가 들어갑니다. a로 기준(이전 엔게이지먼트)을 고르고, b는 지금 열어 둔 프로젝트가 기본값이며, s로 교환하고 r로 다시 읽습니다. 아무것도 보내지 않습니다 — 양쪽 모두 캡처된 트래픽입니다. 행은 엔드포인트이고, (또는 o)를 누르면 선택한 엔드포인트의 양쪽 캡처가 Comparer로 넘어가 바이트 단위 답을 보여줍니다.

엔드포인트 동일성 판정이 전부입니다. 엔게이지먼트 두 번이 같은 식별자를 캡처하는 일은 없으므로, 리터럴 경로로 키를 잡으면 모든 행이 removed 한 번 added 한 번으로 두 번 보고되고 아무 말도 하지 못합니다. 그래서 Sitemap이 그리는 폴딩 템플릿을 그대로 키로 씁니다 — /users/{uuid}, /items/{n}, 쿼리 변형이 접힌 /search. 폴딩은 양쪽의 합집합에 대해 한 번만 돌기 때문에, 한쪽에서만 임계치를 넘긴 라우트도 반대쪽과 매칭됩니다.

판정은 다섯 가지이고, 마지막 두 개의 구분이 핵심입니다:

판정 의미
added B에는 캡처됐고 A에는 없음
gone 양쪽 모두 캡처됨 — 그런데 A는 도달 가능했던 반면 B가 받은 응답은 전부 404/410
changed 양쪽 모두 캡처됨. 상태 클래스, 인증, content type, 크기 중 하나가 허용 범위를 넘어 움직임
same 양쪽 모두 캡처됐고 동등함
not seen A에는 있는데 B는 그 엔드포인트로 요청을 아예 안 함 — 커버리지 공백이지 삭제의 근거가 아님

리테스트가 얕으면 방문한 엔드포인트도 적어집니다. "안 가봤다"를 "사라졌다"로 뭉뚱그리면 짧은 오후 작업이 대규모 수정처럼 보고되므로, 둘은 서로 다른 판정이고 그 단서는 스크롤로 사라지지 않는 헤더에 붙어 있습니다. 양쪽의 플로우·엔드포인트·호스트 수가 숫자 옆에 함께 나오는 것도 같은 이유입니다.

changed 판정은 바이트 동일성이 아니라 허용 밴드로 내립니다 — Repeater의 minimize와 Miner가 쓰는 바로 그 캘리브레이션입니다. 그래서 캡처 사이에 길이가 흔들리는 페이지는 변화 없음으로 읽힙니다. 상태 코드는 클래스로 비교하므로 200201은 발견이 아니고 200403은 발견이며, 후자는 별도의 auth 축으로 보고됩니다.

v는 판정 렌즈를 순환시킵니다. 렌즈가 무엇을 보여주든 개수는 항상 다섯 판정 전부를 덮습니다. 헤드리스로는 gori run diff이고 — --format md면 산출물에 그대로 붙여 넣을 섹션이 나옵니다 — MCP로는 diff_projects입니다.

행은 탭 밖으로 나가라고 있는 것입니다. 리테스트의 산출물은 발견 목록이고 이 탭은 그 입력을 만드는 곳이므로, ⇧F는 선택한 엔드포인트를 Issue로 기록하고 nNote로 기록합니다 — 같은 텍스트, 한쪽은 폼 없이, 둘 다 목록에서 커서를 옮기지 않습니다. 양쪽 모두 이 탭만 아는 것을 싣습니다: 어느 두 프로젝트를 비교했고 그 DB가 어디인지, 양쪽이 실제로 무엇으로 답했는지, 어느 축이 움직였는지. 열려 있는 프로젝트 쪽 캡처는 복사가 아니라 증거로 링크되고, 반대쪽 플로우는 자기 DB 경로와 함께 이름으로 남습니다 — entity_links는 프로젝트 경계를 넘지 못하기 때문입니다. not seen 행은 info로 열리고, 새 캡처가 그 엔드포인트에 요청을 보낸 적이 없다는 사실을 자기 문장으로 적습니다. 관측하지 않은 삭제를 주장하지 않습니다. 같은 문장이 --format json의 모든 행에도 실리므로, 행마다 이슈를 만드는 에이전트도 이 구분을 잃지 않습니다.

이슈, 노트, 리피터, 퍼즈/마이너 세션은 서로 링크할 수 있어, 이슈에서 증거 플로우나 그것을 만든 세션으로 바로 점프할 수 있습니다. 이슈는 선택적으로 CVSS 벡터나 점수를 기록할 수 있고, Severity는 거기서 따라옵니다 — 목록에도, cvss:>=7 필터에도, 모든 리포트에도 반영됩니다. 이슈에서 SpaceSet CVSS(또는 이슈 폼의 cvss 행에서 )를 누르면 계산기가 열립니다. vector: 행에 벡터를 직접 입력하거나 붙여넣어도 되고 8.8 같은 점수만 적어도 되며, 아래 기본 메트릭을 ←/→로 골라 만들어도 됩니다. 양쪽은 서로 맞춰 갱신됩니다. version: 행에서 3.14.0을 고를 수 있고, 두 버전은 각자의 선택을 따로 기억합니다 — v4.0은 Attack Requirements가 추가되고 영향이 Vulnerable/Subsequent 시스템으로 갈리는, 서로 변환되지 않는 다른 평가이기 때문입니다. 붙여넣은 벡터는 자기 버전으로 열리며, 파서가 아는 버전이면(v2 포함) 빌더가 3.1·4.0만 쓰더라도 입력한 그대로 저장·채점됩니다. History, Repeater, Fuzzer, Miner 어디서든 SpaceLink… 를 누르면 프로젝트의 모든 이슈와 노트가 한 카드에 뜨고, 그 위에 + New issue… / + New note…가 고정되어 있습니다. 즉 지금 보고 있는 것에 대해 새 이슈를 만들면서 바로 연결하는 일이, 기존 이슈에 붙이는 것과 똑같은 키 수로 끝납니다. 제목·호스트·상태는 물론 issue / note 라는 단어로도 필터할 수 있고, 필터에 입력한 문자열은 생성 행을 고를 때 새 이슈의 제목으로 그대로 들어갑니다.

다음 단계