[Playbook] Path Traversal 진단 Cheat Sheet

목차


이 문서는 Path Traversal 진단 중 입력이 파일 경로 조립에 참여하는지 확인하고, 걸려 있는 검증의 종류를 판별해 다음 시도를 고르기 위한 빠른 참고서다. 검증별 상세 원리와 전체 풀이는 관련 Write-up과 Path Traversal Note로 연결한다.

승인된 테스트 환경과 실습에서만 사용한다. 파일 읽기는 되돌릴 수 없는 정보 노출이므로, 성립 여부는 /etc/passwd처럼 무해한 마커 파일로만 확인하고 자격 증명·키·개인정보가 담긴 경로는 승인 범위를 확인한 뒤에 접근한다. 같은 파라미터가 쓰기 경로에도 쓰인다면 파일을 생성·덮어쓸 수 있으므로 읽기 검증만으로 멈춘다.

30초 진단 흐름

  1. 요청 값 중 파일 이름·경로·확장자처럼 보이는 파라미터를 하나 고른다.
  2. 존재하지 않는 값을 보내 파일 부재 오류가 나는지 확인한다(경로 조립 여부 판정).
  3. 존재하는 파일에 ../를 붙여 차단인지 제거인지 구분한다.
  4. 이탈 수단을 상대경로 → 절대경로 → 중첩 → 인코딩 순으로 하나씩 시도한다.
  5. 접두·확장자 요구가 있으면 그 형태를 남긴 채 최종 경로만 바꾼다.
관찰 결과판단다음 단계
../../../etc/passwd가 그대로 통함검증 없음성립 확정. 대상 파일 확장
../ 포함 시 오류·거부차단(reject) 방식절대경로·인코딩으로 패턴 회피
../file.jpgfile.jpg로 응답제거(sanitize) 방식....// 중첩 시퀀스
....//도 무력화됨재귀 제거이중 인코딩 %252e%252e%252f
요청 값에 전체 경로가 들어 있음접두 검증 가능성기준 경로 유지 후 탈출
확장자를 바꾸면 거부됨확장자 검증null 바이트 %00.jpg
검증 실패와 파일 부재의 응답이 다름오라클 확보응답 차이로 통과 조건 좁히기
값이 숫자·ID로만 구성됨매핑 테이블 방식경로 조립이 아닐 수 있음. 다른 지점 탐색

1. 파일 참조 지점 찾기

파일명이 URL에 그대로 보이는 경우만 후보가 아니다. 값이 경로의 일부가 되는 모든 지점을 수집한다.

후보 지점흔한 파라미터 형태확인 포인트
이미지·썸네일 로더?filename=, ?image=, ?file=확장자가 값에 노출되는지
첨부파일 다운로드?doc=, ?path=, ?name=원본 파일명이 그대로 쓰이는지
템플릿·테마·언어 선택?lang=, ?theme=, ?view=값 뒤에 확장자가 자동으로 붙는지
로그·리포트 조회?date=, ?report=날짜 문자열이 파일명이 되는지
업로드 처리filename 필드, Content-Disposition저장 경로에 원본 이름이 쓰이는지
쿠키·JSON·헤더세션에 담긴 파일 경로, X- 커스텀 헤더쿼리 외 전송 경로도 동일하게 조립되는지
아카이브 전개ZIP·TAR 내부 엔트리 이름Zip Slip 성립 여부
  • 확장자가 값에 드러나 있는 파라미터가 1순위다. 애플리케이션이 확장자를 붙이는 경우 ?lang=enen.properties처럼 뒤가 고정되므로 null 바이트나 잘림이 필요해진다.
  • 값이 순수한 숫자 ID라면 매핑 테이블 방식일 가능성이 높다. 그래도 존재하지 않는 ID의 오류 응답을 한 번 확인해 파일 부재 오류가 새는지 본다.

2. 경로 조립 여부 판정

취약점을 찾기 전에 그 값이 파일 경로가 되기는 하는지부터 확정한다. 대조군을 세 개 만들어 응답을 비교한다.

