sonotri
  • Dreamhack: simple_sqli_chatgpt
    2024년 10월 01일 19시 55분 51초에 업로드 된 글입니다.
    작성자: sonootri

    1. 문제 설명

    #!/usr/bin/python3
    from flask import Flask, request, render_template, g
    import sqlite3
    import os
    import binascii
    
    app = Flask(__name__)
    app.secret_key = os.urandom(32)
    
    try:
        FLAG = open('./flag.txt', 'r').read()
    except:
        FLAG = '[**FLAG**]'
    
    DATABASE = "database.db"
    if os.path.exists(DATABASE) == False:
        db = sqlite3.connect(DATABASE)
        db.execute('create table users(userid char(100), userpassword char(100), userlevel integer);')
        db.execute(f'insert into users(userid, userpassword, userlevel) values ("guest", "guest", 0), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}", 0);')
        db.commit()
        db.close()
    
    def get_db():
        db = getattr(g, '_database', None)
        if db is None:
            db = g._database = sqlite3.connect(DATABASE)
        db.row_factory = sqlite3.Row
        return db
    
    def query_db(query, one=True):
        cur = get_db().execute(query)
        rv = cur.fetchall()
        cur.close()
        return (rv[0] if rv else None) if one else rv
    
    @app.teardown_appcontext
    def close_connection(exception):
        db = getattr(g, '_database', None)
        if db is not None:
            db.close()
    
    @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')
        else:
            userlevel = request.form.get('userlevel')
            res = query_db(f"select * from users where userlevel='{userlevel}'")
            if res:
                userid = res[0]
                userlevel = res[2]
                print(userid, userlevel)
                if userid == 'admin' and userlevel == 0:
                    return f'hello {userid} flag is {FLAG}'
                return f'<script>alert("hello {userid}");history.go(-1);</script>'
            return '<script>alert("wrong");history.go(-1);</script>'
    
    app.run(host='0.0.0.0', port=8000)

    2. 문제 풀이

    DATABASE = "database.db"
    if os.path.exists(DATABASE) == False:
        db = sqlite3.connect(DATABASE)
        db.execute('create table users(userid char(100), userpassword char(100), userlevel integer);')
        db.execute(f'insert into users(userid, userpassword, userlevel) values ("guest", "guest", 0), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}", 0);')
        db.commit()
        db.close()
    
    def get_db():
        db = getattr(g, '_database', None)
        if db is None:
            db = g._database = sqlite3.connect(DATABASE)
        db.row_factory = sqlite3.Row
        return db
    
    def query_db(query, one=True):
        cur = get_db().execute(query)
        rv = cur.fetchall()
        cur.close()
        return (rv[0] if rv else None) if one else rv

    userid, userpassword, userlevel: guest-guest-0, admin-랜덤값-0으로 되어있다.

    @app.teardown_appcontext
    def close_connection(exception):
        db = getattr(g, '_database', None)
        if db is not None:
            db.close()
    
    @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')
        else:
            userlevel = request.form.get('userlevel')
            res = query_db(f"select * from users where userlevel='{userlevel}'")
            if res:
                userid = res[0]
                userlevel = res[2]
                print(userid, userlevel)
                if userid == 'admin' and userlevel == 0:
                    return f'hello {userid} flag is {FLAG}'
                return f'<script>alert("hello {userid}");history.go(-1);</script>'
            return '<script>alert("wrong");history.go(-1);</script>'

     -> userlevel = request.form.get('userlevel'): 사용자가 입력한 userlevel 데이터를 가져온다.

    -> res = query_db(f"select * from users where userlevel='{userlevel}'"): query_db 함수는 DB에 쿼리를 실행하는 함수이며, 가져온 userlevel을 바탕으로 쿼리문을 실행한다.

    ->  res는 DB 쿼리 결과를 나타낸다.

    -> query_db는 리스트나 튜플 형태로 결과를 반환한다. 아까 userid, userpassword, userlevel 였으니 if문이 저렇게 쓰여지는거다.

    -> 사용자로부터 입력받은 userlevel을 직접 SQL 쿼리문에 삽입하기 때문에, SQL Injection 공격이 진행될 수 있다.

     

    userid == admin 그리고 userlevel == 0이어야 플래그가 반환된다. 쿼리문에 따라 우리가 0을 입력하면 guest와 admin 둘 다 조회될거다. 이후 조건문에 의해 admin이 아니게 되는것이다.

    select * from users where userlevel='{userlevel}'
    //이후 조건
    if res:
                userid = res[0]
                userlevel = res[2]
                print(userid, userlevel)
                if userid == 'admin' and userlevel == 0:
                    return f'hello {userid} flag is {FLAG}'
                return f'<script>alert("hello {userid}");history.go(-1);</script>'
            return '<script>alert("wrong");history.go(-1);</script>'

    3. 취약점

    <시도했지만 실패한 것들>

    1) 0'or'1 -> 1이 문자열로 해석되기 때문에 논리적 참이 아님. 생각을 잘 못했다.

    2) 0'or'1'='1 -> 참을 만들어, DB의 모든 데이터를 반환하는 것을 생각했음. 하지만 실패

    3) 0'or userid='admin -> 이것 또한 실패였음. 왜지

     

    <성공한 것들>

    1) 0' AND userid='admin -> where절 뒤에 AND를 사용해 조건을 더 붙이려고 했음

    2) 0'and userid='admin -> or 조건일때는 안 됬는데 이거는 된다.....?

    3) 0' union select * from users where userid='admin -> sql에서 사용되는 union을 사용해 쿼리문 확장

     

     

     

    'Dreamhack > 워게임' 카테고리의 다른 글

    webhacking.kr: old-50  (1) 2024.10.01
    webhacking.kr: old-27  (0) 2024.10.01
    Lord of Injection: germiln  (0) 2024.10.01
    Web Hacking_command-injection-1🚩  (3) 2024.09.28
    Web Hacking_simple_spqi🚩  (0) 2024.09.27
    댓글