Write-up Baby SQLite With Filter

Trần Minh HuyTrần Minh Huy
4 min read

Challenge Information

Challenge Name: Cookie Arena Baby SQLite With Filter

Challenge URL: http://103.97.125.56:30224/

Challenge Description: 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 the Login function.

Tools Used:

  • Burp Suite Community Edition

Exploitation Steps

Mở Burp Suite lên và quan sát trang web.

Tôi nhận thấy giao diện đăng nhập của trang web đơn giản, với hai ô nhập liệu và một nút "Submit".
Bắt đầu kiểm tra các chức năng và khai thác điểm yếu từ form đăng nhập.

Sau khi tôi nhập "test" vào cả hai ô và nhấn "Submit", trang web trả về thông báo "Good!". Điều này cho thấy quá trình đăng nhập đã diễn ra thành công.

Tôi quan sát thấy từ Burp Suite, yêu cầu được gửi đến endpoint /login với phương thức POST. Các tham số bao gồm:

  • uid=test

  • upw=test

Phản hồi của máy chủ là trạng thái HTTP 200 OK và trả về nội dung "Good!". Điều này cho thấy quá trình đăng nhập đã diễn ra thành công với giá trị uidupw bằng "test".

Bây giờ tôi sẽ tiếp tục thử nghiệm bằng cách thay đổi giá trị của các tham số uidupw với các payload tấn công SQL Injection để xem có thể khai thác được không.

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

Chúng ta có thể nhận thấy rằng để lấy được flag, cần phải có uid là 'admin'. Tuy nhiên, vấn đề là trong cơ sở dữ liệu không có user nào với uid = admin, và thêm vào đó là bộ lọc rất nghiêm ngặt. Do đó, tôi nghĩ rằng có thể thử khiến chương trình chỉ hiển thị một bảng duy nhất với cột uid = admin.

Trong tình huống này, vì hệ thống đã lọc từ khóa SELECT, chúng ta không thể sử dụng cú pháp thông thường để thực hiện truy vấn. Tuy nhiên, dựa trên tài liệu của SQLite, chúng ta có thể tìm cách thay thế và sử dụng mệnh đề VALUES để bắt đầu truy vấn.

Vì từ khoá "admin" bị chặn bởi bộ lọc, chúng ta có thể dùng hàm char() trong SQLite để chuyển đổi từng ký tự thành mã ASCII tương ứng. Sau đó, sử dụng toán tử nối chuỗi || để ghép các ký tự lại với nhau, tạo thành chuỗi "admin". Đồng thời, chúng ta có thể dùng các chú thích dạng /**/ để thay cho khoảng trắng, giúp vượt qua giới hạn của bộ lọc. Cách này cho phép tạo truy vấn SQL mà không bị các bộ lọc thông thường phát hiện.

Payload:

&level=0/**/UNION/**/VALUES/**/(char(97)||char(100)||char(109)||char(105)||char(110))

Và tôi đã tìm thấy flag.

CHH{uS1nG_5yN7@x_d149raM_65536e675c1ebf05287a3c778d2941de}

0
Subscribe to my newsletter

Read articles from Trần Minh Huy directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Trần Minh Huy
Trần Minh Huy