본문 바로가기

Web Hacking/Dreamhack

[Dreamhack] baby-union write up

sqli 문제이다. 코드를 바로 보자!

 

CREATE DATABASE secret_db;
GRANT ALL PRIVILEGES ON secret_db.* TO 'dbuser'@'localhost' IDENTIFIED BY 'dbpass';

USE `secret_db`;
CREATE TABLE users (
  idx int auto_increment primary key,
  uid varchar(128) not null,
  upw varchar(128) not null,
  descr varchar(128) not null
);

INSERT INTO users (uid, upw, descr) values ('admin', 'apple', 'For admin');
INSERT INTO users (uid, upw, descr) values ('guest', 'melon', 'For guest');
INSERT INTO users (uid, upw, descr) values ('banana', 'test', 'For banana');
FLUSH PRIVILEGES;

CREATE TABLE fake_table_name (
  idx int auto_increment primary key,
  fake_col1 varchar(128) not null,
  fake_col2 varchar(128) not null,
  fake_col3 varchar(128) not null,
  fake_col4 varchar(128) not null
);

INSERT INTO fake_table_name (fake_col1, fake_col2, fake_col3, fake_col4) values ('flag is ', 'DH{sam','ple','flag}');

 

위 파일은 init.sql 파일이다. fake_table_name 예시이고 users말고 다른 테이블명을 찾아서 flag를 얻으면 될 것 같다.

 

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

 

위는 main 코드이다. uid와 upw를 입력 받고 그대로 사용하고 있어서 sqli가 발생한다. 

 

처음 접근을 바로 table명과 column명을 찾으려고 접근을 했으나 놓친 것이 있다.. 바로 user테이블의 컬럼 수...

 

 

컬럼 수가 4개다.. union의 특성상 앞 쿼리와 뒤의 쿼리의 반환 컬럼 수가 같아야 동작을 한다. 근데 나는 3개인줄 알고 뒤 컬럼을 3개로 맞추고 시도하고 있었다...ㅠㅠ 아무튼 mysql의 table에 대한 정보는 information_schema.tables에 존재한다.

(information_schema는 DB에 대한 모든 정보들이 담겨 있는 DB이다.)

 

information_schema.tables를 조회할 때 컬럼을 4개로 맞춰줘야한다.

 

a ' union select table_schema, table_name, table_catalog, table_type from information_schema.tables#

 

위와 같이 써준다. 참고로 아래 user.html을 보면 컬럼은 0, 1, 3번째 인덱스에 해당하는 데이터만 보여진다.

 

 

👇 쿼리 실행 결과

 

 

이제 컬럼명만 얻어 내면된다. 컬럼명은 information_schema.columns에 존재한다. 마찬가지로 컬럼 수를 맞춰준다.

 

a ' union select table_name, column_name, column_type, column_key from information_schema.columns#

 

실행결과 👇

 

 

 

이제 구한 컬럼과 테이블명을 이용해 flag를 얻어준다.