Shell script not killing nc process when quitting

139 Views Asked by At

I'm trying to create a simple honeypot using shell scripting. The script uses a while loop to keep a nc from stopping when the client closes the connection. I wrote a function to kill all processes, including the script itself, to make sure that netcat will stop listening after the script is closed.

When I start the script, 3 processes honeypot.sh and 1 process n are created, when I close the script using ctrl-c and check the ps -a, the 3 honeypot.sh are gone, but the single nc is still there, and I have to kill it manually using killall -nc.

One interesting thing about this last nc process, is that even when the script is closed, I am still able to connect to the server, it even shows the banner, but this time, when I close the connection, the nc disappear and the server goes down as it should. - I wanted the server to close fully when the script closes.

Can someone show me what I'm doing wrong?

#! /usr/bin/bash

port="$1"
honeypot_pid=()
touch honeypot.log

# Handle SIGNI and kill created processes
function ctrl_c()
{
    #kill "$honeypot_pid"
#   wait
#   cleanup
#   exit 1
    echo -e "\nEncerrando o servidor..."
    for pid in "${honeypot_pid[@]}"; do
        kill "$pid"
    done
    killall honeypot.sh
    exit 1
}

# Netcat server
server ()
{
    while true; do
        ( echo -e "--------" $(date) "--------\n" ; nc -vnlp $port < banner.txt) >>  honeypot.log 2>&1
    honeypot_pid+=($!)
    done &
}

# Handles ctrl-c
trap ctrl_c INT

echo "Initializing server..."

ifconfig
server

# Show real time log
tail -f ./honeypot.log

2

There are 2 best solutions below

0
Ed Morton On

I think by this:

server ()
{
    while true; do
        ( echo -e "--------" $(date) "--------\n" ; nc -vnlp $port < banner.txt) >>  honeypot.log 2>&1
    honeypot_pid+=($!)
    done &
}

you probably meant to do this:

server()
{
    while true; do
        {
            echo -e "-------- $(date) --------\n"
            nc -vnlp "$port" < banner.txt
        } >>  honeypot.log 2>&1
    done &
    honeypot_pid+=($!)
}

or similar (check the syntax) so you have a loop running in a separate background process, with nc running in the foreground of that process, and honeypot_pid containing the PID of that process so you can kill it later.

That would still leave nc orphaned though - I expect you can resolve that by enabling job control for that background process:

server()
{
    set -m
    while true; do
        {
            echo -e "-------- $(date) --------\n"
            nc -vnlp "$port" < banner.txt
        } >>  honeypot.log 2>&1
    done &
    honeypot_pid+=($!)
    set +m
}

and changing the kill command from kill "$pid" to kill -- -"$pid" so that kills the process group of that process so it'll kill the subshell and anything being run from it, rather than just the subshell.

0
tjm3772 On

This might be a cleaner implementation of what you're attempting:

#!/bin/bash
port=$1
ctrl_c() {
  [[ "$honeypot_pid" ]] && kill "$honeypot_pid"
  [[ "$tail_pid" ]] && kill "$tail_pid"
  killall honeypot.sh
}
trap ctrl_c INT

server() {
  printf '%s\n' "-------- $(date) --------"
  nc -vlnp "$port" < banner.txt &
  honeypot_pid=$!
}
touch honeypot.log
tail -f honeypot.log &
tail_pid=$!

while true ; do
  server >> honeypot.log 2>&1
  wait
  # here you might normally do something like:
  # echo "Restarting server..."
  # sleep 1
done

The goal is:

  • Put nc in the background, instead of the loop
  • Get the pid of the backgrounded command
  • Use wait to stop the program until that command exits. wait can be interrupted by a signal, so you'll still get into your trap handler if someone presses Ctrl+C.
  • If the backgrounded command exits by something other than the user pressing Ctrl+C, the loop will restart and you'll get a new command with its associated pid.
  • Once the user presses Ctrl+C, the pid of the current command will be signaled to exit.

In your original implementation, you don't have nc in the background. You have while true in the background, essentially a subshell running your nc in its own foreground. Since you don't have the pid of that nc you can't kill it, but when you do killall honeypot.sh you kill the subshell around it leaving its child orphaned (parent pid = 1) with no parent left to manage it.