Skip to content

Instantly share code, notes, and snippets.

@cdeck3r
Last active January 13, 2024 09:27
Show Gist options
  • Save cdeck3r/5151c53b282431158a59091c0e26f27d to your computer and use it in GitHub Desktop.
Save cdeck3r/5151c53b282431158a59091c0e26f27d to your computer and use it in GitHub Desktop.
Setup of low interaction honeypot on a Raspberry Pi Zero W

Low Interaction Honeypot for the Raspberry Pi Zero W

Table of Contents

Goal and Objective

This honeypot system aims at the discovery of malicious activities in private home networks. It passively sits there and logs connection attempts and their network packet options.

The system's objective is to inform the administrator of a private home network about unusual activities, e.g. port scanning or strange connection attempts from computers within the network.

A typical use case is to raise an early red flag, when malicious software tries to find a host.

Disclaimer: Please be aware, this design is only an observer or sensor, when it receives network traffic. It neither guarantees the discovery of malicious events, not it guarantees the absence of such events. It also does not relieve you from all measures related to protect your network and computer systems, e.g. the use of firewalls and virus scanners, regular software updates, and personal awareness of recent security risks.

Approach

The honeypot utilizes

  • PSAD to detect port scans and other suspicious traffic
  • fwsnort to detect application level attacks

PSAD analyzes iptables log messages to detect port scans and other suspicious traffic. fwsnort adds iptables rules generated from SNORT rules. Latter define how malicious traffic looks like in terms of packet content from known attacks exploiting vulnerabilities. Using fwsnort iptables is enabled to inspect packet payloads and test it with SNORT rules. If a rule fires, this will be notified by PSAD.

Related projects:

What to learn more? - Check out https://adhdproject.github.io

Hardware Setup

The honeypot runs on a Raspberry Pi Zero W. It connects via Wifi to your home network.

Tools:

  • SD card flash utility: BalenaEtcher
  • wpa_passphrase utility, AFAIK it only works on Linux
  • ssh, you may use winscp on Windows

If you do not have wpa_passphrase utility, please check Raspi wireless setup for alternative procedures.

  1. Download Raspberry Pi OS Lite
  2. Flash image onto SD card using the BalenaEtcher
  3. Once the image is created, access the SD card on your Windows or Linux machine using a card reader.
  4. Copy an empty file named ssh into the card's boot folder.
  5. Create wpa_supplicant.conf file by running the commands below. Insert the country and your network SSID.
    cat << EOF > wpa_supplicant.conf
    ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
    update_config=1
    country=<Insert 2 letter ISO 3166-1 country code here>
    EOF
    
    wpa_passphrase "<your SSID here>" | grep -v "#psk" >> wpa_supplicant.conf
    
    it does not provide a prompt. Just enter the passphrase and hit enter, then it returns to the prompt.
  6. Copy the file wpa_supplicant.conf into the SD card's boot folder
  7. Insert card into Raspberry Pi and boot up.
  8. Give it about 5 minutes and try to ssh into the Raspi using the default login (user / pass): pi / raspberry

Software Setup

The software setup is sourced from a blog article on https://disloops.com/psad-on-raspberry-pi/. We took the description and created the script below. Copy it (using scp) onto the Raspberry Pi and run it as user pi.

There are some changes from the cited blogpost above:

  • Some rules created by fwsnort cause iptables to exit with no space left on device message when loading these rules. These iptables rules have a very extensive set of parameters. The script excludes the SIDs for snort rules causing this error.
  • Since we use the honeypot from within the home network, iptables's FORWARD chain is unused. fwsnort is called with --no-ipt-FORWARD and will not handle packets from the FORWARD chain.
  • The same reason applies to iptables OUTPUT chain. fwsnort is called with --no-ipt-OUTPUT and will not handle packets from the OUTPUT chain.
  • As a consequence, logging directives for ufw only log iptables's INPUT chain.

Altogether, there are more than 10.000 rules in the fwsnort chains. The script logs some rule stats in /var/log/fwsnort/fwsnort.log.

Honeypot Output

The script creates cronjobs to daily status PSAD status reports at 6am. The report as well as PSAD logs are contained in /var/log/psad/.

The author of the above-mentioned blog created the PSADify tool to convert Port Scan Attack Detector (PSAD) output into HTML. You find the daily report in /home/pi/status.html after it was created at 6am.

