Skip to content

Example xinetd Problem

rugo edited this page Feb 22, 2017 · 5 revisions

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

The service of our problem will be a nice little snake game, written by KoWu for a CTF. Snake Screenshot

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.

Content

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.

Config

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

Setup

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

Clone this wiki locally