Do you know CTF (Capture The Flag)? Through CTF, you can learn while experiencing security issues. I thought I would try to explain the problem while creating a vulnerable server by myself. I created a server for CTF's rudimentary problems.
Due to its nature, the server is vulnerable. Please do not implement the content of this article as it is in a production environment.
A competition to find hidden flags in problems and earn points. Questions will be asked from various problems related to computer security. This time, we will focus on the field of the Web regarding vulnerabilities in Web applications.
--The format of the flag is myctf {flag}
.
--Three flags are hidden in the server.
In other words, there are 3 data in the form of myctf {flag}
on this web system, so please find it.
Docker Network Create a network between containers with docker network.
$ docker network create ctf-network
Docker Compose
docker-compose.yml
version: '3.3'
services:
app-python:
container_name: app-python
build:
context: ./python
dockerfile: Dockerfile
tty: true
volumes:
- ${PWD}/python/src:/app
ports:
- "80:8000"
networks:
- ctf-network
db1:
container_name: mysql
build:
context: ./mysql
dockerfile: Dockerfile
environment:
- MYSQL_USER=user
- MYSQL_PASSWORD=password
- MYSQL_ROOT_PASSWORD=password
volumes:
- ./mysql/mysql_conf:/etc/mysql/conf.d
- ./mysql/initdb.d:/docker-entrypoint-initdb.d
networks:
- ctf-network
networks:
ctf-network:
Please change user and password appropriately.
MySQL SQLite seems to be affordable and often used in CTF, but since it is built using Docker, I chose MySQL.
DockerFile
mysql/Dockerfile
FROM mysql:8.0
ADD ./ctf/flag.md /var/ctf/flag.md
RUN mkdir /var/log/mysql
RUN chown mysql:adm /var/log/mysql
Embed the flag in /var/ctf/flag.md
.
If solved in order, this will be the last flag.
mysql/ctf/flag.md
## flag.md
myctf{mission_complete}
Add a MySQL configuration file.
mysql/mysql_conf/custom.cnf
[mysqld]
default_authentication_plugin=mysql_native_password
character-set-server=utf8
collation-server=utf8_general_ci
general_log=ON
general_log_file=/var/log/mysql/query.log
secure-file-priv = ""
[client]
default-character-set=utf8
Add the secure-file-priv
setting to access the file.
sql:mysql/initdb.d/init.sql
SET CHARSET UTF8;
DROP DATABASE IF EXISTS ctf_db;
CREATE DATABASE ctf_db;
USE ctf_db;
DROP TABLE IF EXISTS users;
CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(30) NOT NULL,
last_name VARCHAR(30) NOT NULL,
job VARCHAR(30) NOT NULL,
delete_flag BOOLEAN NOT NULL DEFAULT FALSE
)DEFAULT CHARACTER SET=utf8;
INSERT INTO users (first_name, last_name, job)
VALUES
("Taro", "Yamada", "Server-side engineer"),
("Jiro", "Suzuki", "Front-end engineer"),
("Saburo", "Tanaka", "Infrastructure engineer"),
("Hanako", "Sato", "designer");
INSERT INTO users (first_name, last_name, job, delete_flag)
VALUES ("Ichiro", "Watanabe", "myctf{scf_sql_injection_flag}", TRUE); /*1st flag(Same table) */
DROP TABLE IF EXISTS flag;
CREATE TABLE flag (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
flag VARCHAR(60) NOT NULL,
create_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)DEFAULT CHARACTER SET=utf8;
INSERT INTO flag (flag)
VALUES ("myctf{next_flag_[/var/ctf/flag.md]}"); /*Second flag(Another table) */
Two flags are embedded in the Database. The first flag is that the users table is a logical delete table using delete_flag, which is embedded in the data that is supposed to be deleted. The second flag is embedded in the flag table, which is not referenced on this system.
Python
PHP is mainly used for CTF web problems, but since the backend environment at work is Python, I implemented it in Python. This was sober and difficult. (I made it from the PHP system first) I feel that Python is often used in actual CTF, such as automated scripts for flag acquisition.
When creating a web application with Python, it will be difficult if you do not use frameworks such as Django and Flask, but in order to create vulnerabilities, I will make as much as possible without relying on frameworks.
DockerFile
python/Dockerfile
FROM python:3.8
WORKDIR /app
CMD bash -c "pip install -r ./requirements.txt && python app.py"
EXPOSE 8000
Requirements Files
Some use external libraries. Describe the Lyle Rally to be installed in Requirements Files.
python/src/requirements.txt
Jinja2~=2.10
PyMySQL~=0.9
I had a hard time coding the HTML syntax in python, so I use Jinja2 as the template engine.
python/src/app.py
import socketserver
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
import pymysql.cursors
from jinja2 import Template, Environment, FileSystemLoader
PORT = 8000
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
conn = pymysql.connect(host='mysql',
user='root',
password='password',
db='ctf_db',
charset='utf8',
autocommit=True,
cursorclass=pymysql.cursors.DictCursor)
try:
url = urlparse(self.path)
if url.path == '/':
params = parse_qs(url.query)
query = params.get('q', [''])[0]
with conn.cursor() as cursor:
sql = f"SELECT * FROM users WHERE delete_flag=FALSE AND job LIKE '%{query}%';"
cursor.execute(sql)
users = cursor.fetchall()
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('index.html')
data = {
'query': query,
'users': users
}
disp_text = template.render(data)
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(disp_text.encode('utf-8'))
else:
self.send_response(404)
self.send_header('Content-type', 'text/html')
self.end_headers()
except Exception as e:
print(e)
self.send_response(500)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(str(e).encode('utf-8'))
finally:
conn.close()
with socketserver.TCPServer(("", PORT), MyHandler) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
Please correct user and password to the values changed by Docker Compose.
In the generation of SQL statement, the judgment of delete_flag = FALSE
is included because it is a logical deletion.
python/src/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>CTF experience</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<form class="form-inline my-3">
<div class="form-group mb-2">
<input type="text" name="q" class="form-control" placeholder="Job type search" value="{{ query }}">
</div>
<button type="submit" class="btn btn-primary mb-2 mx-sm-4">Search</button>
</form>
{% if query != '' %}
<p><span class="text-danger">{{ query }}</span>Search results</p>
{% endif %}
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Surname</th>
<th scope="col">Name</th>
<th scope="col">Occupation</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.last_name }}</td>
<td>{{ user.first_name }}</td>
<td>{{ user.job }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
This is the structure of the directory.
├─ docker-compose.yml
├─ mysql
│ ├─ Dockerfile
│ ├─ ctf
│ │ └─ flag.md
│ ├─ initdb.d
│ │ └─ init.sql
│ └─ mysql_conf
│ └─ custom.cnf
└─ python
├─ Dockerfile
└─ src
├─ app.py
├─ index.html
└─ requirements.txt
Execute
$ docker-compose up -d
Once the container is up, access http: // localhost
from your web browser.
You should see a page that looks like the image.
Enter "Engineer" in the job search text box and click the "Search" button.
It should have been searched.
This time it's just a SQL injection issue, There are also classic web-based cross-site scripting and many other vulnerabilities. Rather, it is full of vulnerabilities, and the problem-solving side may not know what to start with. If I feel like it, I'll try to create a cross-site scripting problem.
Security study session may be a "crime related to fraudulent command electromagnetic records" Although it was sometimes canceled (http://ozuma.sakura.ne.jp/sumida/2019/03/15/77/), This article itself, as an explanation of the vulnerability, I pray that you will not be a "guilty of fraudulent command electromagnetic records."
The completed repository is here The PHP version repository that was created in parallel is here The repository created with go language + PostgreSQL is here Since the go language is a statically typed language, it is difficult to guess the table definition because an error will occur if you UNION with the wrong type.
For the answer method of this question, see CTF beginners tried to build a problem server (web) [Answer].