Categories:

Tags:



Enumeration

Reading app.py reveals its mechanics.

  1. When first setup, DB is deleted and recreated.
  2. In recreation, uid of admin is not inserted.
  3. User inputs uid, upw and level are lower cased, and searched against certain keywords.
  4. When keywords are not found in user inputs, inputs are used in a query to get the uid.
  5. If query result is admin, return the flag.
┌──(m0nk3y@kali)-[~/DH/baby-sqlite]
└─$ cat app.py
#!/usr/bin/env python3
from flask import Flask, request, render_template, make_response, redirect, url_for, session, g
import urllib
import os
import sqlite3

app = Flask(__name__)
app.secret_key = os.urandom(32)
from flask import _app_ctx_stack

DATABASE = 'users.db'

def get_db():
    top = _app_ctx_stack.top
    if not hasattr(top, 'sqlite_db'):
        top.sqlite_db = sqlite3.connect(DATABASE)
    return top.sqlite_db


try:
    FLAG = open('./flag.txt', 'r').read()
except:
    FLAG = '[**FLAG**]'


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')

    uid = request.form.get('uid', '').lower()
    upw = request.form.get('upw', '').lower()
    level = request.form.get('level', '9').lower()

    sqli_filter = ['[', ']', ',', 'admin', 'select', '\'', '"', '\t', '\n', '\r', '\x08', '\x09', '\x00', '\x0b', '\x0d', ' ']
    for x in sqli_filter:
        if uid.find(x) != -1:
            return 'No Hack!'
        if upw.find(x) != -1:
            return 'No Hack!'
        if level.find(x) != -1:
            return 'No Hack!'


    with app.app_context():
        conn = get_db()
        query = f"SELECT uid FROM users WHERE uid='{uid}' and upw='{upw}' and level={level};"
        try:
            req = conn.execute(query)
            result = req.fetchone()

            if result is not None:
                uid = result[0]
                if uid == 'admin':
                    return FLAG
        except:
            return 'Error!'
    return 'Good!'


@app.teardown_appcontext
def close_connection(exception):
    top = _app_ctx_stack.top
    if hasattr(top, 'sqlite_db'):
        top.sqlite_db.close()


if __name__ == '__main__':
    os.system('rm -rf %s' % DATABASE)
    with app.app_context():
        conn = get_db()
        conn.execute('CREATE TABLE users (uid text, upw text, level integer);')
        conn.execute("INSERT INTO users VALUES ('dream','cometrue', 9);")
        conn.commit()

    app.run(host='0.0.0.0', port=8001)

Exploitation

In order to perform a SQL injection, there are a few hurdles we need to overcome.

  • uid column from users table does not contain the value admin.
  • User inputs uid and upw cannot be used in SQL injection since the input ‘ is filtered.
  • User inputs cannot contain any form of whitespaces, including spaces, tabs, and new lines.
  • admin cannot be directly supplied as the word admin is filtered.
  • UNION [ALL] SELECT is not an option since the word select is also filtered.
  • Chaining queries using ; is impossible as the execute() function in python’s SQLite module disables the usage of multiple queries by default.
  • SQLite does not perform URL, hex, or unicode decoding by default.

Some of the hurdles can be bypassed using simple bypass techniques.

  • User input level can be used to cause a SQL injection.
  • Whitespaces can be supplied by using comments (/**/).
  • admin can be forged by using SQLite’s built-in function char() and concat operator (||).

With these bypasses, we only need to find a way to add admin to our query result.
Since sending multiple queries is not an option, it seems finding a way to use UNION without SELECT is our best bet.

According to the SQLite’s official documentation, UNION statement should directly be followed by select-core.

Searching for the select-core reveals that it can start from values() instead of SELECT.

With all the information gathered, I’ll send a request that unions empty result for ‘uid’ with the word admin.

Post Exploitation

After a successful exploitation, I’m able to view the flag.