사이트에 path를 입력하는 칸이 보인다.
처음에는 Path Traversal 공격을 생각하고 있었다.
CTF에서 풀때 시간만 있었으면 풀었을 것 같은데 그날 바빠서 1시간밖에 못한게 한이다..
#!/usr/bin/env python3
import os
import string
from flask import Flask, request, abort, render_template, session
SECRETS_PATH = 'secrets/'
ALLOWED_CHARACTERS = string.ascii_letters + string.digits + '/'
app = Flask(__name__)
app.secret_key = os.urandom(32)
# create sample file
with open(f'{SECRETS_PATH}/sample', 'w') as f:
f.write('Hello, world :)')
# create flag file
flag_dir = SECRETS_PATH + os.urandom(32).hex()
os.mkdir(flag_dir)
flag_path = flag_dir + '/flag'
with open('/flag', 'r') as f0, open(flag_path, 'w') as f1:
f1.write(f0.read())
@app.route('/', methods=['GET'])
def get_index():
# safely save the secret into session data
session['secret'] = flag_path
# provide file read functionality
path = request.args.get('path')
if not isinstance(path, str) or path == '':
return render_template('index.html', msg='input the path!')
if any(ch not in ALLOWED_CHARACTERS for ch in path):
return render_template('index.html', msg='invalid path!')
full_path = f'./{SECRETS_PATH}{path}'
if not os.path.isfile(full_path):
return render_template('index.html', msg='invalid path!')
try:
with open(full_path, 'r') as f:
return render_template('index.html', msg=f.read())
except:
abort(500)
사용자입력(path)를 전달 할 수 있음
path를 통해 랜덤으로 생성된 Flag디렉터리를 정확히 전달해야함
- 브루트포스 불가능
이 path가 정상적으로 작동하려면 아래의 조건을 만족해야한다
- 공백 제외
- White_list=ascii_letters, 숫자, /
- 파일이 존재해야함
session['secret'] = flag_path
세션에 플래그 경로를 저장한다. 정리하면 아래의 경로와 같다.
flag_path=secrets/ + os.urandom(32).hex() + /flag
그럼 이 쿠키값에 저장된 세션을 해독해야하는데 비밀키로 urandom을 사용하였다. 세션에 저장되어있는 cookie value를 해독하면 될 것 같다.
그러던중 쿠키값에 저장된 세션이 헷갈려서 다시 공부를 해 보았다.
- 저장 위치: 세션 데이터는 서버 측에서 저장. 사용자 브라우저에는 세션의 식별자인 세션 ID만 저장
- 생성 방식: 사용자가 서버에 접속하거나 로그인할 때, 서버는 세션을 생성하고 고유한 세션 ID를 부여한다. 이 세션 ID는 보통 쿠키를 통해 클라이언트에 전달되어, 이후 클라이언트가 요청할 때마다 세션 ID를 함께 서버로 보낸다.
- 동작 방식: 서버는 클라이언트로부터 받은 세션 ID를 사용해 해당 세션에 저장된 데이터를 찾아 사용자 상태를 관리한다.
- 저장 위치: 쿠키는 클라이언트(사용자의 웹 브라우저)에 저장된다. 클라이언트는 HTTP 요청을 보낼 때마다 저장된 쿠키를 서버에 자동으로 전송한다.
- 생성 방식: 서버에서 쿠키를 생성하고 Set-Cookie 헤더를 통해 브라우저에 전달하면 브라우저는 쿠키를 저장한다. 이후 서버와의 통신 시마다 브라우저는 저장된 쿠키를 자동으로 서버에 보낸다.
- 동작 방식: 서버는 각 요청에서 특정 쿠키를 통해 사용자를 식별한다. 예를 들어, 로그인 상태를 유지하기 위해 사용자의 ID나 토큰 등을 쿠키에 저장하고, 이후 요청 시 쿠키에 있는 정보를 사용하여 사용자를 식별한다.
flask 쿠키와 세션 디코딩 기법을 찾던 중 아래와 같은 자료들을 확인 할 수 있었다.
https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/flask
여기서 나오는 방법중 비밀키가 있는 경우 아래와 같이 세션를 해독할 수 있다.
flask unsign설치
pip3 install flask-unsign
cookie value 해독
flask-unsign --decode --cookie '.eJwtxrsVgCAMBdBdWIAgCR-3IcnDxkrsPO5u463uExbswh32Pysa8ShwU2GUaTQEA8ZOtWXeGiZSyVVcc6-kU-Ck1BnNe4amFOc5jvB-FkYboQ.ZyMSKA.XMBGXHOtXIexUsE11-qyHfvtzxU'
위 명령어 실행시 아래와 같은 결과가 나온다.
{'secret': 'secrets/c04a6edcb54e6fc0a5eaec4d0783428efe16375db3970bf5ed0b094e8d93eb11/flag'}
그러면 플래그값을 얻을 수 있다.
CTF문제인데 1시간밖에 참여할 시간이 없어서 너무 아쉬웠다. 아쉬운만큼 나오자마자 바로 풀어보았다