Files
attack-defense-platform/services/blogpost/chall/app.py
T
2025-10-10 22:34:43 +07:00

249 lines
8.8 KiB
Python

import os
import sqlite3
from flask import *
from werkzeug.utils import *
from werkzeug.security import generate_password_hash, check_password_hash
import hashlib
import re
from markupsafe import escape as m_escape
APP_DIR = os.path.dirname(os.path.abspath(__file__))
UPLOAD_FOLDER = os.path.join(APP_DIR, "uploads")
DB_PATH = "/data/app.db"
FLAG_PATH = "/app/flag.txt"
ALLOWED_EXT = {'png', 'jpg', 'jpeg', 'bmp'}
app = Flask(__name__)
app.secret_key = os.urandom(24)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024
def get_db():
db = getattr(g, "_database", None)
if db is None:
db = g._database = sqlite3.connect(DB_PATH, check_same_thread=False)
db.row_factory = sqlite3.Row
return db
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, "_database", None)
if db is not None:
db.close()
@app.route("/register", methods=["GET", "POST"])
def register():
if request.method == "POST":
username = request.form.get("username", "").strip()
password = request.form.get("password", "").strip()
if not username or not password:
flash("Missing username or password")
return redirect(url_for("register"))
hashed = generate_password_hash(password)
db = get_db()
try:
db.execute("INSERT INTO users (username, password, role) VALUES (?, ?, ?)", (username, hashed, "user"))
db.commit()
flash("Registered. Please login.")
return redirect(url_for("login"))
except sqlite3.IntegrityError:
flash("Username already taken")
return redirect(url_for("register"))
return render_template("register.html")
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username", "").strip()
password = request.form.get("password", "").strip()
db = get_db()
cur = db.execute("SELECT id, username, password, role FROM users WHERE username = ?", (username,))
row = cur.fetchone()
if row and check_password_hash(row["password"], password):
session["user_id"] = row["id"]
session["username"] = row["username"]
session["role"] = row["role"]
flash("Logged in")
return redirect(url_for("index"))
else:
flash("Invalid credentials")
return render_template("login.html")
@app.route("/logout")
def logout():
session.clear()
flash("Logged out")
return redirect(url_for("index"))
@app.route("/", methods=["GET", "POST"])
def index():
if "user_id" not in session:
return redirect(url_for("login"))
user_id = session["user_id"]
db = get_db()
q = request.values.get("q", "").strip()
if q:
cur = db.execute(
"SELECT p.*, u.username AS author "
"FROM posts p LEFT JOIN users u ON p.author_id = u.id "
"WHERE p.author_id = ? AND (p.title LIKE ? OR p.content LIKE ?) "
"ORDER BY p.id DESC",
(user_id, f"%{q}%", f"%{q}%")
)
else:
cur = db.execute(
"SELECT p.*, u.username AS author "
"FROM posts p LEFT JOIN users u ON p.author_id = u.id "
"WHERE p.author_id = ? "
"ORDER BY p.id DESC",
(user_id,)
)
posts = cur.fetchall()
return render_template("index.html", posts=posts, q=q)
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXT
@app.route("/create", methods=["GET", "POST"])
def create_post():
if "user_id" not in session:
flash("Login required")
return redirect(url_for("login"))
if request.method == "POST":
title = request.form.get("title", "")
content = request.form.get("content", "")
file = request.files.get("image")
image_filename = None
metadata_text = ""
if file and allowed_file(file.filename):
original_filename = file.filename
save_path = os.path.join(app.config['UPLOAD_FOLDER'], original_filename)
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
file.save(save_path)
try:
cmd = f"exiftool {save_path}"
meta_file = save_path + ".meta"
full_cmd = f"{cmd} > {meta_file} 2>&1"
os_status = os.system(full_cmd)
if os.path.exists(meta_file):
with open(meta_file, "r", encoding="utf-8", errors="ignore") as mf:
metadata_text = mf.read()
else:
metadata_text = "no-metadata"
except Exception as e:
metadata_text = f"exif_err: {e}"
try:
h = hashlib.sha256()
with open(save_path, "rb") as fbin:
for chunk in iter(lambda: fbin.read(8192), b""):
h.update(chunk)
digest = h.hexdigest()
_, ext = os.path.splitext(original_filename)
ext = ext.lower() if ext else ""
new_filename = f"{digest}{ext}"
new_path = os.path.join(app.config['UPLOAD_FOLDER'], new_filename)
new_meta = new_path + ".meta"
if not os.path.exists(new_path):
os.replace(save_path, new_path)
else:
try:
os.remove(save_path)
except Exception:
pass
if os.path.exists(meta_file):
try:
os.replace(meta_file, new_meta)
except Exception:
try:
with open(meta_file, "rb") as mf_src, open(new_meta, "wb") as mf_dst:
mf_dst.write(mf_src.read())
os.remove(meta_file)
except Exception:
pass
image_filename = new_filename
if os.path.exists(new_meta):
try:
with open(new_meta, "r", encoding="utf-8", errors="ignore") as mf2:
metadata_text = mf2.read()
except Exception:
pass
except Exception as e:
image_filename = original_filename
db = get_db()
try:
cur = db.execute(
"INSERT INTO posts (title, content, image_filename, author_id) VALUES (?, ?, ?, ?)",
(title, content, image_filename, session['user_id'])
)
db.commit()
post_id = cur.lastrowid
metadata_insert = f"UPDATE posts SET metadata = '{metadata_text}' WHERE id = {post_id};"
db.executescript(metadata_insert)
db.commit()
except Exception as e:
db.execute(
"UPDATE posts SET metadata = ? WHERE id = ?",
(metadata_text, post_id if 'post_id' in locals() else None)
)
db.commit()
flash("Post created")
return redirect(url_for("index"))
else:
flash("Missing or invalid image (png/jpg/jpeg/bmp)")
return render_template("create_post.html")
@app.route('/uploads/<path:filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
@app.route("/post/<int:pid>")
def view_post(pid):
db = get_db()
cur = db.execute(
"SELECT p.*, u.username as author FROM posts p LEFT JOIN users u ON p.author_id = u.id WHERE p.id = ?",
(pid,)
)
post = cur.fetchone()
if not post:
abort(404)
return render_template("view_post.html", post=post)
@app.route("/profile")
def profile():
if "user_id" not in session:
flash("Login required")
return redirect(url_for("login"))
db = get_db()
cur = db.execute("SELECT id, username, role FROM users WHERE id = ?", (session["user_id"],))
user = cur.fetchone()
flag_content = None
with open(os.path.join(APP_DIR, "templates", "profile.html"), "r", encoding="utf-8") as fh:
profile_template = fh.read()
username = user["username"] if user else ""
profile_source = profile_template.replace("{{ user.username }}", username)
if user and user["role"] == "admin":
try:
with open(FLAG_PATH, "r") as f:
flag_content = f.read().strip()
except Exception:
flag_content = "flag not found"
return render_template_string(profile_source, user=user, flag=flag_content)
if __name__ == "__main__":
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
app.run(host="0.0.0.0", port=8000)