Building a Docker Service Honeypot
Introduction
One of the well-known misconfigurations for docker is an exposure of control API. By default, the docker client (CLI) communicates with the daemon using REST API over a UNIX socket, which is not accessible outside. Administrators can decide to expose Docker API for remote managing purposes or to use external tools like Portainer. When exposed, it allows to upload images, create new containers, execute arbitrary commands on the host system, etc. And there is no authentication by default. On Shodan, I managed to find more than 800 hosts with accessible services.
It was possible to retrieve a real configuration for a lot of hosts. Some of them were already infected by miners. I noticed, that a couple of random systems were just silly honeypots and returned static responses for every request and it was not possible to interact with them. So, I decided to have some fun and create a bit more sophisticated honeypot with additional features.
Architecture
The main idea was to mimicking a docker service instead of building a real one. I decided to use Python, Flask, and MongoDB as a database to be able to keep the current docker state and make it more realistic. It doesn’t just grab requests. I wanted to make it possible to check current status, download images, create new containers, and even run simple commands. Threat Intelligence gathering was one of the goals as well, so I wrote some additional code to integrate it with a local MISP instance.
The basic schema:
There are 3 main scripts:
- app.py - The main request handler. Responsible for requests logging and working with internal fake images and containers.
- analyzer.py - Implements request analyzing logic based on data from the database, shows the information about attacks.
- actions.py - Can be used to export Threat Intelligence data to\from MISP or local CSV files.
Implemented commands
Currently, the following docker commands are supported:
- info
- version
- images (image ls)
- image pull
- image build
- containers (container ls)
- container inspect
- container run
- container exec
- container cp
- container rm
Installation and running
The source code can be found on Gitlab (https://github.com/i223t/DockerTrap). The tool can be used as a standalone Flask app or as a docker container using docker-compose instructions.
There are 2 docker-compose files:
- docker-compose.yml - contains an internal MongoDB instance. It can be used just to quickly test the tool without any additional configurations or installations.
- docker-compose-external-db.yml - a configuration where MongoDB is not installed and it is necessary to set a connection URI for an external MongoDB instance. In this case, environment variables in the default.env files should be changed accordingly. I suggest using a free MongoDB Atlas account on https://www.mongodb.com/
Both installations can be launched using the commands:
1
2
3
docker-compose up
#or
docker-compose -f docker-compose-external-db.yml up
Tools
Since all the tools are inside docker it’s necessary to get the running docker container`s name:
1
docker ps
analyzer.py can be launched using the command (it might be necessary to change the container`s name)
1
docker exec -it dockertrap_docker_1 python3 /app/src/analyzer.py
actions.py can be used to export data or communicate with the MISP instance:
1
2
3
4
5
6
7
8
#to export events for the last 60 minutes as a CSV file
docker exec -it dockertrap_docker_1 python3 actions.py export_csv -f /tmp/events.csv -l 60
#to get events for the last 60 minutes and create a new event in MISP (event will be updated if it already exists)
docker exec -it dockertrap_docker_1 python3 actions.py export_misp -l 60 -e DockerTrap
#to export a published MISP event as a MISP feed
docker exec -it dockertrap_docker_1 python3 actions.py generate_misp_feed -e DockerTrap -d ./export/misp
Observations:
In 99.9% I saw just very simple connections from miner bots. The most common used technique is a new container creation and a malicious command line as a parameter. Something like:
The main idea here is to create a new container, escalate privileges by mounting the host`s root filesystem and run a command to download and configure a miner.
Entrypoint usage is a quite common way as well. It uses the same approach to break out:
Sometimes there is just a custom image:
It also caught some other exploitation techniques, like:
- changing the authorized_keys file on the host file system used for SSH authentication
- container creation attempts using the “build” command
- command execution using the containers which are already exist
Sorry, I didn’t make any screenshots for that.
Bonus
On the link below you can find the actual MISP feed which is populated on a scheduled basis: https://ti.chudamax.com/DockerTrap/export/