Skip to content

Honeypot

Robots aren't our friends!

Sunday hackers either, and you know what? Let's give them a little lesson. You know, people who think stealing grannies will enrich themselves or advance the cause. Often they will either try by hand (if they are lame) to break into a server, or send their compromised pc armada try stuff. All day the internet traffic is saturated with these shitty bots, it's super boring. Here we will set them up and get as much information as possible about them. All of this will be used to feed DIY super blocklists (like the good granny soup) and available on the net for everyone.


image


Base configuration

Connect to the provider pannel and check the server IP. Let's use ssh to connect to root first. We will create a user with rights now.

adduser honeypot
passwd honeypot
usermod -aG sudo honeypot
  • Here we have changed the default port on 5588 (22 is for the honeypot)
  • We forbid root connection
  • We limit sessions to one
  • Also, we block attempts to up to three
  • We remove X11 and all horrible stuff

Go to the directory /etc/ssh and open sshd_config and modify with :

Protocol 2
Port 5588
PermitRootLogin no
MaxAuthTries 3
MaxSessions 1
AllowUsers "srv-admin"
DenyUsers root
AuthorizedKeysFile  .ssh/authorized_keys
KbdInteractiveAuthentication no
UsePAM yes
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no
LoginGraceTime 300
X11Forwarding no
ClientAliveInterval 0
PermitTTY yes
PrintMotd no
Banner /etc/banner.exemple
Subsystem   sftp    /usr/lib/ssh/sftp-server
LogLevel VERBOSE

Firewalld

Download firewalld and activate the service

sudo apt install firewalld
sudo systemctl enable --now firewalld.service
sudo firewall-cmd --set-default-zone=block

Open ports related to your service list on the "block" profile :

sudo firewall-cmd --add-port=21/tcp --zone=block --permanent
sudo firewall-cmd --add-port=22/tcp --zone=block --permanent
sudo firewall-cmd --add-port=23/tcp --zone=block --permanent
sudo firewall-cmd --add-port=25/tcp --zone=block --permanent
sudo firewall-cmd --add-port=80/tcp --zone=block --permanent
sudo firewall-cmd --add-port=110/tcp --zone=block --permanent
sudo firewall-cmd --add-port=123/tcp --zone=block --permanent
sudo firewall-cmd --add-port=143/tcp --zone=block --permanent
sudo firewall-cmd --add-port=161/tcp --zone=block --permanent
sudo firewall-cmd --add-port=389/tcp --zone=block --permanent
sudo firewall-cmd --add-port=443/tcp --zone=block --permanent
sudo firewall-cmd --add-port=445/tcp --zone=block --permanent
sudo firewall-cmd --add-port=1080/tcp --zone=block --permanent
sudo firewall-cmd --add-port=1433/tcp --zone=block --permanent
sudo firewall-cmd --add-port=1521/tcp --zone=block --permanent
sudo firewall-cmd --add-port=3306/tcp --zone=block --permanent
sudo firewall-cmd --add-port=5060/tcp --zone=block --permanent
sudo firewall-cmd --add-port=5432/tcp --zone=block --permanent
sudo firewall-cmd --add-port=5900/tcp --zone=block --permanent
sudo firewall-cmd --add-port=6379/tcp --zone=block --permanent
sudo firewall-cmd --add-port=6667/tcp --zone=block --permanent
sudo firewall-cmd --add-port=8080/tcp --zone=block --permanent
sudo firewall-cmd --add-port=9200/tcp --zone=block --permanent
sudo firewall-cmd --add-port=11211/tcp --zone=block --permanent

Dont forget your custom ssh port (not the 22 !)

sudo firewall-cmd --add-port=5588/tcp --zone=block --permanent
sudo firewall-cmd --reload
sudo systemctl restart firewalld.service

Fail2ban

Install and enable fail2ban :

sudo apt install fail2ban
sudo systemctl enable --now fail2ban.service

Enable SSH jail :

sudo nano /etc/fail2ban/jail.local
[sshd]

enabled = true
port = 5588
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 1w

Reload and restart fail2ban

sudo systemctl reload fail2ban.service && sudo systemctl restart fail2ban.service

Docker installation

sudo apt update

Install keys and trusted docker repository

sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"

Update, upgrade and install docker + docker-compose

