627 lines
24 KiB
Bash
Executable File
627 lines
24 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
INTERFACES_FILE="/etc/network/interfaces"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
LOG_FILE="/var/log/portspoof.log"
|
|
|
|
# SHA256 hash of the shell access password.
|
|
# To change it: echo -n "yourpassword" | sha256sum
|
|
# Default password: admin
|
|
SHELL_PASSWORD_HASH="7ec275d16be840c7a810253fabf65237ca8559b1c2353734b161e63af847d8ed"
|
|
|
|
# ── helpers ──────────────────────────────────────────────────────────────────
|
|
|
|
get_interfaces() {
|
|
ls /sys/class/net/ 2>/dev/null | grep -v '^lo$'
|
|
}
|
|
|
|
get_ip() {
|
|
local iface="$1"
|
|
ip -4 addr show "$iface" 2>/dev/null | awk '/inet /{print $2}' | cut -d/ -f1
|
|
}
|
|
|
|
get_netmask() {
|
|
local iface="$1"
|
|
local prefix
|
|
prefix=$(ip -4 addr show "$iface" 2>/dev/null | awk '/inet /{print $2}' | cut -d/ -f2)
|
|
[ -z "$prefix" ] && return
|
|
python3 -c "import ipaddress; print(ipaddress.IPv4Network('0.0.0.0/$prefix', strict=False).netmask)" 2>/dev/null
|
|
}
|
|
|
|
get_gateway() {
|
|
ip route show default 2>/dev/null | awk '/default/{print $3; exit}'
|
|
}
|
|
|
|
is_dhcp() {
|
|
local iface="$1"
|
|
grep -A5 "iface $iface" "$INTERFACES_FILE" 2>/dev/null | grep -q "dhcp"
|
|
}
|
|
|
|
# ── arrow-key menu ────────────────────────────────────────────────────────────
|
|
# Usage: arrow_menu result_var "Item 1" "Item 2" ...
|
|
# Sets result_var to the 0-based index of the chosen item.
|
|
|
|
arrow_menu() {
|
|
local -n _res="$1"; shift
|
|
local items=("$@")
|
|
local count=${#items[@]}
|
|
local cur=0
|
|
|
|
tput civis
|
|
trap 'tput cnorm; echo; exit 130' INT
|
|
|
|
_draw_menu() {
|
|
for (( i=0; i<count; i++ )); do
|
|
tput el
|
|
if [ "$i" -eq "$cur" ]; then
|
|
printf " \e[1;7m %-72s \e[0m\n" "${items[$i]}"
|
|
else
|
|
printf " %-72s\n" "${items[$i]}"
|
|
fi
|
|
done
|
|
tput cuu "$count"
|
|
}
|
|
|
|
# reserve lines
|
|
for (( i=0; i<count; i++ )); do printf "\n"; done
|
|
tput cuu "$count"
|
|
_draw_menu
|
|
|
|
local key esc
|
|
while true; do
|
|
IFS= read -rsn1 key
|
|
if [[ "$key" == $'\x1b' ]]; then
|
|
IFS= read -rsn2 -t 0.05 esc
|
|
key+="$esc"
|
|
fi
|
|
case "$key" in
|
|
$'\x1b[A') (( cur = (cur - 1 + count) % count )); _draw_menu ;;
|
|
$'\x1b[B') (( cur = (cur + 1) % count )); _draw_menu ;;
|
|
'') tput cud "$count"; break ;;
|
|
esac
|
|
done
|
|
|
|
tput cnorm
|
|
trap - INT
|
|
_res=$cur
|
|
}
|
|
|
|
# ── status box ────────────────────────────────────────────────────────────────
|
|
|
|
status_box() {
|
|
echo " ╔══════════════════════════════════════════════════════════════════════════╗"
|
|
echo " ║ DAProgs HoneyPot Server. ║"
|
|
echo " ╠══════════════════════════════════════════════════════════════════════════╣"
|
|
echo " ║ Network Interface Status ║"
|
|
echo " ╠══════════════════════════════════════════════════════════════════════════╣"
|
|
echo " ║ v0.14 ║"
|
|
|
|
local ifaces
|
|
mapfile -t ifaces < <(get_interfaces)
|
|
|
|
if [ ${#ifaces[@]} -eq 0 ]; then
|
|
echo " ║ No interfaces found ║"
|
|
echo " ║ ║"
|
|
else
|
|
for iface in "${ifaces[@]}"; do
|
|
local ip netmask gw method
|
|
ip=$(get_ip "$iface")
|
|
netmask=$(get_netmask "$iface")
|
|
gw=$(get_gateway)
|
|
ip=${ip:-"(no IP)"}
|
|
netmask=${netmask:-"(unknown)"}
|
|
gw=${gw:-"(none)"}
|
|
|
|
if is_dhcp "$iface"; then
|
|
method="DHCP"
|
|
elif grep -A5 "iface $iface" "$INTERFACES_FILE" 2>/dev/null | grep -q "static"; then
|
|
method="Static"
|
|
else
|
|
method="Unknown"
|
|
fi
|
|
|
|
printf " ║ Interface : %-20s Mode: %-10s ║\n" "$iface" "$method"
|
|
printf " ║ IP Address: %-45s ║\n" "$ip"
|
|
printf " ║ Netmask : %-45s ║\n" "$netmask"
|
|
printf " ║ Gateway : %-45s ║\n" "$gw"
|
|
echo " ║ ║"
|
|
done
|
|
fi
|
|
|
|
echo " ╚══════════════════════════════════════════════════════════════════════════╝"
|
|
}
|
|
|
|
show_status() {
|
|
clear
|
|
echo ""
|
|
status_box
|
|
echo ""
|
|
echo " Use ↑ ↓ to navigate, Enter to select, Ctrl+C to exit"
|
|
echo ""
|
|
}
|
|
|
|
# ── select interface ──────────────────────────────────────────────────────────
|
|
|
|
select_interface() {
|
|
local ifaces
|
|
mapfile -t ifaces < <(get_interfaces)
|
|
|
|
if [ ${#ifaces[@]} -eq 0 ]; then
|
|
echo "No network interfaces found."
|
|
return 1
|
|
fi
|
|
|
|
if [ ${#ifaces[@]} -eq 1 ]; then
|
|
SELECTED_IFACE="${ifaces[0]}"
|
|
return 0
|
|
fi
|
|
|
|
clear
|
|
echo ""
|
|
echo "╔══════════════════════════════════════════════════════════════════════════╗"
|
|
echo "║ Select Interface ║"
|
|
echo "╚══════════════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
|
|
local choice
|
|
arrow_menu choice "${ifaces[@]}"
|
|
echo ""
|
|
SELECTED_IFACE="${ifaces[$choice]}"
|
|
}
|
|
|
|
# ── validate IPv4 ─────────────────────────────────────────────────────────────
|
|
|
|
valid_ip() {
|
|
local ip="$1"
|
|
[[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1
|
|
IFS='.' read -ra octets <<< "$ip"
|
|
for octet in "${octets[@]}"; do
|
|
[ "$octet" -le 255 ] || return 1
|
|
done
|
|
return 0
|
|
}
|
|
|
|
# ── set static IP ─────────────────────────────────────────────────────────────
|
|
|
|
set_static() {
|
|
local iface="$1"
|
|
local new_ip new_netmask new_gw
|
|
|
|
clear
|
|
echo ""
|
|
echo "╔══════════════════════════════════════════════════════════════════════════╗"
|
|
printf "║ Configure Static IP ── Interface: %-34s║\n" "$iface "
|
|
echo "╚══════════════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
|
|
local cur_ip cur_mask cur_gw
|
|
cur_ip=$(get_ip "$iface")
|
|
cur_mask=$(get_netmask "$iface")
|
|
cur_gw=$(get_gateway)
|
|
|
|
while true; do
|
|
read -rp " IP address [${cur_ip:-?}]: " new_ip
|
|
new_ip="${new_ip:-$cur_ip}"
|
|
valid_ip "$new_ip" && break
|
|
echo " Invalid IP address. Try again."
|
|
done
|
|
|
|
while true; do
|
|
read -rp " Netmask [${cur_mask:-255.255.255.0}]: " new_netmask
|
|
new_netmask="${new_netmask:-${cur_mask:-255.255.255.0}}"
|
|
valid_ip "$new_netmask" && break
|
|
echo " Invalid netmask. Try again."
|
|
done
|
|
|
|
while true; do
|
|
read -rp " Gateway [${cur_gw:-?}]: " new_gw
|
|
new_gw="${new_gw:-$cur_gw}"
|
|
valid_ip "$new_gw" && break
|
|
echo " Invalid gateway. Try again."
|
|
done
|
|
|
|
echo ""
|
|
echo "┌──────────────────────────────────────────────────────────────────────────┐"
|
|
echo "│ Summary │"
|
|
printf "│ Interface : %-58s │\n" "$iface"
|
|
printf "│ IP Address: %-58s │\n" "$new_ip"
|
|
printf "│ Netmask : %-58s │\n" "$new_netmask"
|
|
printf "│ Gateway : %-58s │\n" "$new_gw"
|
|
echo "└──────────────────────────────────────────────────────────────────────────┘"
|
|
echo ""
|
|
read -rp "Apply these settings? [y/N]: " confirm
|
|
[[ "$confirm" =~ ^[Yy]$ ]] || { echo "Cancelled."; return; }
|
|
|
|
if [ -f "$INTERFACES_FILE" ]; then
|
|
cp "$INTERFACES_FILE" "${INTERFACES_FILE}.bak.$(date +%Y%m%d%H%M%S)"
|
|
fi
|
|
|
|
write_interfaces "$iface" "static" "$new_ip" "$new_netmask" "$new_gw"
|
|
|
|
echo ""
|
|
echo " Written to $INTERFACES_FILE"
|
|
echo " Applying configuration..."
|
|
|
|
if command -v ifdown &>/dev/null; then
|
|
ifdown "$iface" 2>/dev/null
|
|
ifup "$iface" 2>/dev/null && echo " Done." || echo " ifup failed — you may need to reconnect manually."
|
|
else
|
|
ip addr flush dev "$iface" 2>/dev/null
|
|
ip addr add "${new_ip}/$(mask_to_cidr "$new_netmask")" dev "$iface" 2>/dev/null
|
|
ip route add default via "$new_gw" 2>/dev/null
|
|
ip link set "$iface" up 2>/dev/null
|
|
echo " Done (applied via iproute2)."
|
|
fi
|
|
}
|
|
|
|
# ── set DHCP ──────────────────────────────────────────────────────────────────
|
|
|
|
set_dhcp() {
|
|
local iface="$1"
|
|
|
|
echo ""
|
|
read -rp "Switch $iface to DHCP? [y/N]: " confirm
|
|
[[ "$confirm" =~ ^[Yy]$ ]] || { echo "Cancelled."; return; }
|
|
|
|
if [ -f "$INTERFACES_FILE" ]; then
|
|
cp "$INTERFACES_FILE" "${INTERFACES_FILE}.bak.$(date +%Y%m%d%H%M%S)"
|
|
fi
|
|
|
|
write_interfaces "$iface" "dhcp"
|
|
|
|
echo " Written to $INTERFACES_FILE"
|
|
echo " Applying configuration..."
|
|
|
|
if command -v ifdown &>/dev/null; then
|
|
ifdown "$iface" 2>/dev/null
|
|
ifup "$iface" 2>/dev/null && echo " Done." || echo " ifup failed — check your DHCP server."
|
|
else
|
|
ip addr flush dev "$iface" 2>/dev/null
|
|
dhclient "$iface" 2>/dev/null && echo " Done." || echo " dhclient failed."
|
|
fi
|
|
}
|
|
|
|
# ── write /etc/network/interfaces ────────────────────────────────────────────
|
|
|
|
write_interfaces() {
|
|
local target_iface="$1"
|
|
local method="$2"
|
|
local new_ip="$3"
|
|
local new_netmask="$4"
|
|
local new_gw="$5"
|
|
|
|
local tmpfile
|
|
tmpfile=$(mktemp)
|
|
|
|
if ! grep -q "iface lo" "$INTERFACES_FILE" 2>/dev/null; then
|
|
printf "auto lo\niface lo inet loopback\n\n" >> "$tmpfile"
|
|
fi
|
|
|
|
if [ -f "$INTERFACES_FILE" ]; then
|
|
awk -v iface="$target_iface" '
|
|
/^(auto|allow-hotplug|iface) / && $0 ~ iface { skip=1 }
|
|
skip && /^(auto|allow-hotplug|iface) / && $0 !~ iface { skip=0 }
|
|
!skip { print }
|
|
' "$INTERFACES_FILE" >> "$tmpfile"
|
|
fi
|
|
|
|
{
|
|
echo ""
|
|
echo "auto $target_iface"
|
|
if [ "$method" = "static" ]; then
|
|
echo "iface $target_iface inet static"
|
|
echo " address $new_ip"
|
|
echo " netmask $new_netmask"
|
|
echo " gateway $new_gw"
|
|
else
|
|
echo "iface $target_iface inet dhcp"
|
|
fi
|
|
} >> "$tmpfile"
|
|
|
|
mv "$tmpfile" "$INTERFACES_FILE"
|
|
}
|
|
|
|
# ── cidr helper ───────────────────────────────────────────────────────────────
|
|
|
|
mask_to_cidr() {
|
|
python3 -c "
|
|
import ipaddress
|
|
print(ipaddress.IPv4Network('0.0.0.0/$1', strict=False).prefixlen)
|
|
" 2>/dev/null || echo "24"
|
|
}
|
|
|
|
# ── update ───────────────────────────────────────────────────────────────────
|
|
|
|
run_update() {
|
|
clear
|
|
echo ""
|
|
echo "╔══════════════════════════════════════════════════════════════════════════╗"
|
|
echo "║ Update ║"
|
|
echo "╚══════════════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
|
|
if [ ! -f /root/update.sh ]; then
|
|
echo " Error: /root/update.sh not found."
|
|
echo ""
|
|
read -rp "Press Enter to continue..."
|
|
return
|
|
fi
|
|
|
|
bash /root/update.sh
|
|
echo ""
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ── reboot ───────────────────────────────────────────────────────────────────
|
|
|
|
reboot_confirm() {
|
|
clear
|
|
echo ""
|
|
echo "╔══════════════════════════════════════════════════════════════════════════╗"
|
|
echo "║ Reboot ║"
|
|
echo "╚══════════════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
read -rp " Are you sure you want to reboot? [y/N]: " confirm
|
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
|
echo ""
|
|
echo " Rebooting..."
|
|
sleep 1
|
|
reboot
|
|
else
|
|
echo " Cancelled."
|
|
echo ""
|
|
read -rp "Press Enter to continue..."
|
|
fi
|
|
}
|
|
|
|
# ── shell access ─────────────────────────────────────────────────────────────
|
|
|
|
shell_access() {
|
|
clear
|
|
echo ""
|
|
echo "╔══════════════════════════════════════════════════════════════════════════╗"
|
|
echo "║ Shell Access ║"
|
|
echo "╚══════════════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
|
|
local attempts=3
|
|
while (( attempts-- > 0 )); do
|
|
local input input_hash
|
|
read -rsp " Password: " input
|
|
echo ""
|
|
input_hash=$(echo -n "$input" | sha256sum | cut -d' ' -f1)
|
|
|
|
if [ "$input_hash" = "$SHELL_PASSWORD_HASH" ]; then
|
|
echo ""
|
|
echo " Access granted. Type 'exit' to return to the menu."
|
|
echo ""
|
|
bash
|
|
return
|
|
else
|
|
echo " Incorrect password.$( (( attempts > 0 )) && echo " $attempts attempt(s) remaining." || echo "" )"
|
|
fi
|
|
done
|
|
|
|
echo " Access denied."
|
|
echo ""
|
|
read -rp "Press Enter to continue..."
|
|
}
|
|
|
|
# ── log helpers ───────────────────────────────────────────────────────────────
|
|
# Prints the last 5 events from the past hour (always exactly 5 lines):
|
|
# timestamp (local time), src_ip, dst_port.
|
|
|
|
log_last5() {
|
|
if [ ! -f "$LOG_FILE" ]; then
|
|
echo " (log file not found)"
|
|
return
|
|
fi
|
|
tail -n 5 "$LOG_FILE" | python3 -c '
|
|
import sys, json
|
|
from datetime import datetime, timedelta, timezone
|
|
out = []
|
|
cutoff = datetime.now(timezone.utc) - timedelta(hours=1)
|
|
for line in sys.stdin:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
try:
|
|
e = json.loads(line)
|
|
except ValueError:
|
|
continue
|
|
raw_ts = str(e.get("timestamp", ""))
|
|
if raw_ts.endswith("Z"):
|
|
raw_ts = raw_ts[:-1] + "+00:00"
|
|
try:
|
|
ts = datetime.fromisoformat(raw_ts)
|
|
except ValueError:
|
|
continue
|
|
if ts.tzinfo is None:
|
|
ts = ts.replace(tzinfo=timezone.utc)
|
|
if ts < cutoff:
|
|
continue
|
|
src = str(e.get("src_ip", "?"))
|
|
dst = str(e.get("dst_port", "?"))
|
|
local_ts = ts.astimezone().strftime("%Y-%m-%d %H:%M:%S")
|
|
out.append(f" {local_ts:<34} {src:<18} {dst}")
|
|
if not out:
|
|
out.append(" (no events in the last hour)")
|
|
while len(out) < 5:
|
|
out.append("")
|
|
print("\n".join(out))
|
|
'
|
|
}
|
|
|
|
log_columns() {
|
|
printf " %-34s %-18s%s\n" "Timestamp" "Source IP" "Destination Port"
|
|
printf " %s %s %s\n" "──────────────────────────────────" "──────────────────" "──────────────────"
|
|
}
|
|
|
|
# ── log viewer ────────────────────────────────────────────────────────────────
|
|
|
|
show_log_viewer() {
|
|
tput civis
|
|
trap 'tput cnorm; echo; return 1' INT
|
|
|
|
while true; do
|
|
clear
|
|
echo ""
|
|
echo "╔══════════════════════════════════════════════════════════════════════════╗"
|
|
printf "║ %-71s ║\n" "Last 5 Events ── portspoof.log"
|
|
echo "╚══════════════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
log_columns
|
|
log_last5
|
|
echo ""
|
|
echo " Auto-refresh: 1s — press any key to return to the menu"
|
|
if IFS= read -rsn1 -t 1 _; then
|
|
break # key pressed — back to menu
|
|
fi
|
|
done
|
|
|
|
tput cnorm
|
|
trap - INT
|
|
}
|
|
|
|
# ── main screen: status + menu + live log (1s refresh) ────────────────────────
|
|
# Fixed geometry, measured up from the hint line at the bottom:
|
|
# 1 blank + 5 log data lines + 5 log header lines + 1 blank + $count menu lines
|
|
|
|
main_screen() {
|
|
local -n _res="$1"
|
|
local cur=0
|
|
local count=${#MENU_ITEMS[@]}
|
|
local key esc
|
|
|
|
local status log_lines
|
|
status=$(status_box)
|
|
|
|
print_menu_lines() {
|
|
local i
|
|
for (( i=0; i<count; i++ )); do
|
|
tput el
|
|
if [ "$i" -eq "$cur" ]; then
|
|
printf " \e[1;7m %-72s \e[0m\n" "${MENU_ITEMS[$i]}"
|
|
else
|
|
printf " %-72s\n" "${MENU_ITEMS[$i]}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
print_log_lines() {
|
|
local i
|
|
for (( i=0; i<5; i++ )); do
|
|
tput el
|
|
printf '%s\n' "${log_lines[$i]:-}"
|
|
done
|
|
}
|
|
|
|
tput civis
|
|
trap 'tput cnorm; echo; exit 130' INT
|
|
|
|
mapfile -t log_lines < <(log_last5)
|
|
|
|
clear
|
|
echo ""
|
|
printf '%s\n' "$status"
|
|
echo ""
|
|
for (( i=0; i<count; i++ )); do printf '\n'; done
|
|
tput cuu "$count"
|
|
print_menu_lines
|
|
echo ""
|
|
echo " ╔══════════════════════════════════════════════════════════════════════════╗"
|
|
printf " ║ %-71s║\n" "Last 5 Events ── portspoof.log"
|
|
echo " ╚══════════════════════════════════════════════════════════════════════════╝"
|
|
log_columns
|
|
for (( i=0; i<5; i++ )); do printf '\n'; done
|
|
tput cuu 5
|
|
print_log_lines
|
|
echo ""
|
|
echo " Use ↑ ↓ to navigate, Enter to select, Ctrl+C to exit"
|
|
tput cuu 1 # park on the hint line; refreshes below are relative to it
|
|
|
|
while true; do
|
|
if IFS= read -rsn1 -t 1 key; then
|
|
case "$key" in
|
|
'')
|
|
break # Enter — selection made
|
|
;;
|
|
$'\x1b')
|
|
IFS= read -rsn2 -t 0.05 esc
|
|
case "$key$esc" in
|
|
$'\x1b[A')
|
|
(( cur = (cur - 1 + count) % count ))
|
|
tput cuu $((12 + count)); print_menu_lines; tput cud 12
|
|
;;
|
|
$'\x1b[B')
|
|
(( cur = (cur + 1) % count ))
|
|
tput cuu $((12 + count)); print_menu_lines; tput cud 12
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
else
|
|
mapfile -t log_lines < <(log_last5)
|
|
tput cuu 6; print_log_lines; tput cud 1
|
|
fi
|
|
done
|
|
|
|
tput cnorm
|
|
trap - INT
|
|
_res=$cur
|
|
}
|
|
|
|
# ── main menu ─────────────────────────────────────────────────────────────────
|
|
|
|
MENU_ITEMS=("Refresh" "Log" "Set Static IP" "Set DHCP" "Shell" "Reboot" "Update")
|
|
|
|
main_menu() {
|
|
local choice
|
|
while true; do
|
|
main_screen choice
|
|
echo ""
|
|
|
|
case "$choice" in
|
|
0)
|
|
;; # Refresh — just redraw
|
|
1)
|
|
show_log_viewer
|
|
echo ""
|
|
read -rp "Press Enter to continue..."
|
|
;;
|
|
2)
|
|
if select_interface; then
|
|
set_static "$SELECTED_IFACE"
|
|
fi
|
|
echo ""
|
|
read -rp "Press Enter to continue..."
|
|
;;
|
|
3)
|
|
if select_interface; then
|
|
set_dhcp "$SELECTED_IFACE"
|
|
fi
|
|
echo ""
|
|
read -rp "Press Enter to continue..."
|
|
;;
|
|
4)
|
|
shell_access
|
|
;;
|
|
5)
|
|
reboot_confirm
|
|
;;
|
|
6)
|
|
run_update
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# ── entry point ───────────────────────────────────────────────────────────────
|
|
|
|
if [ "$EUID" -ne 0 ]; then
|
|
echo "This script must be run as root (use sudo)."
|
|
exit 1
|
|
fi
|
|
|
|
main_menu
|