요청기대되는 응답의미
정상 파일명파일 내용기준선
존재하지 않는 파일명파일 부재 오류값이 실제 경로 조회에 사용됨
파라미터 자체 삭제파라미터 누락 오류검증 실패 응답과 구분할 기준
  • 세 응답이 모두 같다면 값이 경로에 쓰이지 않거나 오류가 통일되어 있는 것이다. 응답 길이와 처리 시간까지 비교한 뒤 다음 지점으로 넘어간다.
  • 검증 실패가 “파라미터 누락"으로 표시되는 구현이 있다. 이 경우 파일 부재 오류가 나는 값 = 검증 통과이므로 그 자체가 강력한 오라클이 된다.
  • 응답이 항상 200에 이미지 Content-Type이면 상태 코드로는 구분되지 않는다. Content-Length와 본문 원문으로 판정한다.

3. 검증 방식 판별

존재하는 파일에 무해한 접두를 붙여 응답을 본다. 이 한 번의 요청이 이후 모든 시도의 방향을 정한다.

GET /image?filename=../12.jpg HTTP/2
응답검증 방식진행 방향
오류·거부차단(reject)패턴 자체를 피한다 — 절대경로, 인코딩
12.jpg가 정상 반환제거(sanitize)제거 결과를 이용한다 — ....//
12.jpg가 반환되며 앞 경로도 유효검증 없음바로 이탈 시도
  • 이 구분을 건너뛰면 어느 쪽이 막혔는지 알 수 없는 실패만 쌓인다. 반드시 먼저 확정한다.
  • 차단 방식이라면 ..만 보는지 ../까지 보는지도 확인한다. ..%2f가 통하면 구분자만 검사 대상에서 빠진 것이다.

4. 이탈 수단 사다리

아래 순서로 하나씩 올라간다. 각 항목은 독립적이므로 하나가 막혀도 나머지를 모두 확인한다.

순서페이로드통하면 확인되는 것관련 실습
1../../../etc/passwd검증 없음simple case
2/etc/passwd절대경로 우회absolute path bypass
3....//....//....//etc/passwd비재귀 제거stripped non-recursively
4..%2f..%2f..%2fetc/passwd구분자만 인코딩해도 통과
5%252e%252e%252f 반복 + etc/passwd검증 이후 추가 디코딩superfluous URL-decode
6..%c0%af..%c0%af..%c0%afetc/passwd오버롱 UTF-8 허용
7..\..\..\windows\win.iniWindows 구분자
8/var/www/images/../../../etc/passwd접두 검증validation of start of path
9../../../etc/passwd%00.jpg확장자 검증 + null 바이트null byte bypass

제거 방식일 때 쓸 수 있는 중첩 변형은 다음과 같다. 제거 대상이 ../인지 ..인지, 역슬래시도 구분자로 처리되는지에 따라 통하는 형태가 달라진다.

입력제거 대상제거 후
....//../../
.../././../
....\/..\../
....\\..\..\
  • 단일 URL 인코딩이 통하지 않는 것은 정상이다. 웹 서버가 쿼리 문자열을 애플리케이션에 넘기기 전에 이미 디코딩하므로 필터는 복원된 ../를 본다. 인코딩 계열은 디코딩이 검증 이후에 한 번 더 일어날 때만 의미가 있다.
  • ../의 개수는 기준 디렉터리 깊이 이상이면 된다. 루트에서 ..는 루트를 가리키므로 넉넉하게 붙여도 결과가 달라지지 않는다.
  • 페이로드를 바꿀 때는 한 번에 하나만 바꾼다. 인코딩과 중첩을 동시에 적용하면 실패 원인을 분리할 수 없다.

5. 부가 검증 우회

이탈 자체는 되는데 다른 조건 때문에 거부되는 경우다.