sudo apt update
sudo apt upgrade
sudo apt install docker-ce docker-compose

Honeypot deployment

Create a base directory, here honeypot-config and add a docker-compose.yml

mkdir honeypot-config
cd honeypot-config
sudo nano docker-compose.yml

Paste this configuration :

version: '3.3'
services:
    honeypots:
        container_name: honeypots
        image: justsky/honeypots:latest   # latest, dev
        restart: unless-stopped
        command: --setup all
# Add your custom path to this folder
        volumes:
            - './honeypot_logs:/honeypots/logs'
# Dont change the internal ports, change only external            
        ports:
            - 21:21         # FTP
            - 22:22         # SSH
            - 23:23         # TELNET
            - 25:25         # SMTP
            - 80:80         # HTTP
            - 110:110       # POP3
            - 123:123       # NTP
            - 143:143       # IMAP
            - 161:161       # SNMP
            - 389:389       # LDAP
            - 443:443       # HTTPS
            - 445:445       # SMB
            - 1080:1080     # SOCKS5
            - 1433:1433     # MSSQL
            - 1521:1521     # ORACLE
            - 3306:3306     # MYSQL
            - 5060:5060     # SIP
            - 5432:5432     # POSTGRES
            - 5900:5900     # VNC
            - 6379:6379     # REDIS
            - 6667:6667     # IRC
            - 8080:8080     # HTTPPROXY
            - 9200:9200     # ELASTIC
            - 11211:11211   # MEMCACHE

Deploy the honeypot

sudo docker-compose up -d

Check ports with netstat :

sudo apt install net-tools
sudo netstat -tunlp

Exemple :

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:5899            0.0.0.0:*               LISTEN      87930/nginx: master
tcp        0      0 0.0.0.0:5900            0.0.0.0:*               LISTEN      77829/docker-proxy
tcp        0      0 0.0.0.0:33030           0.0.0.0:*               LISTEN      77917/docker-proxy
tcp        0      0 0.0.0.0:33031           0.0.0.0:*               LISTEN      77928/docker-proxy
tcp        0      0 0.0.0.0:33028           0.0.0.0:*               LISTEN      77896/docker-proxy
tcp        0      0 0.0.0.0:33029           0.0.0.0:*               LISTEN      77906/docker-proxy
tcp        0      0 0.0.0.0:5432            0.0.0.0:*               LISTEN      77818/docker-proxy
tcp        0      0 0.0.0.0:11211           0.0.0.0:*               LISTEN      77885/docker-proxy
tcp        0      0 0.0.0.0:5060            0.0.0.0:*               LISTEN      77804/docker-proxy
tcp        0      0 0.0.0.0:9200            0.0.0.0:*               LISTEN      77874/docker-proxy
tcp        0      0 0.0.0.0:1521            0.0.0.0:*               LISTEN      77781/docker-proxy
tcp        0      0 0.0.0.0:389             0.0.0.0:*               LISTEN      77731/docker-proxy
tcp        0      0 0.0.0.0:1433            0.0.0.0:*               LISTEN      77771/docker-proxy
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      77864/docker-proxy
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      77741/docker-proxy
tcp        0      0 0.0.0.0:445             0.0.0.0:*               LISTEN      77750/docker-proxy
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      77675/docker-proxy
tcp        0      0 0.0.0.0:110             0.0.0.0:*               LISTEN      77684/docker-proxy
tcp        0      0 0.0.0.0:123             0.0.0.0:*               LISTEN      77696/docker-proxy
tcp        0      0 0.0.0.0:6667            0.0.0.0:*               LISTEN      77850/docker-proxy
tcp        0      0 127.0.0.54:53           0.0.0.0:*               LISTEN      8081/systemd-resolv
tcp        0      0 0.0.0.0:1080            0.0.0.0:*               LISTEN      77760/docker-proxy
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      77792/docker-proxy
tcp        0      0 0.0.0.0:6379            0.0.0.0:*               LISTEN      77838/docker-proxy
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      8081/systemd-resolv
tcp        0      0 0.0.0.0:143             0.0.0.0:*               LISTEN      77708/docker-proxy
tcp        0      0 0.0.0.0:161             0.0.0.0:*               LISTEN      77718/docker-proxy
tcp6       0      0 :::5900                 :::*                    LISTEN      77834/docker-proxy
tcp6       0      0 :::33030                :::*                    LISTEN      77923/docker-proxy
tcp6       0      0 :::33031                :::*                    LISTEN      77932/docker-proxy
tcp6       0      0 :::33028                :::*                    LISTEN      77902/docker-proxy
tcp6       0      0 :::33029                :::*                    LISTEN      77913/docker-proxy
tcp6       0      0 :::5432                 :::*                    LISTEN      77822/docker-proxy
tcp6       0      0 :::11211                :::*                    LISTEN      77890/docker-proxy
tcp6       0      0 :::5060                 :::*                    LISTEN      77813/docker-proxy
tcp6       0      0 :::5588                 :::*                    LISTEN      1/systemd
tcp6       0      0 :::9200                 :::*                    LISTEN      77879/docker-proxy
tcp6       0      0 :::1521                 :::*                    LISTEN      77786/docker-proxy
tcp6       0      0 :::389                  :::*                    LISTEN      77735/docker-proxy
tcp6       0      0 :::1433                 :::*                    LISTEN      77776/docker-proxy
tcp6       0      0 :::8080                 :::*                    LISTEN      77869/docker-proxy
tcp6       0      0 :::443                  :::*                    LISTEN      77746/docker-proxy
tcp6       0      0 :::445                  :::*                    LISTEN      77755/docker-proxy
tcp6       0      0 :::80                   :::*                    LISTEN      77679/docker-proxy
tcp6       0      0 :::110                  :::*                    LISTEN      77691/docker-proxy
tcp6       0      0 :::123                  :::*                    LISTEN      77701/docker-proxy
tcp6       0      0 :::6667                 :::*                    LISTEN      77856/docker-proxy
tcp6       0      0 :::1080                 :::*                    LISTEN      77765/docker-proxy
tcp6       0      0 :::3306                 :::*                    LISTEN      77798/docker-proxy
tcp6       0      0 :::6379                 :::*                    LISTEN      77844/docker-proxy
tcp6       0      0 :::143                  :::*                    LISTEN      77712/docker-proxy
tcp6       0      0 :::161                  :::*                    LISTEN      77724/docker-proxy

