김승현

[Dreamhack : Web] file-download-1 본문

Web/Dreamhack : Web

[Dreamhack : Web] file-download-1

kshind 2023. 2. 23. 13:42

문제

file download 취약점이 있어서 그걸 활용해서 flag.py를 다운받으라는 문제인 것 같다.

 

풀이

접속하면 메뉴엔 Home, Upload My Memo가 있고 Please upload your memo!가 출력되어 있다.

upload my memo에 가보자.

filename과 content를 우리 마음대로 작성할 수 있는 것 같다.

이렇게 작성해서 한번 업로드 해보자.

그러면 이렇게 업로드가 되고

입력한 내용이 저장되어 있다. 근데 주소창을 잘 보면 메뉴에 없던 read라는 path로 읽는 걸 볼 수 있다.

소스 코드를 한 번 확인 해보자.

 


<전체 코드>

#!/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)

코드 초반부

os, shutil, flask. flag가 import되어 있는데 shutil은 파일과 파일 모음에 대한 여러 가지 고수준 연산을 제공하는

라이브러리라고 한다.

 

app = FLASK(__name__)을 통해 플라스크 객체를 생성하고 UPLOAD_DIR 변수에 uploads라는 문자열을 저장했다.

 

@APP.route('/') == host3.dreamhack.games:[port]/

 

files에 UPLOAD_DIR 변수에 있는 파일들의 리스트를 저장했고

render_template를 통해 index.htlml을 반환해주었다.

os.listdir은 해당 디렉토리에 있는 파일들의 리스트를 구할 수 있게 해주는 거라고 한다.

 

@APP.route('/upload') == host3.dreamhack.games:[port]/upload

=

메소드가 POST라면,

filename 변수에 사이트에서 전달된 filmename을 저장

content 변수에 사이트에서 전달된 content를 utf-8로 인코딩해서 저장 (utf-8 : 유니코드를 위한 가변 길이 문자 인코딩)

 

그리고 만약 filename에서 ..이 포함되어 있다면

render_template 함수를 통해 upload_result.html을 보여주는데 이때 bad characters,,를 출력하는 걸 리턴

 

[UPLOAD_DIR변수]/[filename변수] 경로에 있는 걸 binary 형식으로 작성한다. 그리고 코드에서 f라는 명칭으로 사용함

 

모두 종료되면 upload.html을 render_template 함수를 이용해서 반환(upload.html이 화면에 보임)

 

@APP.route('/read') == host3.dreamhack.games/read

erroe 변수에 False저장, data 변수는 byte 타입으로 저장받을 예정

 

filename 변수에 사이트에서 전달된 name 저장

 

[UPLOAD_DIR변수]/[filename변수] 경로에 있는 걸 binary 형식으로 읽는다. 이걸 코드에서 f라고 지칭할 예정

 

이게 실패한다면(에러 중에서 IsADirectoryError, FileNotFoundError들로 인해) error를 True로 변경

 

render_template 함수를 통해 read.hml을 filename의 이름을 가지고

content엔 data변수에 있는 내용을 utf-8로 디코드하고 error엔 유무를 판단한 걸 반환해서 보여준다.

 

if __name__ 부분

코드가 실행될 때(사이트가 실행될 때) 실행되는 부분이고

UPLOAD_DIR 경로 안에 파일이 존재한다면

파일들을 모두 다 지운다.

 

그리고 UPLOAD_DIR이라는 디렉토리를 만든다고 한다.


 

여기까지 코드를 보면 크게 upload와 read가 있다.

이건 upload의 코드 중 일부인데 ..을 필터링해서 사용 못하게 하니까 우리는 read를 이용해야 한다.

이건 read나 upload 둘 다 있는 건데 UPLOAD_DIR/[우리가 업로드한 파일] 이런 식으로 저장되는 걸 알 수 있다.

그렇다면 우리가 업로드 했던 파일을 read하고

거기서 ../을 입력하면 UPLOAD_DIR에 우리는 위치할 수 있다. 바로 해보자.

현재 디렉토리 정리
상위 디렉토리         하위 파일
UPLOAD_DIR    -   first_memo
                           -       flag.py 

 

이건 처음에 내가 업로드했던 파일이다.

주소는 http://host3.dreamhack.games:15702/read?name=fisrt_memo 

주소에서 first_memo를 지우고 ../flag.py를 해보자.

'Web > Dreamhack : Web' 카테고리의 다른 글

[Dreamhack : Web] Carve party  (0) 2023.02.23
[Dreamhack : Web] web-ssrf  (0) 2023.02.23
[Dreamhack : Web] image-storage  (0) 2023.02.23
[Dreamhack : Web] command-injection-1  (0) 2023.02.22
[Dreamhack : Web] mango  (0) 2023.02.22