Dreamhack/Dreamhack Wargame (Challenge)

[3] IT 비전공자 [dreamhack] file-download-1 문제 풀기

imaginefuture-1 2024. 9. 23. 09:04

하핫 벌써 3번째 문제다!!

https://mokpo.tistory.com/29

 

 

역시나 이 블로그를 풀이법을 봤다

파일 다운로드 취약점이란 파일 다운로드 기능이 존재하는 웹에서 파일 다운로드 시

파일의 경로 및 파일명을 파라미터로 받아 처리하는 경우 이를 적절히 필터링 하지 않으면 공격자가 이를 조작하여 허용되지 않은 파일을 다운 받을 수 있고 임의의 위치에 있는 파일을 열람하거나 다운받는 것을

가능케 하는 취약점을 말한다.

출처: https://mokpo.tistory.com/29 [MSS:티스토리]

파라미터에 대한 설명은 아래에 있다

파라미터를 번역하면 매개변수라는 의미를 지니는데요, IT업계에서는 ‘소프트웨어나 시스템상의 작동에 영향을 미치며, 외부로부터 투입되는 데이터’라는 의미로 통용되고 있습니다. 파라미터를 활용하여 소프트웨어나 시스템을 작동시키기 때문에 다양한 활용도를 지니고 있어 없어서는 안되는 녀석이죠.

웹해킹 문제 사이트에 접속해보자

이미 내가 벌써 만들어 놓긴했는데 처음 들어가보면 my-first-memo, park은 없고 your uploaded memos만 뜬다

upload my memo에 들어가 똑같이 적어본다

페이지 경로는 /read?name=my-first-memo 이다.

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

 

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

출처: https://mokpo.tistory.com/29 [MSS:티스토리]

존재하지않는다고 뜬다

역시나 정답은 소스코드에 있다고!

소스코드를 보러가자!

 

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

https://blog.naver.com/sooftware/221595850854

참고로 소스코드 이쁘게 첨부하는 방법도 배웠다 (하하 뿌듯)

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

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

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

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

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

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)

요부분 설명 같다

read_memo() 함수에 정의된 내용을 보면 upload_memo() 와 다르게 ".." 대한

탐지가 존재하지 않는다.

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

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)

요부분 설명 같다

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

왜 ..는 웹 디렉토리 최상위 경로에 있다고 말하는거지?

../는 상위 경로를 나타냅니다. 상위 경로는 현재 폴더가 속한 폴더를 가리킵니다.

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

출처: https://mokpo.tistory.com/29 [MSS:티스토리]

하하 찾았다 이녀석!

소요시간 약 40분

파라미터, 상위 디렉토리, 디렉토리, 경로, 처음보는 용어들...

계속 보다보면 익숙해지겠지!

내일 정확히 뭔지 조교수님한테 바로 여쭤봐야겠다ㅎㅎ;;

차근차근 잘 따라해서 문제푼 것만해도 잘했다! 나야! 아자아자