List generation

We will now look at the generation of lists so that we can then retrieve them on the platform we want If you look in the log folder, you can see that many files are present.

image

If you let the trap run a bit and look in the http.log file

image

Many queries are present and it shows that it works, it says the format is different depending on the services, we will have to clean it and send it to a new file.

cd /home/honeypot/honeypot-config
mkdir listes
touch daily.sh
chmod +x daily.sh
sudo nano daily.sh

We will make a script that will run commands to recover ip in all log files. Then we will put the file in the previously created list folder. Here is the content of the script

cat /home/honeypot/honeypot-config/honeypot_logs/* >> /home/honeypot/honeypot-config/listes/ip.txt.tmp
cat /home/honeypot/honeypot-config/listes/ip.txt.tmp | grep -oP '"src_ip": "\K[^"]+' $FILE | sed 's/"//g' | sort | uniq > /home/honeypot/honeypot-config/listes/ip.txt

rm -rf /home/honeypot/honeypot-config/honeypot_logs/*.logs
rm -rf /home/honeypot/honeypot-config/honeypot_logs/honey*
rm -rf /home/honeypot/honeypot-config/listes/ip.txt.tmp

If you display the generated ip.txt file, you will see that this is much better

image

Now it's time to make sure we can get it back. Here I would make it available with a web server on a non-standard port. A script in my rest will curl the file in order to recover its content every night.

cron task

Since I use ubuntu, cron is already installed and preconfigured. In a more modern approach we could have made a systemd service (which is much better). Here for simplicity reasons, we will just add the script in cron.

sudo crontab -e

add this

0 2 * * * /home/honeypot/honeypot-config/daily.sh

Lists will update every day at 2am

Serve file

Now let's make our blocklist available with a simple web server

sudo apt install nginx

Go to the site-available nginx directory

cd /etc/nginx/sites-availables

Create a configuration for the website with 5899 port :

server {
    listen 5899;
    # Define the root directory for your static files
    root /home/honeypot/honeypot-config/listes;
    # Configure access to static files
    location /ip.txt {
    }

    # Additional configuration options can be added here

    # Define access log and error log locations
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    # Add any other server-specific configurations here
if ($block_ua) {
                return 403;
        }
}

Restart nginx and check the website "http://ServerIP:5899/ip.txt"

sudo systemctl reload nginx && sudo systemctl restart nginx

Nice, it's works !

image

Curl this page to get the list.

AbuseIP Reporting

Create an account on AbuseIP (best website for malicious ip reporting) In your profile, check your API key. Create a bash script :

sudo nano report-abuseip-v2.sh
#!/bin/bash

ABUSE_IP_DB_API_KEY="XXXXXXXXXXXXXXXXXXXX"
REPORTED_FILE="/home/honeypot/honeypot-config/listes/reported.txt" #Reported-ip-logs

report_ip() {
    local ip=$1
    # Trim leading and trailing whitespace and carriage returns
    ip=$(echo "$ip" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/\r//')

    # Validate the IP format (basic validation)
    if [[ ! $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
        echo "Invalid IP address format: $ip"
        return 1
    fi

    # Send the report to AbuseIPDB API
    response=$(curl -s -w "%{http_code}" -o /tmp/response_body.txt "https://api.abuseipdb.com/api/v2/report" \
        -H "Key: $ABUSE_IP_DB_API_KEY" \
        -d "ip=$ip" \
        -d "categories[]=10" \
        -d "categories[]=18" \
        -d "categories[]=21" \
        -d "categories[]=15" \
        -d "comment=Multiple attacks on Honeypot servers")

    response_code=$(tail -n1 <<< "$response")  
    response_body=$(cat /tmp/response_body.txt)  


    if [[ "$response_code" == "200" ]]; then
        echo "Successfully reported IP: $ip"
    else
        echo "Failed to report IP: $ip"
        echo "HTTP Status Code: $response_code"
        echo "Response Body: $response_body"
    fi
}

read_report_file() {
    local file=$1
    # Make sure the file exists before attempting to read
    if [ ! -f "$file" ]; then
        echo "Error: File not found: $file"
        exit 1
    fi

    while IFS= read -r line; do
        # Skip empty lines and lines starting with a comment (if any)
        [[ -z "$line" || "$line" =~ ^# ]] && continue
        report_ip "$line"
    done < "$file"
}


if [ $# -eq 0 ]; then
    echo "Usage: $0 <ip_address> | <report_file>"
    exit 1
fi


if [ $# -eq 2 ] && [ -f "$2" ]; then
    read_report_file "$2"
elif [ $# -eq 1 ] && [ -f "$1" ]; then
    read_report_file "$1"
else
    report_ip "$1"
fi

This script can report a file and push on AbuseIP with categories "bruteforce", "web attack", "spam", "hacking" ...

I'm adding a message "Multiple attacks on Honeypot servers"

If we run the script :

image

Here exemple for bruteforce :

image

Edit cron tasks :

sudo nano crontab -e

Paste :

0 3 * * * /home/honeypot/honeypot-config/report-abuseip-v2.sh ./listes/ip.txt

At 3am, the server will send the entire ip found on the previous day.

Base Hardening

Hardening on this part consists of some key steps that reduce the attack surface. A honeypot is a server that is targeted by very large amounts of ip every day and we want it to be robust to trap as many bots as possible without maintenance. The classic protections are already there for the ssh and fail2ban, here we will concentrate on preventing a privilege escalation in case of compromise of one of the elements.

We're going to make it impossible to use the root account and access its shell in case of privilege elevation or bad exploit !

Modify the /etc/passwd file and change root line with :

root:x:0:0:root:/root:/sbin/nologin

Disable TTY access :

sudo rm -rf /etc/securetty
sudo touch /etc/securetty
sudo chmod 600 /etc/securetty

Enforce the PAM configuration :

sudo nano /etc/pam.d/login
auth    required       pam_listfile.so \
        onerr=succeed  item=user  sense=deny  file=/etc/ssh/deniedusers

Restric file modification :

chmod 600 /etc/ssh/deniedusers

Edit the file sudo nano /etc/ssh/deniedusers and add "root"

Disable root password and groups :

sudo passwd -l root
sudo usermod -L root

Okay root now is safe, we can move on!

Apparmor

First install apparmor (under ubuntu it is already present and preconfigured).

This is a MAC protection that reduces potential interrelation between processes. A docker profile is present (attention, only rootfull, otherwise you will have to point to podman and why not SElinux).

sudo apt install apparmor apparmor-utils

Modify the kernel parameters on the /boot and add this line :

lsm=landlock,lockdown,yama,integrity,apparmor,bpf

Enable the service, reboot and enforce profiles :

sudo systemctl enable apparmor.service
sudo aa-enforce /etc/apparmor.d/*

Nginx Hardening

Enforce the nginx configuration with these elements :


server {
    listen 5899;
    # Define the root directory for your static files
    root /home/honeypot/honeypot-config/listes;
    # Configure access to static files
    location /ip.txt {

       if ($server_protocol = HTTP/1.0) {
                add_header Upgrade "HTTP/2.0";
                add_header Connection "Upgrade";
        return 426 "Upgrade Required: Upgrade your client to support more modern HTTP protocol";
        break;
        }

            limit_except GET {
                deny all;
            }

            add_header X-Frame-Options "DENY";
            add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted.cdn.com;";
            add_header X-XSS-Protection "1; mode=block";
            add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
            add_header X-Content-Type-Options "nosniff";
            add_header Referrer-Policy "no-referrer";
            add_header Permissions-Policy "geolocation=(); midi=(); notifications=(); push=(); sync-xhr=(); accelerometer=(); gyroscope=(); magnetometer=(); payment=(); usb=(); vr=(); camera=(); microphone=(); speaker=(); vibrate=(); ambient-light-sensor=(); autoplay=(); encrypted-media=(); execute-clipboard=(); document-domain=(); fullscreen=(); imagecapture=(); lazyload=(); legacy-image-formats=(); oversized-images=(); unoptimized-lossy-images=(); unoptimized-lossless-images=(); unsized-media=(); vertical-scroll=(); web-share=(); xr-spatial-tracking=();";
        server_tokens off;


        }



    location ~ \.(7z|asp|aspx|bak|bz|bz2|cer|cgi|conf|crt|gz|ini|jsp|key|log|pem|php|php7|rar|sh|sql|tar|zip)$ {
            return 403;
            break;
            }

    # Additional configuration options can be added here

    # Define access log and error log locations
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    # Add any other server-specific configurations here

if ($block_ua) {
                return 403;
        }

if ($is_blocked_common_exploits_path) {
    return 403;
}

}

map $request_uri $is_blocked_common_exploits_path {
    "~*//"                                                      1;
    "~*(boot.ini|etc/passwd|self/environ)"                      1;
    "~*(%2e%2e|%252e%252e|%u002e|%c0%2e)"                       1;
    "~*(\.\./\.\./|\.\.\.|%252e%252e%252e)"                     1;
    "~*(~|`|<|>|:|;|{|}|\[|\]|\(|\))"                           1;
    default 0;
}

