Watching the Bad Guys with Cowrie


Photo by Christina Branco on Unsplash

Photo by Christina Branco on Unsplash


Use Cowrie as a honeypot to capture attack data and learn more about your attacker's methods.

Special Thanks: This article was made possible by support from Linux Professional Institute

For one reason or another, I stumbled across a relatively old piece of software the other day while researching some unrelated DevOps security news. Doing so reminded me that I hadn’t run a honeypot for around fifteen years, and it inspired me to see if there were any advances in honeypot functionality.

There are three key types of honeypots, which fall into categories base on interactivity:

  • A “low interaction” honeypot tempts attackers to probe a system or network with, for example, visible but inaccessible admin network ports for common applications.
  • A “medium interaction” honeypot might tempt an attacker with some interaction on the system itself (I'll look more at this type of honeypot in a moment).
  • A “high interaction” honeypot looks like a precious system and alludes to being fully open and exploitable. Any business offering a this type of honeypot publicly will most likely treat it as a disposable resource that can be resurrected with relative ease.


Because my professional background with Linux, I was initially drawn to Kippo, which then led me to Cowrie, a more-recently maintained fork of Kippo.

Cowrie appealed to me mainly because I wanted to use a container to capture some real attack data. I liked the idea of seeing precisely what commands an attacker might have been typing, with the aim of being able to gauge the sophistication and maturity level of an attack and whether the attack used manually input commands or automated bot behavior.

In this article, I’ll show you how to install Cowrie using Docker, attack it non-invasively, and then analyze the results. Cowrie describes itself on Docker Hub as “a medium interaction SSH and Telnet honeypot designed to log brute force attacks and the shell interaction performed by the attacker.” Intrigued? Let’s crack on. But a serious word of warning before you get started, see the “Danger, High Voltage!” box.

Danger, High Voltage!

Be advised that you need to be extremely selective about where you put honeypots. You are effectively leaving your front door open to a burglar so you can surveil them.

Just in case I any rattled cages, I wanted some clarity about the Acceptable Usage Policy (AUP) in place for AWS (Amazon Web Services) EC2 server instances, so I contacted the AWS team responsible for penetration tests by filling out an online form stating that I wanted to run a single honeypot.

I have to admit that I was a little surprised by the response, which was something along the lines of “only passive Honeypots are allowed.” For further clarification, I confirmed that systems that “appear” vulnerable are okay to run on AWS publicly. However, if an attacker can log into such a system, then it breaches their AUP. As a result, the rest of this article shows testing software on a locked-down (non-public) AWS instance, which was then destroyed shortly afterwards.

It’s safe to say that not all cloud providers or ISPs are equal, so check with your host before doing anything that might raise eyebrows. AWS frowns on all kinds of floods or denial or service (DoS) testing from what I could see. Additionally they don’t want the smaller instances used for penetration tests, such as the nano, small, and micro sizes.

However, I found that Digital Ocean actually wrote a tutorial on the older Kippo software. It’s well written making it worth a quick look, but it is deprecated on a number of levels. I have a Digital Ocean account, so I raised a support ticket checking their stance given the tutorial. Their response was refreshing. It alluded to the fact that so long as shared hardware resources weren’t affected by running servers for security research (servers are called “droplets” in this case) then their Terms of Service (TOS) wouldn’t be breached. As cloud servers follow the shared tenancy model, this made perfect sense. Later on in this article, you'll see that the Docker container offers a degree of isolation. If I was running Cowrie on Digital Ocean, I would limit that container’s CPU, IO, and kernel system calls to meet their TOS.

In short, whichever host you use (including running a honeypot on your home broadband), consider yourself suitably warned that you’re better of checking first.

Stack ‘Em Up

For speed, reproducibility, and a DevOps approach, I wanted to fire up Terraform on AWS to create a locked-down EC2 instance and then use Docker running Cowrie for testing. I started with this Docker Hub page, which provided a Dockerfile to view and then use to build a container.

To get Docker installed on my shiny Ubuntu 18.04 instance that Terraform built, I used the Docker Community Edition (CE) install instructions. If you don’t have Docker installed, then skip to the “Install from a repository” section in the instructions and continue from there. However, for testing, the route that I prefer is installing from Docker in its newer containerd incarnation as a compressed package, so I found the packages URL for my Ubuntu version (Bionic Beaver) and then ran the command as shown in Listing 1 to install the latest package I could find. I’m using m1.large for the instance size if it helps (which uses the amd64 architecture).

Listing 1: Installing Docker CE

$ wget
$ dpkg -i containerd.io_1.2.2-1_amd64.deb

$ wget
$ dpkg -i docker-ce-cli_18.09.1~3-0~ubuntu-bionic_amd64.deb

$ apt install -f # meet unmet package dependencies

$ wget
$ dpkg -i docker-ce_18.09.1~3-0~ubuntu-bionic_amd64.deb

As shown in Listing 1, there’s some tidying up of unmet dependencies midway through the installation.


Now that I’ve got a container run time working on a non-public host, I can build my Cowrie container.

Cowrie's author is Michel Oosterhof, who originally forked Kippo from what I can determine. Having looked through the Dockerfile a bit more closely, I can see that there are two ports exposed on the last line, which provide SSH and Telnet servers, as shown in Listing 2.

Listing 2: The Bottom of the Dockerfile

VOLUME [ "/cowrie/cowrie-git/var", "/cowrie/cowrie-git/etc" ]
ENTRYPOINT [ "cowrie" ]
CMD [ "start", "-n" ]
EXPOSE 2222 2223

Keep in mind the following about the Dockerfile: Two images are created, with one trimmed down for running (see the comments at the top of the Dockerfile); I'm using a Debian OS as my base for the images; the images will be built fresh from the latest GitHub source; and there’s a few fairly sizable chunks of Python involved.

Build It and They Will Come

To get started, I copy the Dockerfile to my server’s filesystem and then run the following command, which kicks off the image-building process beautifully if all goes well:

$ docker build -t cowrie

As you can see, the images built from that command begin with cowrie. Size-wise, the images come in at 553MB and 386MB for the run-time image. As promised in the Dockerfile comments, there’s a difference in image size between the unnamed <none> “builder” image and the “run-time” images, which is good news. Figure 1 shows what denotes success once your terminal has stopped scrolling with build information.

Figure 1: The docker ps command shows which container images are on the system.

Figure 1's output looks promising; next I will try to fire up Cowrie.

Buy Linux Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus