https://dreamhack.io/wargame/challenges/552
환경
- Flask에서 동작하는 웹 애플리케이션.
- [/view] 페이지에서 서버에 등록된 이미지들을 출력.
- [/request] 페이지에서 URL을 통해 업로드.
- [/upload] 페이지에서 파일을 등록하여 업로드.
접근
- [/request]에서 브라우저를 통해 내부 파일을 업로드할 수 없도록 url 파라미터의 입력 값이 “file://”으로 시작되거나 “flag” 문자열이 포함되면 업로드를 하지 않도록 필터링한다.
- url 파라미터에 “file:///flag.txt“를 전달하는 것을 목표로 한다.
풀이
- 웹 브라우저에 “file://”으로 scheme을 완성하지 않아도 내부 파일에 접근이 가능하다. 아래의 이미지와 같이 “file:/home/kali”를 전달하면, 내부에서는 “file:///home/kali”로 자동 완성이 되고, 정상적으로 페이지를 불러온다. 문제의 url 파라미터는 입력 값에서만 검사하기 때문에, 입력 값을 “file:“로 하면 해당 필터링을 우회할 수 있다.

- 플래그 파일을 읽기 위해 경로 필터링 중 “flag”를 우회해야 한다. 웹에서 요청을 전송할 때, URL 인코딩을 수행한다. 그리고 서버에서 동작할 때 디코딩된다. 그리고 서버 내부에서 mini_database 배열에 저장할 때 URL 디코딩을 한 번 더 수행한다. 그러므로 총 2번의 URL 디코딩을 수행한다. 그러나 “flag” 필터링은 URL 디코딩이 한 번 일어났을 때 검사를 수행하므로, 요청 값을 두 번 인코딩하면 “flag” 필터링을 우회할 수 있다.
- “f” 문자를 URL 인코딩하면 %66이다. 그리고 한 번 더 인코딩하면 %2566이 된다.


플래그 획득 과정
- /request 페이지에서 아래와 같이 페이로드를 입력하고 전송.

- /view 페이지에 flag.txt의 값이 담긴 이미지를 출력.

- 추가된 이미지를 [새 탭에서 이미지 열기]로 base64로 인코딩된 플래그 값을 볼 수 있다.

- 인코딩된 플래그를 디코딩하면 플래그 획득.
