update cdn

This commit is contained in:
Jonathan
2025-10-11 13:08:07 +07:00
parent 3857f0bdc0
commit eb4f9ad157
27 changed files with 165 additions and 78 deletions
+38
View File
@@ -0,0 +1,38 @@
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
DEBIAN_FRONTEND=noninteractive
WORKDIR /app
# System deps: exiftool + sshd + bash (sqlite CLI not required for Python sqlite3)
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libimage-exiftool-perl \
openssh-server \
bash \
&& rm -rf /var/lib/apt/lists/*
# Unprivileged user for the app / SSH
RUN useradd -m -d /home/ctfuser -s /bin/bash ctfuser
# SSH minimal setup
RUN mkdir -p /run/sshd && chmod 755 /run/sshd && ssh-keygen -A
COPY chall/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY /chall /app
RUN chmod +x /app/entrypoint.sh && mkdir -p /app/uploads && chmod 755 /app/uploads
RUN mkdir -p /data /app/uploads /run/sshd \
&& chown -R ctfuser:ctfuser /app /app/uploads /data \
&& chmod 755 /app
EXPOSE 8000
EXPOSE 22
ENTRYPOINT ["/bin/bash", "/app/entrypoint.sh"]
+30
View File
@@ -0,0 +1,30 @@
## CDN
db used: sqlite
flag.txt: GEMASTIK{random sha256 generated on app start}
### feature:
[authentication required with login and register, register default as "user" role]
1. upload image
### Vulns
#### vuln1: SSTI on image Date Created metadata, exiftool cant insert this, need to write the image's blob
example:
```bash
(base) jons@01-20-jonathanmarbun:/mnt/c/1Jonathan/CTFS/gawe/gms25/web2/exploit$ exiftool -overwrite_original -IPTC:DateCreated="{{7*7}}" image.png
Warning: Invalid date format (use YYYY:mm:dd) in IPTC:DateCreated (ValueConvInv)
Nothing to do.
```
payload to inject:
```{{lipsum.__builtins__['open']('flag.txt').read()}}```
when editing the Date Created metadata manually, somehow it has limit of 46 char (but we can expand that to make it more by deleting the content of another metadata) -> check ssti.png
it probably have different behavior on another image file or format
payload: check exploit/exp3.py
#### vuln2:
### Patching Rule?
- dont remove flag.txt/changes its content
- ensure image metadata generation still available
- ensure exiftool still used
@@ -13,7 +13,7 @@ from werkzeug.utils import secure_filename
APP_DIR = os.path.dirname(os.path.abspath(__file__))
DB_PATH = os.path.join(APP_DIR, "data.db")
UPLOAD_DIR = os.path.join(APP_DIR, "uploads")
FLAG_PATH = os.path.join(APP_DIR, "flag.txt")
FLAG_PATH = os.path.join("/flag.txt")
ALLOWED_EXT = {"png", "jpg", "jpeg", "bmp"}
MAX_CONTENT_LENGTH = 8 * 1024 * 1024
@@ -261,5 +261,4 @@ def too_large(_):
if __name__ == "__main__":
with app.app_context():
init_db()
generate_flag_at_boot()
app.run(host="0.0.0.0", port=8000, debug=False)
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -euo pipefail
umask 002 # group-writable (helps with bind mounts)
APP_DIR="/app"
UPLOADS_DIR="${APP_DIR}/uploads"
DATA_DIR="/data"
DBFILE="${DATA_DIR}/app.db"
FLAG_FILE="/flag.txt"
mkdir -p "${UPLOADS_DIR}" "${DATA_DIR}"
# Make sure runtime dirs are writable (bind-mounts may ignore chown; that's OK)
chown -R ctfuser:ctfuser "${UPLOADS_DIR}" "${DATA_DIR}" 2>/dev/null || true
chmod 775 "${UPLOADS_DIR}" "${DATA_DIR}" || true
# ------- Start SSH (and set password if provided) -------
if [[ -n "${SSH_PASSWORD:-}" ]]; then
echo "ctfuser:${SSH_PASSWORD}" | chpasswd || true
fi
if command -v service >/dev/null 2>&1; then
service ssh start || /usr/sbin/sshd &
else
/usr/sbin/sshd &
fi
# -------------------------------------------------------
# Initialize DB AS ctfuser (so SQLite can write WAL/SHM next to it)
su -s /bin/bash -c "python - <<'PY'
import app as m
from contextlib import contextmanager
@contextmanager
def ctx():
with m.app.app_context():
yield
with ctx():
# your function should create tables if missing
m.init_db()
# OPTIONAL: if you had a generate_flag_at_boot, call it here as well
try:
m.generate_flag_at_boot()
except Exception:
pass
print('DB initialized by ctfuser.')
PY
" ctfuser
# OPTIONAL: if your filesystem dislikes WAL, uncomment:
# su -s /bin/bash -c \"python - <<'PY'
# import sqlite3
# import os
# db = sqlite3.connect(os.environ.get('DB_PATH', '${DBFILE}'))
# db.execute('PRAGMA journal_mode=DELETE;')
# db.execute('PRAGMA synchronous=NORMAL;')
# db.close()
# print('SQLite journal_mode=DELETE applied.')
# PY
# \" ctfuser
# Secure the flag file if present
if [ -d "${FLAG_FILE}" ]; then
if [ -f "${FLAG_FILE}" ]; then
chown root:root "${FLAG_FILE}" || true
chmod 444 "${FLAG_FILE}" || true
fi
else
if [ -f "${FLAG_FILE}" ]; then
chown root:root "${FLAG_FILE}" || true
chmod 444 "${FLAG_FILE}" || true
fi
fi
echo "Starting Flask app on 0.0.0.0:8000 as ctfuser…"
exec su -s /bin/bash -c "cd '${APP_DIR}' && python app.py" ctfuser
Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

+15
View File
@@ -0,0 +1,15 @@
version: "3.8"
services:
cdn_services:
container_name: cdn_container
hostname: cdn
build: .
ports:
- "4500:8000" # app
- "4522:22" # ssh
environment:
SECRET_KEY: "c75f1259a4c95bb31563405d488d7bf9c0eaf4d562fd13557624f8e18eb5cfff"
# Optional: set SSH password for ctfuser at runtime
SSH_PASSWORD: "root"
# Optional: override if your app reads it
restart: always
+1 -1
View File
@@ -7,7 +7,7 @@ import requests
print("SSTI (Vuln) Exploit — fixed HOST env, redirects, timeouts")
HOST = "http://localhost:4414"
HOST = "http://localhost:4500"
REGISTER_URL = f"{HOST}/register"
LOGIN_URL = f"{HOST}/login"
UPLOAD_URL = f"{HOST}/upload"
Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

-21
View File
@@ -1,21 +0,0 @@
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends libimage-exiftool-perl \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . /app
RUN chmod +x /app/entrypoint.sh \
&& mkdir -p /app/uploads && chmod 755 /app/uploads
EXPOSE 8000
ENTRYPOINT ["/bin/bash", "entrypoint.sh"]
-13
View File
@@ -1,13 +0,0 @@
version: "3.8"
services:
cdn:
build: .
container_name: cdn_container
ports:
- "11000:8000"
environment:
SECRET_KEY: "c75f1259a4c95bb31563405d488d7bf9c0eaf4d562fd13557624f8e18eb5cfff"
RESEED_FLAG: "1"
volumes:
- ./uploads:/app/uploads
restart: unless-stopped
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
mkdir -p /app/uploads
chmod 755 /app/uploads
python - <<'PY'
import os
import app as m
with m.app.app_context():
m.init_db()
m.generate_flag_at_boot()
print("DB & flag initialized.")
PY
FLAG_PATH="${FLAG_PATH:-/app/flag.txt}"
if [ -d "$FLAG_PATH" ]; then
if [ -f "$FLAG_PATH/flag.txt" ]; then
chown root:root "$FLAG_PATH/flag.txt" || true
chmod 444 "$FLAG_PATH/flag.txt" || true
fi
else
if [ -f "$FLAG_PATH" ]; then
chown root:root "$FLAG_PATH" || true
chmod 444 "$FLAG_PATH" || true
fi
fi
exec python app.py
-7
View File
@@ -1,7 +0,0 @@
# Convenience: reseed flag once without starting server
import hashlib, secrets, os
FLAG_PATH = os.path.join(os.path.dirname(__file__), "flag.txt")
sha = hashlib.sha256(secrets.token_bytes(32)).hexdigest()
with open(FLAG_PATH, "w", encoding="utf-8") as fh:
fh.write(f"GEMASTIK{{{sha}}}\n")
print("Flag reseeded:", open(FLAG_PATH).read().strip())