요구 조건확인 방법우회 형태
기준 경로로 시작해야 함/etc/passwd/base/nothing.jpg의 응답 비교/base/../../../etc/passwd
특정 확장자로 끝나야 함확장자만 바꿔 거부 여부 확인...%00.jpg, 경로 뒤 ?·# 절단
확장자가 서버에서 자동으로 붙음값 뒤에 고정 문자열이 붙는지 확인null 바이트로 뒷부분 절단
파일명에 디렉터리 구분자 불가%2f·\·%5c 각각 시도허용되는 구분자 탐색
  • 접두 검증은 정규화 전 문자열을 검사하는 결함이다. 기준 경로를 그대로 남긴 채 뒤에서 빠져나가는 것이 요령이다.
  • null 바이트는 런타임에 의존한다. PHP 5.3.4, Java 7u40 이후로는 예외가 발생하므로 통하지 않는다고 해서 다른 우회까지 포기하지 않는다.
  • 확장자 검증이 걸린 지점에서는 애초에 허용 확장자를 가진 민감 파일(백업 .jpg, 로그 등)이 기준 디렉터리 밖에 있는지도 함께 본다.

6. 대상 파일 선택

성립 확인은 무해한 파일로, 영향도 산정은 승인 범위 안에서 진행한다.

목적Linux · UnixWindows
성립 확인/etc/passwdC:\windows\win.ini
호스트 식별/etc/hostnameC:\windows\system32\drivers\etc\hosts
프로세스 정보/proc/self/cmdline
환경 변수·시크릿/proc/self/environ
애플리케이션 설정WEB-INF/web.xml, .env, config.phpweb.config
  • 깊이를 모를 때는 /etc/passwd처럼 절대경로가 확정된 파일로 먼저 확인하고, 상대 위치가 필요한 설정 파일은 그다음에 시도한다.
  • /proc/self/environ은 환경 변수에 담긴 자격 증명이 그대로 노출되므로 심각도 산정에는 결정적이지만, 그만큼 취급에 주의가 필요하다. 승인 범위를 먼저 확인한다.
  • 컨테이너 환경이라면 파일 시스템 자체가 좁아 /etc/passwd가 있어도 얻을 것이 적을 수 있다. 애플리케이션 루트 기준의 상대 경로를 함께 시도한다.

7. 특수·예외 상황

아래 항목은 일반 흐름에 맞지 않을 때만 확인한다.

상황짧은 판단 기준관련 글
같은 파라미터가 저장 경로에도 쓰임읽기가 아니라 쓰기 프리미티브. 심각도가 크게 오름Galaxy S24 Quick Share 사례
리다이렉트 목적지에 경로가 들어감../로 목적지를 벗어나 다른 엔드포인트 지정 가능client-side redirect
WAF에서만 차단됨백엔드가 보는 값과 WAF가 보는 값의 차이를 만든다superfluous URL-decode
ZIP 업로드 후 자동 전개엔트리 이름에 ../ 포함 여부(Zip Slip)
응답이 항상 200 이미지Content-Length와 본문 원문으로 판정
경로가 URL이 될 수도 있음file://·http:// 스킴 처리 시 SSRF·LFI로 확장
심볼릭 링크가 기준 디렉터리에 있음문자열 정규화만으로는 걸러지지 않음

Quick Checklist

  • 파일 이름·경로·확장자가 값에 드러나는 파라미터를 모두 수집했는가?
  • 존재하지 않는 값의 오류 응답으로 경로 조립 여부를 확인했는가?
  • 검증 실패 응답과 파일 부재 응답을 구분할 기준을 확보했는가?
  • 필터가 차단인지 제거인지 먼저 확정했는가?
  • 상대경로·절대경로·중첩·인코딩·역슬래시를 각각 독립적으로 시도했는가?
  • 단일 인코딩 실패를 인코딩 계열 전체의 실패로 오해하지 않았는가?
  • 접두·확장자 요구가 있다면 그 형태를 남긴 채 최종 경로만 바꿨는가?
  • 페이로드를 한 번에 하나씩만 바꿔 실패 원인을 분리했는가?
  • 성립 확인을 무해한 마커 파일로 끝내고 승인 범위를 넘지 않았는가?
  • 같은 파라미터가 쓰기 경로에도 쓰이는지 확인했는가?