We find essential limitations for our case, such as forcing HTTP2 or limiting only GET requests, which prevents someone from sending data to our machine (to retrieve the blocklist, this will be largely enough).

The headers required for protection against XSS injections are added.

I also took the initiative to prohibit any access to the types of interesting files, in case someone could make a directory listing or a missconfiguration arrives. At the end of the file, you can see some path traversal blockage that I could find on the net, which allows to reinforce all this.

A file blacklist-UA.conf is created in /nginx and present with an include in nginx.conf:

      include /etc/nginx/conf.d/*.conf;
      include /etc/nginx/sites-enabled/*;
      include blacklist-UA.conf;

Content :

map $http_user_agent $block_ua {
        default           0;
        ~*profound        1;
        ~*scrapyproject   1;
        ~*netcrawler      1;
        ~*nmap            1;
        ~*sqlmap          1;
        ~*slowhttptest    1;
        ~*nikto           1;
        ~*jersey          1;
        ~*brandwatch      1;
        ~*magpie-crawler  1;
        ~*mechanize       1;
        ~*python-requests 1;
        ~*redback         1;
        ~*python          1;
        ~*wget            1;
        ~*Pcore-HTTP      1;
        ~*HTTrack         1;
}

This file protect with suspect user agent detection on the website (nmap, sqlmap, nikto scanner, python ...). Avoid scanner and enumeration on the honeypot.


This hardening part will be improved over time. I strongly recommend following OWASP for good base security.