[WEB] file-download-1 문제풀이

파일 다운로드 취약점이란 파일 다운로드 기능이 존재하는 웹에서 파일 다운로드 시 파일의 경로 및 파일명을 파라미터로 받아 처리하는 경우 이를 적절히 필터링 하지 않으면 공격자가 이를 조작하여 허용되지 않은 파일을 다운 받을 수 있고 임의의 위치에 있는 파일을 열람하거나 다운받는 것을 가능케 하는 취약점을 말한다.

 

문제를 확인해보자.

 

 

flag.py 파일을 다운로드하여 플래그를 획득하는 문제이다. 일단 페이지에 접속해보자.

 

 

메모를 작성할 수 있는 페이지가 나온다. 위 내용대로 메모를 작성하고 upload 버튼을 눌러보자.

 

 

my-first-memo 라는 메모가 작성된다. 페이지 경로는 /read?name=my-first-memo 이다.

read 페이지에서 name 파라미터로 전달된 이름의 파일을 읽는 것으로 추측된다.

 

name 파라미터 값을 flag.py로 변경하여 시도해보자.

 

 

역시 안될 줄 알았다. 티오리 형님들은 문제를 낼 때 소스코드를 꼭 봐야 풀 수 있도록 만들었나보다.

소스코드를 보자.

(사실 소스코드 보기 전에 풀었지만 제작자가 섭섭할 수 있기 때문에 예의상 소스코드를 봐야한다)

 

#!/usr/bin/env python3
import os
import shutil

from flask import Flask, request, render_template, redirect

from flag import FLAG

APP = Flask(__name__)

UPLOAD_DIR = 'uploads'


@APP.route('/')
def index():
    files = os.listdir(UPLOAD_DIR)
    return render_template('index.html', files=files)


@APP.route('/upload', methods=['GET', 'POST'])
def upload_memo():
    if request.method == 'POST':
        filename = request.form.get('filename')
        content = request.form.get('content').encode('utf-8')

        if filename.find('..') != -1:
            return render_template('upload_result.html', data='bad characters,,')

        with open(f'{UPLOAD_DIR}/{filename}', 'wb') as f:
            f.write(content)

        return redirect('/')

    return render_template('upload.html')


@APP.route('/read')
def read_memo():
    error = False
    data = b''

    filename = request.args.get('name', '')

    try:
        with open(f'{UPLOAD_DIR}/{filename}', '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)


if __name__ == '__main__':
    if os.path.exists(UPLOAD_DIR):
        shutil.rmtree(UPLOAD_DIR)

    os.mkdir(UPLOAD_DIR)

    APP.run(host='0.0.0.0', port=8000)
​

 

upload_memo() 함수에 정의된 내용을 살펴보자.

사용자가 메모를 작성하면 제목은 filename으로, 내용은 content로 저장된다.

여기서 POST 요청으로 전달한 filename 파라미터 값에 상위 디렉토리 이동 명령어 ".."를 탐지한다.

".." 문자열이 탐지되지 않으면, filename 파라미터 명으로 파일을 생성하고, content를 내용으로 작성한다.

생성된 파일의 경로는 /uploads/{filename} 이다.

 

사실 flag.py가 어디있는지 모르지만 ".." 문자열을 탐지하는 것으로 보아 웹 디렉토리 최상위 경로에 있는 것 같다.

 

read_memo() 함수에 정의된 내용을 보면 upload_memo() 와 다르게 ".." 문자열에 대한 탐지가 존재하지 않는다.

그럼 /read?name=../flag.py로 접근하면 flag.py 파일을 읽어올 것 같다.

 

 

플래그가 뙇 하고 나타났다.

 

플래그 파일의 위치가 공개되지 않는 것으로 보아 기본적인 추론능력을 요하는 것 같다.

하지만 방구석 명탐정 코난인 나에게는 아주 손쉬운 문제였다.

 

문제풀이 끗

복사했습니다!