[Dreamhack Web - Lv 1] file-download-1

문제 정보

File Download 취약점이 존재하는 웹 서비스입니다.
flag.py를 다운로드 받으면 플래그를 획득할 수 있습니다.

풀이 힌트

1. 공격 백터 파악

2. Path Traversal Attack

 

문제 풀이

더보기
더보기

들어가보니, 기능이 Upload My Memo라는 Upload 기능일 거 같은 메뉴가 하나있었다.

 

해당 페이지의 공격 백터는 filename과 content이다.
일단 업로드를 하면 어떻게 작동하는지 확인을 해보도록 하겠다.

 

업로드를 한 결과 Home 페이지에 내가 filename에 넣은 값이 리스트로 들어가 있었다.

 

들어가보니, Get 메소드로 name을 넘겨주는 것을 확인하였다.
이 또한 사용자가 값을 입력할 수 있으니, 공격 백터에 추가하겠다.

내가 테스트로 넣은 값들은 잘 나오고 있었다.

 

내가 현재 찾은 공격 백터는 아래와 같다.

  • upload 페이지 : filename, content → Post
  • read 페이지 : name → Get

 

일단 나는 Path Traversal 취약점이 생각이 났고, read 페이지에 넣어봤다.

 

그 결과 flag.py의 내용이 그대로 출력되어 Flag를 획득할 수 있었다.

코드 분석

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

해당 코드를 보니, 아주 간단하게 구성이 되어있었다.

 

@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')

위 코드는 Upload 페이지의 코드이다.
클라이언트에서 서버로 filename과 content를 Post로 보내면, uploads 폴더 안에 filename으로 만들어진 파일이 생성된다.

해당 파일의 내용은 content가 채우게 된다. 그 후 Home 페이지로 리다이렉트가 된다.

 

업로드 같은 기능은 공격자에게 업로드되는 경로, 업로드되는 파일 이름이 노출이 되면, 업로드 공격으로 이어질 수 있다.
또한 해당 문제 풀이와 같이 의도치 않는 파일을 다운로드하거나 읽을 수 있기 때문에 업로드되는 경로와 업로드되는 파일 이름은 공격자가 알지 못하도록 해야한다.

 

위 코드에서 재미난 부분이 있는데, 파일 이름에 ..이 포함되어 있다면 bad characters를 출력하도록 한 것이다.
아마도 문제가 Download 공격이니, 읽어오는 기능에서 공격하도록 하고 싶은게 아니였을까....?

 

@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)

위 코드는 read 페이지의 코드이다.
Get 메소드로 name에 파일 이름을 넣으면 uploads 폴더에 있는 해당 파일을 읽어서 출력하는 기능이다.

하지만, 이 기능에선 업로드에선 검증하였던 ..을 하지 않아서 Path Traversal 취약점이 발생하였다.

'Wargame > Dreamhack' 카테고리의 다른 글

[Dreamhack Web - Lv 1] csrf-1  (0) 2021.10.25
[Dreamhack Web - Lv 1] proxy-1  (0) 2021.10.25
[Dreamhack Web - Lv 1] xss-1  (0) 2021.10.23
[Dreamhack Web - Lv 1] welcome  (0) 2021.10.15
[Dreamhack Web - Lv 1] simple_sqli  (0) 2021.10.15