-
Notifications
You must be signed in to change notification settings - Fork 3
Example xinetd Problem
Problems in Berlyne are basically mircoservices that read the flag from /opt/flag.txt and make it somehow possible to access the flag using a security vulnerability.
Berlyne does not limit a problem in any way.
In this tutorial we will create a Berlyne problem that gets delivered via the internet super daemon xinetd. As mentioned, we could also write a service that handles connections by itself or set up a web server, as in the example web problem.
A Berlyne problem consists of three files, see creating problems page for details.
The full problem can be downloaded here
The service of our problem will be a nice little snake game, written by KoWu for a CTF.
This service has a format string vulnerability.
It just consists of the binary file snake.
Since snake is just a single player game that has to be played locally, we will use the super server xinetd to deliver input/output over a TCP socket.
As mentioned in the creating problems page, we can deploy the content of the problem either in a directory called content.
After creating the problem's directory in the problem's path of Berlyne (VAGR_DEPLOYMENT_PATH in settings.py) we can copy the binary in a folder called "content" within this directory:
cd problems
# create directories
mkdir -p snake/content/bin
cp ~/snake snake/content/bin
which gives us a directory structure like this:
problems/
└── snake/
└── content/
└── bin/
└── snake
Now, we just need a configuration file for xinetd:
service snake
{
type = UNLISTED
protocol = tcp
socket_type = stream
port = 6666
server = /opt/problem/bin/snake
user = snake
wait = no
}
This is a generic config file, that defines a service for xinetd.
Important to know here is:
- The TCP port used is 6666
- The binary executed by xinetd is stored as /opt/problem/bin/snake
- The user that executes the binary is called snake
So, with e.g. netcat installed, one could connect to the service with the command:
nc HOSTNAME 6666
we now save the xinetd service in the content directory as well.
# in the content dir snake/content
# create sub dir
mkdir xinetd
cd xinetd
echo "service snake
{
type = UNLISTED
protocol = tcp
socket_type = stream
port = 6666
server = /opt/problem/bin/snake
user = snake
wait = no
}" > snake_service_config
Which gives us a directory structure that looks like this:
problems/
└── snake/
└── content/
└── bin
└── snake
└── xinetd/
└── snake_service_config
The content directory will be synchronized to the underlying virtual machine later.
Except for the dl_only directory, which (if exists) is not synced to the VM, but can contain downloadable content, see creating problems page.
The main Berlyne configuration file is called config.json and also lies in the snake directory.
For the full functionality see the documentation in Creating problems. The configuration for our task is stored as config.json file, is written in JSON format and looks like this:
{
"category": "Exploit",
"desc": "<a href='{DL_binary}'>What</a> a nice little game. Wanna play?<br><pre>stty -icanon && nc {HOST} {PORT_6666}</pre>",
"ports": [{"guest": 6666,
"desc": "Website",
"host": 0}],
"points": 350,
"downloads": { "binary": "bin/snake" },
"tags": ["reversing", "ROP", "format string", "binary"],
"name": "Snake Game"
}
For the description of the fields, see creating problems or example web problem page.
Important to know here is:
-
The port the service uses (6666) will be forwarded to a random port (0) on the host system Berlyne runs on.
-
The file snake which is our binary in the directory snake/content will be offered as download.
-
The desc field will be formatted, replacing {DL_binary}, {HOST} and {PORT_6666} with the download link for the binary, the host address and the generated random port number that forwards to the service's port 6666.
Our problems folder structure, now looks like this:
problems/
└── snake/
└── config.json
└── content/
└── bin/
└── snake
└── xinetd/
└── snake_service_config
The setup file is the so called provisioning file.
That means it only runs once when the problem is started for the first time or when it is rebuild.
The Setup file is a shell script that runs with root privileges.
In our case it will do the following:
- Create a user for the service
- Make the binary executable
- Install xinetd
- Move the xinetd service config to the appropriate place
- Restart xinetd
That is done with the following script, which will be stored as setup file in the problems directory:
#!/bin/bash
# Make apt non interactive
export DEBIAN_FRONTEND=noninteractive
# Add user
useradd snake -s /bin/false
# Install xinetd
apt-get update
apt-get install -y xinetd
# Make file executable
chmod +x $CONTENT_DIR/bin/snake
# Move service config
mv $CONTENT_DIR/xinetd/snake_service_config /etc/xinetd.d/
# Restart xinetd with new service
service xinetd restart
The used environment variable $CONTENT_DIR is set by Berlyne and contains the path to the synced files (usually /opt/problem).
That's it!
Our folder structure now looks like this:
problems/
└── snake/
└── setup
└── config.json
└── content/
└── bin/
└── snake
└── xinetd/
└── snake_service_config
And the problem snake could now be installed and used via Berlynes WebUI