소개
상태만 검사하는 스캐너가 실제 진단 응답과 문서 예제를 모두 탐지했습니다. 이제 의도한 질문을 묻는 검사를 만듭니다. 인증되지 않은 요청이 이 애플리케이션의 실제 내부 진단 객체를 받았을까요?
Nuclei 네이티브 YAML 템플릿을 만들고 응답 조건을 테스트한 다음, 허가된 Fieldwork Studio 사이트 여섯 곳을 스캔합니다. 재사용 가능한 템플릿과 실제 탐지 결과가 인계할 산출물입니다. 환경은 이전 실습과 독립적이며 파일이나 프로세스가 이어지지 않습니다.
먼저 실제 응답으로 진단 노출 확인하기를 완료하거나, HTTP 상태, 콘텐츠 유형 헤더, 작은 JSON 객체와 Nuclei JSONL 탐지 결과를 읽는 데 익숙해야 합니다. 이 실습에서는 YAML과 일치 표현식을 설명합니다. 모든 대상 설정은 합성 데이터입니다. 제공된 읽기 전용 범위만 사용하고 공개 워크숍 페이지와 일정을 보존하세요. 읽기와 일반적인 실수를 포함해 약 65분을 예상하세요. 이는 작성자의 추정치입니다.
검사의 응답 계약 정하기
이 단계에서는 정상 서비스를 확인하고 템플릿이 답해야 할 질문을 정확히 정합니다. Workshops를 열고 수업을 살펴본 다음, Get the workshop schedule을 사용해 실제 fieldwork-schedule.csv를 다운로드하세요. 평가하는 동안 Printmaking과 Bookbinding이 계속 제공되어야 합니다.

