
이 문제는.. 사실 뒤에서 풀이자 적은 것부터 갑자기 풀다가 롸업이 필요한 수준까지는 아니길래 두문제정도는 그냥 풀었는데
이건 ...롸업도 없고 어떻게 풀어야 할지 감이 안잡히다가 풀어내긴 했지만! 롸업을 써야할 필요성을 느껴서 쓴다..
저 문제 설명(역사 => history)이 이 문제를 푸는데에 큰 도움이 되는 힌트이다.
이 문제를 푸는 핵심부터 말하자면
/read의 허술한 경로 필터를 우회해서 directory traversal을 하고 "history"를 읽어 flag의 위치를 알아내는 것이다.
이 과정을 알아보자.
1. app.py 소스 코드 주요 부분 분석
@APP.route('/read')
def read_memo():
error = False
data = b''
filename = request.args.get('name', '')
f = filename.replace("//", "/")
f = f.replace("../", "")
f = f.replace("./", "")
f = f.replace("\\\\\\\\", "\\\\")
f = f.replace("\\\\", "")
try:
with open(f'{UPLOAD_DIR}/{f}', 'rb') as f:
data = f.read()
except (IsADirectoryError, FileNotFoundError):
error = True
return render_template('read.html',
filename=filename,
content=data.decode('utf-8'),
error=error)
=> 하지만 필터 순서/방식 때문에 우회가 가능하다
2. 필터 우회 아이디어
우리가 원하는 최종 목표는:
f == "../something"
으로 만들어서,
open(f'uploads/{f}') # == open('uploads/../something')
→ 상위 디렉토리 파일에 접근하는 것이다.
..\//something <= 이렇게 쓰면 우리가 목표하는 바대로 접근이 가능하다.(필터 순서와 방식 때문에)
url로는 백슬래시를 인코딩하여 아래와 같은 형태가 되고 이것이 이 문제의 핵심 취약점이다.