Config

The script can be started on a fresh RaspiOS installation. It defines two variables at the beginning:

MY_HOSTNAME="homeserver"
MY_TZ="Europe/Berlin"

Additionally, it requests a password change of the pi user.

Download and Install

You can directly download or even run the script from this gist.

Download script from this gist and redirect stream into honeypot.sh:

curl -sL https://gist.githubusercontent.com/cdeck3r/5151c53b282431158a59091c0e26f27d/raw/HoneyPiZeroW.md | sed -n -e '/^#!\/bin\/bash/,$p' | sed '$d' > honeypot.sh

Run script from this gist:

bash <(curl -sL https://gist.githubusercontent.com/cdeck3r/5151c53b282431158a59091c0e26f27d/raw/HoneyPiZeroW.md | sed -n -e '/^#!\/bin\/bash/,$p' | sed '$d')

Manual installation:

  • Copy and save the script below in file
  • Transfer the file to the Raspberry Pi. You may want to use winscp on Windows
  • Login to the Raspberry Pi with the user pi
  • Run the script file: bash <file>

Script

#!/bin/bash
set -e

MY_HOSTNAME="homeserver"
MY_TZ="Europe/Berlin" # see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

##################################
# 0. startup check
##################################

echo "Check startup user"

CURR_USER=$(id --user --name)
if [ "${CURR_USER}" != "pi" ]; then
    echo "User mismatch. Script must run as user: pi. Abort."
    exit 1
fi

##################################
# 1. change standard password
##################################

echo "Request password change"

# 1a. Current password: raspberry
# 1b. New password: <enter your new password>
# 1v. Retype new password: <enter your new password again>

passwd 

##################################
# 2. change hostname
##################################

echo "Change hostname to ${MY_HOSTNAME}"

NEW_NAME="${MY_HOSTNAME}"
CURR_HOSTNAME=$(hostname)

sudo -s -- <<EOF 
if [ "${CURR_HOSTNAME}" != "${NEW_NAME}" ]; then
    echo "${NEW_NAME}" >/etc/hostname
    sed -i "s/${CURR_HOSTNAME}/${NEW_NAME}/g" /etc/hosts
    hostname "${NEW_NAME}"
fi
EOF

##################################
# 3. change timezone
##################################

echo "Change timezone to ${MY_TZ}"

sudo -s -- <<EOF 
timedatectl set-timezone "${MY_TZ}"
EOF

##################################
# 4. Install software
##################################

echo "Install software: ufw, psad"

sudo -s -- <<EOF 
apt-get update
apt-get install -y ufw psad
apt-get -y --auto-remove purge avahi-daemon
EOF

##################################
# 5. Setup ufw logging
##################################

echo "Enable ufw"
# we only allow port 22

sudo -s -- <<EOF 
ufw --force reset
ufw allow SSH
ufw --force enable
systemctl enable ufw
systemctl restart ufw
EOF

echo "Configure ufw logs"
# ufw logs only in /var/log/ufw.log
sudo -s -- <<EOF
sed -i "s/^#& stop.*/\& stop/" /etc/rsyslog.d/20-ufw.conf 
systemctl restart rsyslog
EOF

sudo ufw logging on 

echo "Change ufw logging directives"
# change logging directives
LOG_DIRECTIVE1="-A INPUT -j LOG --log-tcp-options"
LOG_DIRECTIVE2="-A FORWARD -j LOG --log-tcp-options"
BEFORE_RULES="before.rules"
BEFORE6_RULES="before6.rules"

sudo -s -- <<EOF 
cat "/etc/ufw/${BEFORE_RULES}" | grep -v -e "${LOG_DIRECTIVE1}" | grep -v -e "${LOG_DIRECTIVE2}" | grep -v "COMMIT" > "${HOME}/${BEFORE_RULES}"
cat "/etc/ufw/${BEFORE6_RULES}" | grep -v -e "${LOG_DIRECTIVE1}" | grep -v -e "${LOG_DIRECTIVE2}" | grep -v "COMMIT" > "${HOME}/${BEFORE6_RULES}"

echo "${LOG_DIRECTIVE1}" >> "${HOME}/${BEFORE_RULES}"
echo "COMMIT" >> "${HOME}/${BEFORE_RULES}"
echo "${LOG_DIRECTIVE1}" >> "${HOME}/${BEFORE6_RULES}"
echo "COMMIT" >> "${HOME}/${BEFORE6_RULES}"

cp "${HOME}/${BEFORE_RULES}" "/etc/ufw/${BEFORE_RULES}"
cp "${HOME}/${BEFORE6_RULES}" "/etc/ufw/${BEFORE6_RULES}"

EOF

sudo ufw reload

# cleanup
rm -rf "${HOME}/before.rules"
rm -rf "${HOME}/before6.rules"

##################################
# 6. Configure PSAD 
##################################

echo "Configure PSAD Port Scan Detection"

sudo -s -- <<EOF
sed -i "s/^HOSTNAME.*/HOSTNAME ${HOSTNAME};/" /etc/psad/psad.conf
sed -i "s/^HOME_NET.*/HOME_NET any;/" /etc/psad/psad.conf
sed -i "s/^ALERTING_METHODS.*/ALERTING_METHODS noemail;/" /etc/psad/psad.conf
sed -i "s/^EXPECT_TCP_OPTIONS.*/EXPECT_TCP_OPTIONS Y;/" /etc/psad/psad.conf
sed -i "s/^AUTO_DETECT_JOURNALCTL.*/AUTO_DETECT_JOURNALCTL N;/" /etc/psad/psad.conf
sed -i "s/^ENABLE_SCAN_ARCHIVE.*/ENABLE_SCAN_ARCHIVE Y;/" /etc/psad/psad.conf
sed -i "s/^IMPORT_OLD_SCANS.*/IMPORT_OLD_SCANS Y;/" /etc/psad/psad.conf
sed -i "s/^IPT_SYSLOG_FILE.*/IPT_SYSLOG_FILE \/var\/log\/ufw.log;/" /etc/psad/psad.conf
EOF

sudo psad --HUP
sudo psad --sig-update

##################################
# 7. Install fwsnort
##################################

# if there is a previous installation
# flush the fwsnort chains
sudo -s -- <<EOF
[[ -x /usr/sbin/fwsnort ]] && { /usr/sbin/fwsnort --ipt-flush; }
EOF

echo "Download and unzip fwsnort"
cd; 
sudo rm -rf Downloads
mkdir -p Downloads && cd Downloads
wget --no-verbose https://github.com/mrash/fwsnort/archive/master.zip
unzip -q master.zip 
cd fwsnort-master
chmod a+x install.pl

echo "Install fwsnort"
# enable auto-yes
sed -i "s/my \$ans = '';/my \$ans = 'n';/g" install.pl
sudo -s -- <<EOF
./install.pl
EOF

echo "Configure fwsnort"
sudo -s -- <<EOF
sed -i "s/^HOME_NET.*/HOME_NET any;/" /etc/fwsnort/fwsnort.conf
EOF

# Create fwsnort startup script /root/fwsnort_startup.sh
sudo -s -- <<EOF
cat << 'FWSNORT' > /root/fwsnort_startup.sh
#!/bin/bash

FWSNORT_LOG=/var/log/fwsnort/fwsnort.log

/usr/sbin/fwsnort --update-rules
# run fwsnort to generate iptables rules
#   - exclude rules for FORWARD and OUTPUT chain
#   - exclude snort rules, which cause iptables to crash on Raspberry Pi Zero W
/usr/sbin/fwsnort --no-ipt-FORWARD --no-ipt-OUTPUT --exclude-sid "2025413,2025412,2025414,2025415,2030901,2031069,2031236,2031299,2032952,2033034,2033152"
/var/lib/fwsnort/fwsnort.sh

##
# some logging to FWSNORT_LOG
##

# Lines in rules file
echo "\$(date) - [Check] Lines in rules file: \$(wc -l /var/lib/fwsnort/fwsnort.save)" >> "\${FWSNORT_LOG}"
# List all fwsnort currently active iptables rules (lists the fwsnort chains). 
echo "\$(date) - [Check] Rules in fwsnort chains: \$(fwsnort --ipt-list | wc -l)" >> "\${FWSNORT_LOG}"
# iptables rules count src: https://serverfault.com/a/868091
echo "\$(date) - [Check] Rules in iptables: \$(iptables -n --list --line-numbers | sed '/^num\|^$\|^Chain/d' | wc -l)" >> "\${FWSNORT_LOG}"

# notify psad about new rules
psad --HUP
FWSNORT

EOF
sudo /root/fwsnort_startup.sh

# return to /home/pi and cleanup
cd; 
sudo rm -rf /home/pi/Downloads

##################################
# 8. Install cronjobs
##################################

echo "Add fwsnort startup script to root crontab"

# remove existing fwsnort startup script from crontab and re-add it
sudo -s -- <<EOF
crontab -l | grep -v 'PATH=' | grep -v 'fwsnort_startup.sh' | grep -v 'shutdown' | grep -v 'psad' | crontab - || { echo "Ignore error: $?"; }
(
    crontab -l
    echo "PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin"
    echo "@reboot sleep 300 && /root/fwsnort_startup.sh" 
    echo "15 0 * * * /sbin/shutdown -r now"
    echo "0 6 * * * /usr/sbin/psad --Status"
) | crontab - || {
    echo "Error adding cronjob. Code: $?"
}
EOF

sudo systemctl restart cron.service

##################################
# 9. Install PSADify
##################################

# download
echo "Download and unzip PSADify"
cd; 
sudo rm -rf Downloads
mkdir -p Downloads && cd Downloads
wget --no-verbose https://github.com/disloops/psadify/archive/refs/heads/master.zip
unzip -q master.zip 
mv psadify-master psadify

sudo -s -- <<EOF
cp -R psadify /root
chown -R root:root /root/psadify
EOF

echo "Add PSADify cronjob"

# remove existing PSADify cronjob from crontab and re-add it
sudo -s -- <<EOF
crontab -l | grep -v 'psadify.py' | crontab - || { echo "Ignore error: $?"; }
(
    crontab -l
    echo "0 6 * * *  python3 /root/psadify/psadify.py -o /home/pi/status.html && chown pi:pi /home/pi/status.html  >/dev/null 2>&1"
) | crontab - || {
    echo "Error adding cronjob. Code: $?"
}
EOF

sudo systemctl restart cron.service

##################################
# 10. Reduce power consumption
##################################

echo "Deactivate bluetooth"
rfkill block bluetooth

echo "Stop and disable bluetooth services"
declare -a SYSTEMD_SERVICES=("bluetooth" "hciuart")

# iterate through bluetooth services and disable them
for serv in "${SYSTEMD_SERVICES[@]}"; do
    systemctl is-active "${serv}" >/dev/null && {
sudo -s -- <<EOF 
        systemctl stop "${serv}" || { echo "Ignore error when stopping service: ${serv}"; }
        systemctl disable "${serv}" || { echo "Ignore error when disabling service: ${serv}"; }
EOF
    }
done
sudo systemctl daemon-reload

echo "Disable HDMI"
tvservice --off

echo "Disable actitivty LED"
ACT_LED=/sys/class/leds/led0/trigger
echo none | sudo tee "${ACT_LED}"

# disable ACT_LED at bootup
sudo -s -- <<EOF
crontab -l | grep -v "${ACT_LED}" | crontab - || { echo "Ignore error: $?"; }
(
    crontab -l
    echo "@reboot sleep 60 && echo none | tee "${ACT_LED}"" 
) | crontab - || {
    echo "Error adding cronjob. Code: $?"
}
EOF

sudo systemctl restart cron.service


echo "Some vcgencmd info..."
echo "---------------------"
VCGENCMDS="hostname && vcgencmd get_camera; vcgencmd get_throttled ; vcgencmd measure_temp;  vcgencmd measure_temp pmic; vcgencmd measure_volts core; vcgencmd measure_volts sdram_c; vcgencmd measure_volts sdram_i; vcgencmd measure_volts sdram_p; vcgencmd get_config total_mem; vcgencmd get_mem arm; vcgencmd get_mem gpu; vcgencmd mem_oom; vcgencmd display_power -1 0; vcgencmd display_power -1 1; vcgencmd display_power -1 2; vcgencmd display_power -1 3; vcgencmd display_power -1 7"
eval "${VCGENCMDS}"
echo "---------------------"
##################################
# Final cleanup
##################################

# return to /home/pi and cleanup
cd; 
sudo rm -rf /home/pi/Downloads
@disloops
Copy link

Hello Christian!

I am extremely glad that you found PSADify useful. Thanks very much for the issue you pointed out on Github, I have corrected it.

I have also incorporated the --no-ipt-OUTPUT option into my fwsnort commands, as you suggested, and updated my blog article to reflect that.

I looked for the no space left on device error message in my logs but did not see it anywhere. I assume that I did not experience this because I am using the more capable Raspberry Pi 3 Model B and you are using the Raspberry Pi Zero W. Still, I'd be interested to know exactly where to look for this error message so I can be aware of it.

Please keep in touch as you continue to gather data with your project and give me any further feedback you have for PSADify.

Best of luck!
@disloops

@cdeck3r
Copy link
Author

cdeck3r commented Aug 11, 2021

Hi Matt (@disloops),
great, this gist is of use for you. :-)