두 워크숍 카드는 보존해야 할 공개 서비스를 보여 줍니다. 초록색 Get the workshop schedule 링크는 실제 일정을 다운로드합니다.
Auditor로 전환하세요. /home/labex/project에 있는 Kali 감사 컨테이너로 들어갑니다. 범위와 허가된 기본 URL을 읽으세요.
cat scope.txt
cat targets.txt
여섯 사이트는 http://workshop.test:8080/ 아래의 studio, archive, manual, garden, status, members입니다. 읽기 전용 HTTP GET 요청으로 이 URL들만 테스트하세요. 대상 사이트나 제공된 범위 파일을 수정하지 마세요.
실제 진단 응답을 다시 살펴보세요. 앞에서와 같이 curl -i는 응답 헤더를 포함합니다.
curl -i http://workshop.test:8080/studio/maintenance/diagnostics
탐지 결과는 HTTP 200, JSON 콘텐츠 유형, 애플리케이션의 최상위 내부 진단 객체를 함께 만족해야 합니다. 네 개의 스칼라 필드는 서비스와 공개 범위, 절대 데이터베이스 파일 경로, 따옴표 없는 0 이상의 정수 요청 횟수를 나타냅니다. 파일 경로와 횟수는 바뀔 수 있습니다. 키 순서와 공백은 의미에 영향을 주지 않습니다. 중첩된 문서 예제는 실제 객체가 아닙니다.
이 검사는 이 작은 애플리케이션에 명시된 응답 형식을 대상으로 합니다. 임의의 JSON 엔드포인트가 취약하거나 사이트에 다른 문제가 없다는 것을 입증하지는 않습니다. 특히 공개 페이지, 공개 JSON 요약, 상태 확인 응답, HTTP 거부, 로그인 리디렉션은 요구되는 노출 증거가 아닙니다.
디렉터리가 바뀌면 pwd로 확인하고, 상대 파일 이름으로 읽거나 만들기 전에 cd /home/labex/project로 돌아오세요. 재연결할 때 작업을 보존하세요. 터미널을 열어도 환경은 다시 만들어지지 않습니다.
네이티브 응답 매처 만들기
이 단계에서는 HTTP 200 일치에 의존하는 대신 재사용 가능한 템플릿을 작성합니다. Auditor에 머무르세요.
Nuclei 템플릿에는 id, 설명용 info, 그리고 http와 같은 프로토콜 섹션이 있습니다. 들여쓰기는 중첩된 YAML 필드를 묶고, 대시는 목록 항목을 시작합니다. 탭 대신 공백을 사용하세요. method: GET은 읽기 전용 요청을 선택합니다. {{BaseURL}}은 Nuclei에 전달된 대상에서 가져오므로 템플릿에 특정 호스트 이름, 포트 또는 사이트 접두사를 넣지 않습니다. redirects: false는 로그인 리디렉션을 그대로 관찰하게 합니다.
**매처(matcher)**는 응답에 적용하는 조건입니다. 이 템플릿은 다음 세 매처 그룹을 모두 요구합니다.
| 그룹 | 필요한 관찰 | 중요한 이유 |
|---|---|---|
| 상태 | HTTP 200 | 거부나 리디렉션은 요청한 내부 응답이 아닙니다. |
| 헤더 | 선택적 매개변수를 허용하는 JSON 콘텐츠 유형 | 일반 HTML 페이지는 진단 노출을 입증하지 않습니다. |
| 본문 | 평면 JSON 객체 안에 명시된 실제 진단 필드 | 공개 JSON, 상태 확인 데이터, 중첩 예제는 다른 결과입니다. |
matchers-condition: and는 이 그룹들을 결합합니다. 이를 생략하면 기본 OR 관계 때문에 HTTP 200만으로 다시 탐지 결과가 나올 수 있습니다. DSL 매처 내부의 condition: and도 마찬가지로 나열된 모든 본문 표현식을 요구합니다.
DSL은 도메인 특화 언어(domain-specific language)의 약자로, 여기서는 응답을 검사하는 짧은 표현식입니다. body는 응답 텍스트입니다. json_minify(body)는 유효한 JSON을 파싱하고 압축하며, 키 순서에 의존하지 않고 의미 없는 공백을 제거합니다. contains(text, fragment)는 리터럴 조각을 찾습니다. 먼저 정규화하면 "visibility" : "internal"로 작성된 필드도 "visibility":"internal"과 같은 조건에 일치합니다.
regex(pattern, text)는 정규 표현식을 적용합니다. 아래 표현식은 소수의 기호를 사용합니다.
/[A-Za-z0-9._/-]+는 맨 앞의 슬래시 뒤에 하나 이상의 경로 문자를 요구합니다. 허용 문자는 영문자, 숫자, 마침표, 밑줄, 슬래시, 하이픈입니다. 대문자를 포함해 변경된 절대 경로도 허용합니다.[0-9]+[,}]는 숫자 바로 뒤에 쉼표나 닫는 중괄호가 와야 합니다. 따라서 따옴표로 감싼 횟수, 음수, 소수의 앞부분을 온전한 정수 횟수로 취급하지 않습니다.^\{[^{}]*\}$는 바깥 중괄호 한 쌍을 요구하고 중첩된 중괄호는 허용하지 않습니다. 문서 예제 내부의 객체 대신 이 과정의 필드 네 개로 이루어진 평면 진단 형식과 일치합니다.
헤더 표현식은 대소문자를 구분하지 않는 줄 단위 일치를 위해 (?im)을 사용하고, \s*는 공백을 허용합니다. 선택적 (?: ... )? 그룹은 ; charset=utf-8 같은 매개변수를 허용합니다. 이 표현식을 완전한 JSON 콘텐츠 유형 검사로 복사해 사용할 수 있으며, 범용 MIME 파서를 만들 필요는 없습니다.
아래 YAML에서 >-는 다음 들여쓴 줄의 텍스트 값을 시작합니다. 추가적인 YAML 따옴표 이스케이프 없이 표현식의 따옴표와 역슬래시를 보존합니다. 각 표현식 내부의 작은따옴표는 리터럴 텍스트를 감싸며, 그 안의 큰따옴표는 일치시킬 JSON 필드의 문장 부호입니다.
diagnostics.yaml을 만드세요. 이전 실습과 마찬가지로 >는 이 파일을 덮어쓰고, 따옴표로 감싼 here-document 구분자는 블록을 문자 그대로 복사합니다. 마지막 YAML은 한 줄에 단독으로 있어야 합니다.
cat > diagnostics.yaml <<'YAML'
id: workshop-diagnostics-exposed
info:
name: Public workshop diagnostics
author: labex
severity: medium
http:
- method: GET
path:
- "{{BaseURL}}/maintenance/diagnostics"
redirects: false
matchers-condition: and
matchers:
- type: status
status: [200]
- type: regex
part: header
regex:
- '(?im)^content-type:\s*application/json(?:\s*;[^\r\n]*)?\s*$'
- type: dsl
condition: and
dsl:
- >-
contains(json_minify(body), '"service":"workshop-portal"')
- >-
contains(json_minify(body), '"visibility":"internal"')
- >-
regex('"database_file":"/[A-Za-z0-9._/-]+"', json_minify(body))
- >-
regex('"request_count":[0-9]+[,}]', json_minify(body))
- >-
regex('^\{[^{}]*\}$', json_minify(body))
YAML
실제 엔진에 템플릿 검증을 요청하세요. -validate는 Nuclei 템플릿 구문을 검사하며 올바른 탐지 동작을 입증하지는 않습니다. -t는 파일을 선택하고, -duc는 업데이트 확인을 비활성화하며, -ni는 외부 Interactsh 콜백을 비활성화하고, -nc는 터미널 색상 코드를 제거합니다.
nuclei -t diagnostics.yaml -validate -duc -ni -nc
All templates validated successfully가 나와야 합니다. Nuclei가 YAML 또는 표현식 오류를 보고하면 완전한 블록과 들여쓰기, 따옴표, 역슬래시를 비교한 다음, 수정한 here-document와 검증 명령을 다시 실행하세요. targets.txt와 scope.txt를 보존하세요.
이 과정에서는 배운 범위인 다음 하위 집합을 허용합니다. 허가된 {{BaseURL}}/maintenance/diagnostics 경로에 대한 HTTP GET 정의 하나, 리디렉션 없음, 응답만 검사하는 status, word, regex 또는 DSL 매처입니다. 허용되는 DSL 도우미는 contains, contains_all, contains_any, regex, json_minify, len, tolower, toupper, starts_with, ends_with이며, body, all_headers, status_code, content_length에 적용합니다. 최상위에는 id, info, http만 사용하고, 요청 필드는 method, path, redirects, matchers-condition, matchers만 사용하세요. 템플릿은 32 KiB 이내, 매처는 최대 열두 개로 제한합니다. 다른 프로토콜, 스크립트, 외부 콜백, 로컬 파일 접근은 이 과제의 범위 밖입니다. 이 범위 안에서 동등한 매처 구성을 사용해도 됩니다.
검증은 변경하지 않은 템플릿을 Nuclei 네이티브 엔진으로 실행하여 진단 노출 조건에 일치해야 하는 응답과 일치하지 않아야 하는 응답을 일회용 예제로 검사합니다. 예제에는 변경된 포트, 호스트 이름, 값, JSON 배치, 콘텐츠 유형 매개변수가 포함됩니다. 구문 검사만 통과하거나 항상 일치하거나 전혀 일치하지 않는 파일로는 이 기능 검사를 통과할 수 없습니다.
검사 실행 후 인계할 산출물 검토하기
이 단계에서는 실제로 허가된 사이트에 템플릿을 적용하고 검토 가능한 증거를 보관합니다. Auditor에 머무르세요. 경로가 BaseURL에 상대적이므로 대상 목록이 바뀌어도 같은 파일을 재사용할 수 있습니다.
실제 평가를 실행하세요. -l은 기본 URL들을 제공하고, -o는 탐지 결과를 저장하며, -elog는 요청 오류를 저장합니다. -jsonl은 탐지 결과마다 JSON 객체 하나를 사용하고, -ot는 Nuclei 자체 요청과 응답을 유지하면서 인코딩된 템플릿을 제외합니다.
nuclei -t diagnostics.yaml -l targets.txt -duc -ni -nc -jsonl -ot -o findings.jsonl -elog scan-errors.jsonl
탐지 결과가 없는 것을 해석하기 전에 Nuclei 자체 요청 오류 로그를 살펴보세요.
cat scan-errors.jsonl
이번 성공적인 실행에서는 로그가 비어 있어야 합니다. Nuclei가 0으로 종료하거나 No results found를 출력해도 연결 오류가 있으면 검사가 완료되지 않은 것입니다. 결론을 내리기 전에 철자나 연결 문제를 바로잡고, 이전 증거와 새 실행이 혼동되지 않도록 재시도에는 새 파일 이름을 사용하세요. 최종 제출 파일은 findings.jsonl과 scan-errors.jsonl입니다. 새 이름으로 재시도했다면 cp로 성공한 한 쌍을 이 최종 파일 이름으로 복사하세요. 예를 들어 cp retry-findings.jsonl findings.jsonl은 첫 번째 파일을 두 번째 파일에 덮어씁니다. 같은 실행의 오류 로그도 함께 복사하세요.
탐지 결과를 읽으세요.
cat findings.jsonl
/studio/maintenance/diagnostics로 끝나는 일치 URL이 하나 나와야 합니다. 탐지 개수만 보지 말고 포함된 응답과 X-Audit-Event를 읽으세요. 본문은 수동으로 확인한 계약을 충족해야 합니다.
그럴듯한 다른 두 응답이 일치하지 않은 이유를 확인하세요.
curl -i http://workshop.test:8080/garden/maintenance/diagnostics
이는 visibility가 public으로 설정된 공개 JSON입니다. 내부 진단 노출이 아닙니다.
curl -i http://workshop.test:8080/members/maintenance/diagnostics
로그인을 가리키는 Location과 함께 HTTP 302가 나와야 합니다. 템플릿은 이를 따라가지 않으므로 로그인 페이지를 진단 결과로 오인하지 않습니다. 나머지 비일치 사이트는 보호된 archive, manual의 중첩 문서, status의 최소 상태 확인 응답입니다. 각 사이트에는 여전히 허가된 읽기 전용 요청이 전송됩니다. 탐지 결과가 없다는 것은 이 템플릿의 조건에 대해서만 설명하며, 사이트 전체의 보안 상태를 뜻하지 않습니다.
auditor가 읽을 수 있는 자체 완결적인 일반 파일로 제출하세요. 인계할 산출물은 재사용 가능한 diagnostics.yaml, 실제 findings.jsonl, 그리고 같은 실행에서 생성된 scan-errors.jsonl입니다. 범위 파일도 함께 보관하세요. 템플릿에는 대상에 종속적인 데이터베이스 파일 경로, 요청 횟수, 호스트 이름, 포트가 들어 있지 않습니다. 검증은 격리된 예제를 실행하기 전에 실제 증거를 확인하며 여러분의 파일을 보존합니다. 정상 기능 보존을 확인하는 일반적인 읽기 전용 요청은 서버 로그 항목을 추가하고 요청 횟수를 늘릴 수 있지만, 학습자가 수행한 평가의 증거로 쓰이지는 않습니다.
독립 챌린지에서는 새 배포 목록과 주소를 제공합니다. 공개 워크숍 사용과 허가된 유지보수 접근을 계속 제공하면서 같은 응답 계약과 오류 처리 원칙을 적용하세요.
요약
상태, 콘텐츠 유형, 정규화한 진단 필드를 결합하는 Nuclei 네이티브 HTTP 템플릿을 작성했습니다. 응답의 차이를 테스트하고, BaseURL을 통한 이식성을 유지했으며, 실행 실패를 진단 노출 조건에 일치하는 응답이 없다는 결과로 간주하지 않으면서 허가된 모든 사이트를 평가했습니다. 인계할 산출물은 실제 템플릿, 탐지 결과와 같은 실행에서 생성된 Nuclei 자체 요청 오류 로그입니다.



