SSH honeypot

If you’ve ever wondered how to build a simple ssh honeypot, this post goes through the process with just the openssh source and some container environment like docker.

There are many existing projects you will find online, but here we will do it from scratch.

First we need the openssh source code. We’re not building for openbsd so we need the portable version. Get it from one of the mirrors on https://www.openssh.com/portable.html and extract it.

wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.9p2.tar.gz
tar xf openssh-9.9p2.tar.gz
cd openssh-9.9p2

Since openssh has no configuration for logging credentials we need to patch the code. Copy the below code snippet and put it inside a file.

--- ./auth-passwd.c	2024-03-11 07:20:49.000000000 +0200
+++ auth-passwd.c	2024-06-11 23:39:15.685103484 +0300
@@ -78,6 +78,12 @@
 {
 	Authctxt *authctxt = ssh->authctxt;
 	struct passwd *pw = authctxt->pw;
+
+	// Log the credentials in a hard coded file
+	FILE *f = fopen("/var/log/honeypot/sshd.log", "a");
+	fprintf(f, "from: %s username: %s password: %s\n", ssh->remote_ipaddr, authctxt->user, password);
+	fclose(f);
+
 	int result, ok = authctxt->valid;
 #if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
 	static int expire_checked = 0;

This will log username, password and source address in the hard-coded file /var/log/honeypot/sshd.log Now apply the patch file that was saved earlier:

patch -p1 < sshd.patch

Before we compile openssh we need to install some prerequisites. On a bare bones debian 12 installation this is what’s needed:

sudo apt update && sudo apt install gcc libz-dev libssl-dev make

Now run the configure script and build it

./configure
make

After it is built you should have the openssh binaries in your current directory. It is time to set up the container environment Follow the instructions on docker.com to install it. Here are the commands for debian, you should be able to find them for other distros as well.

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl 
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Give yourself access to the docker socket. After this restart your session or run newgrp
sudo usermod -aG docker $(logname)

You may install podman or some other contaier environment, some part of the setup may differ in that case.

Now let’s build the container image. Put the below text inside a file and name it Dockerfile

FROM debian:latest
WORKDIR /
COPY sshd .
COPY sshd-session .
COPY sshd_config .
COPY rsa .
COPY rsa.pub .
COPY ecdsa .
COPY ecdsa.pub .
COPY ed25519 .
COPY ed25519.pub .
COPY sshd_banner .
RUN mkdir /var/log/honeypot
RUN apt-get update && apt-get install openssl -y
RUN echo 'sshd:x:101:65534::/run/sshd:/usr/sbin/nologin' >> /etc/passwd
RUN echo 'sshd:!:19669::::::' >> /etc/shadow
RUN mkdir /var/empty
CMD ["/sshd", "-f", "/sshd_config", "-D", "-osshdsessionpath=/sshd-session"]
EXPOSE 22

Create the ssh server key pairs.

ssh-keygen -f rsa -N ''
ssh-keygen -f ecdsa -N ''
ssh-keygen -f ed25519 -N ''

Edit the sshd_config file to reflect their location in the container.

echo -e 'HostKey /rsa\nHostKey /ecdsa\nHostKey /ed25519' >> sshd_config

I have also made myself a banner to avoid a real person accidentally writing credentials to the server.

$ cat sshd_banner 
##############################################################
#                   _    _           _   _                   #
#                  / \  | | ___ _ __| |_| |                  #
#                 / _ \ | |/ _ \ '__| __| |                  #
#                / ___ \| |  __/ |  | |_|_|                  #
#               /_/   \_\_|\___|_|   \__(_)                  #
#                                                            #
#  By proceeding forward you are made fully aware that the   #
#   source address, username and password will be logged!    #
#                                                            #
#   This ssh server is intended to track bots, if you are    #
#     not one you should probably not proceed further!       #
#                                                            #
##############################################################
$

Your also need to add it to the sshd_config

echo 'Banner = /sshd_banner' >> sshd_config

The image is ready to be built. You can name and tag it however you like.

$ docker build -t honeypot:v1 .
<... output from docker build ...>
$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
honeypot     v1        62157c7c93ee   7 seconds ago   151MB
$

Run the container, you need to map port 22 inside it to some outside port, and also make the /var/log/honeypot directory visible outside it.

docker run -d --restart unless-stopped -p 2222:22 -v /var/log/honeypot:/var/log/honeypot honeypot:v1

This is about it, you can now test it. You probably want to expose port 22 and make it reachable from the internet if you want to collect brute force attempts.