[Write-Up] PortSwigger - File Path Traversal, Traversal Sequences Stripped With Superfluous URL-Decode

목차


문제 분석

난이도: PRACTITIONER
Lab: File path traversal, traversal sequences stripped with superfluous URL-decode

image.png

상품 이미지 표시 기능에 path traversal 취약점이 존재한다. 애플리케이션이 경로 이탈 시퀀스가 포함된 입력을 차단하지만, 그 입력을 사용하기 전에 URL 디코딩을 한 번 더 수행한다. /etc/passwd 파일의 내용을 가져오면 문제가 해결된다.

Path Traversal 진단

이미지 요청 형태를 확인한다.

GET /image?filename=8.jpg HTTP/2
Host: <lab-id>.web-security-academy.net

앞선 랩들에서 사용한 페이로드를 순서대로 시도한다.

시도결과해석
../8.jpg경로 이탈이 반영되지 않음../가 검증에 걸림
....//동일중첩 시퀀스도 무력화됨
%2e%2e%2f동일디코딩된 뒤 검증되므로 효과 없음

세 번째 결과가 중요하다. 단일 URL 인코딩이 통하지 않는 이유는 필터가 강해서가 아니라, 웹 서버와 프레임워크가 쿼리 문자열을 애플리케이션에 넘기기 전에 이미 디코딩하기 때문이다. 필터가 값을 받아 볼 시점에는 %2e%2e%2f가 이미 ../로 복원되어 있다.

그런데 문제 설명은 애플리케이션이 입력을 사용하기 전에 URL 디코딩을 한 번 더 수행한다고 밝히고 있다. 즉 값이 디코딩되는 지점이 두 곳인데 검증은 그 사이에 한 번만 놓여 있다. 이 구조에서는 검증 시점에 인코딩된 상태로 남아 있다가 검증 이후 디코딩에서 복원되는 값을 만들 수 있다.

단계입력값 %252e%252e%252f의 상태
HTTP 계층 디코딩 후%2e%2e%2f
필터 검증 시점%2e%2e%2f — 경로 이탈 시퀀스가 보이지 않아 통과
애플리케이션의 추가 디코딩 후../
파일 API 전달../ 로 경로 이탈 성립

익스플로잇

../를 두 번 URL 인코딩해 페이로드를 구성한다. %2e., %2f/이므로 각 문자의 %를 다시 %25로 인코딩하면 된다.

GET /image?filename=%252e%252e%252f%252e%252e%252f%252e%252e%252fetc/passwd HTTP/2
Host: <lab-id>.web-security-academy.net

요청을 전송하면 응답 본문에 /etc/passwd의 내용이 포함되며 문제가 해결된다.

image.png


정리

이 랩의 결함은 필터의 성능이 아니라 위치에 있다. 필터는 자기가 받은 문자열에 대해 정확히 판단했지만, 그 문자열은 파일 API가 최종적으로 받는 문자열과 같지 않았다. 검증 시점과 해석 시점 사이에 변환이 하나 더 끼어 있는 순간, 검증은 실제로 사용될 값이 아니라 그 값의 중간 표현을 본 것이 된다.

이 구조는 path traversal에 국한되지 않는다. WAF가 검사한 요청과 백엔드가 파싱한 요청이 달라지는 WAF 우회, 프론트엔드와 백엔드가 요청 경계를 다르게 읽는 HTTP request smuggling, 필터가 본 문자열과 브라우저 파서가 본 문자열이 달라지는 XSS 필터 우회가 모두 같은 계열이다. 공통 원인은 하나의 입력을 서로 다른 두 컴포넌트가 서로 다른 규칙으로 해석한다는 데 있다.

같은 계열의 변형 인코딩도 함께 정리해 둘 만하다. 다만 아래 항목들은 대상 컴포넌트가 해당 인코딩을 받아들일 때만 성립하므로, 통하지 않았다고 해서 필터가 안전하다는 뜻은 아니다.

형태복원 결과성립 조건
%2e%2e%2f../디코딩이 검증 이후에 일어남
%252e%252e%252f../디코딩이 검증 전후로 두 번 일어남
..%c0%af../오버롱 UTF-8을 허용하는 레거시 디코더
..%u2215../%u 표기를 처리하는 비표준 파서
%2e%2e/../부분 인코딩. 문자열 비교식 필터에 유효

방어는 인코딩 형태를 하나씩 열거해 막는 방향으로는 닫히지 않는다. 디코딩을 필요한 곳에서 한 번만 수행하고, 검증은 애플리케이션이 실제로 사용할 최종 값에 대해서만 수행해야 한다. 그리고 그 검증은 문자열 패턴 검사가 아니라, 조립된 경로를 정규화한 뒤 기준 디렉터리 하위인지 확인하는 형태여야 한다. 정규화 이후의 경로에는 인코딩이 남아 있지 않으므로 이 계열의 우회가 애초에 성립하지 않는다.