It took a while to identify iptables no space left on device error. I saw something was wrong after I ran the command fwsnort --ipt-list as you suggested in your blog. There was nothing added for the fwsnort chains. Inspecting the echos of my script I saw an unspecific error when running iptables-restore. It basically reported about a failed commit.
I then let fwsnort generate all rules into a file. I inserted an iptables command in front of each -A ... rule in that file. At the beginning, I added the bash shebang and the set -x command and let the script run. As a result, it added each rule separately to the iptables chains, so I could see which rule failed. I let the raspi test 30.000 rules generated by fwsnort (INPUT, OUTPUT, FORWARD and their ESTB twin parts). That took a while, because the more rule, the slower the addition of new rules gets. The test output was redirected into a file for later inspection. So, I saw that the failed rules were extraordinary long. I believe, the error has something to do with the memory contraints as you pointed out in your comment.

A good next step would be to transfer the PSAdify `status.html' file to some place I could easily review. Maybe I add a webserver, which will open a new port. ;-) I also thought about wiring a signal light to the Raspi in order to indicate a sitation requiring review.

Best regards
@cdeck3r

@disloops
Copy link

I added some very basic instructions for pushing the status.html file to an AWS S3 bucket in this post, it has worked well for me. The idea about the signal light is very cool.

Can I enlist your help with something? I am creating the list of "Last Attackers" by sorting all the IP address folders in /var/log/psad by ctime:

https://github.com/disloops/psadify/blob/2fe37e9dd89574ba05b924d7dabb0ed9a50b5d78/psadify.py#L46

files = sorted(glob.iglob('/var/log/psad/[1-2]*'), key=os.path.getctime, reverse=True)

However, I now notice that the ctime of the folder does not always update when one of the files it contains is updated. For example:

/var/log/psad $ sudo ls -lac 89.248.165.247
total 1692
drwx------     2 root root    4096 Aug 11 09:50 .
drwxr-xr-x 48894 root root 1691648 Aug 11 10:09 ..
-rw-------     1 root root    3566 Aug 11 10:09 192.168.1.130_email_alert
-rw-------     1 root root      33 Aug 11 10:09 192.168.1.130_packet_ctr
-rw-------     1 root root     197 Aug 11 09:50 192.168.1.130_signatures
-rw-------     1 root root      11 Aug 11 10:09 192.168.1.130_start_time
-rw-------     1 root root    2739 Aug 11 09:50 89.248.165.247_whois
-rw-------     1 root root       2 Aug 11 10:09 danger_level
-rw-------     1 root root       2 Aug 11 10:09 email_ctr

The ctime remains 9:50 while the files therein have been modified more recently. What is the correct way to create a list of directories, ordered by the last update to one of the files they contain?

I use ctime a total of five times throughout the script, each of which may require review.

@disloops
Copy link

Christian,

Update on the question above: I committed a change that addressed the issue and also corrected a rather serious error in the reporting on the "Last Attacks" page.

Please take a look at the commit and incorporate the latest version of the script into your project. I've only done minimal testing so far but I believe the changes are sound. Apologies for the updates as soon as you've finished releasing your project, but your interest in PSADify helped me to discover and correct the issue.

Update here: disloops/psadify@6efb719

@cdeck3r
Copy link
Author

cdeck3r commented Aug 12, 2021

Hi Matt,
I summarized the discussion in the following issue disloops/psadify#3. I will review your code and comment on this issue. Give me a couple of hours.

Christian

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment