SuperAPI AI Directory

server-security-scan

Security & auditing

View repository

Updated 10-16 06:16

About this skill

Server Security Scan

Overview

Dual-mode security audit: external (IP only → simulate attacker perspective) and internal (IP + SSH → scan from inside). Never destructive. Always requires explicit authorization confirmation before starting.

Authorization Gate (MANDATORY)

Before ANY scan, check the allowlist first:

ALLOWLIST="$HOME/.config/server-security-scan/allowlist.txt"
if [ -f "$ALLOWLIST" ] && grep -qxF "<IP>" "$ALLOWLIST"; then
  echo "IP <IP> found in allowlist — proceeding."
else
  # Ask explicit confirmation
  echo "IP <IP> not in allowlist."
  echo "Confirm you are the authorized owner/administrator and have explicit permission to scan this target."
  # If not confirmed → stop immediately
fi

To pre-authorize IPs and skip the prompt:

mkdir -p ~/.config/server-security-scan
echo "192.168.1.10" >> ~/.config/server-security-scan/allowlist.txt

If not in allowlist and not confirmed → stop immediately.


Output directory

All scan files go into a dated subdirectory to avoid collisions across multiple scans:

SCAN_DIR="./security-scan/$(date +%Y-%m-%d)-<IP>"
mkdir -p "$SCAN_DIR"

Use $SCAN_DIR as base for all -oN / -output / -o flags below. At the end, generate $SCAN_DIR/REPORT.md from findings.


Mode 1: External Scan (IP only)

Goal: reproduce what an external attacker sees from the internet.

Step 1 — Passive reconnaissance

# Reverse DNS
dig -x <IP> +short
host <IP>

# ASN / geolocation
curl -s "https://ipinfo.io/<IP>/json"

Step 2 — Port scan

Note: many cloud providers (Hetzner, DigitalOcean, OVH) filter ICMP — add -Pn if nmap reports "host seems down".

# Full TCP scan with version and OS detection (non-aggressive)
nmap -Pn -sV -sC -O -p- --open -T3 --reason <IP> -oN "$SCAN_DIR/01-nmap.txt"

# Faster alternative (top 1000 ports)
nmap -Pn -sV -sC --open -T3 <IP> -oN "$SCAN_DIR/01-nmap.txt"

Step 3 — Update nuclei templates (run once per session)

nuclei -update-templates -silent

Step 4 — Web scanning (if port 80/443/8080/8443 is open)

# Nikto on each web port found
nikto -h <IP> -p <PORT> -output "$SCAN_DIR/02-nikto-<PORT>.txt"

# Nuclei CVE and misconfiguration templates
nuclei -u http://<IP>:<PORT> -t cves/ -t misconfiguration/ \
  -severity medium,high,critical -o "$SCAN_DIR/03-nuclei.txt"

Step 5 — Service-specific checks

# SSH: check version and algorithms
nmap --script ssh2-enum-algos,ssh-auth-methods -p 22 <IP>

# SMB (if port 445 is open)
nmap --script smb-vuln* -p 445 <IP>

# SSL/TLS (if HTTPS)
nmap --script ssl-enum-ciphers -p 443 <IP>

Step 6 — External report

Include in the report:

  • Open ports, services, and detected versions
  • CVEs identified (CVSS score from nuclei output; flag as "self-assigned" if not from NVD)
  • Weak configurations (deprecated SSH algorithms, obsolete TLS, etc.)
  • Remediation for each finding

Mode 2: Internal Scan (IP + SSH credentials)

Goal: audit from inside — configurations, privilege escalation paths, hidden services.

SSH access

# With private key
ssh -i /path/to/key user@<IP>

# With password
ssh user@<IP>

Step 1 — System information

uname -a && cat /etc/os-release
hostname && id && whoami
uptime && last -n 20

Step 2 — Users, authentication, and SSH hardening

# Users with a shell
grep -E '/bin/(bash|sh|zsh|fish)$' /etc/passwd

# Users with UID 0 (root equivalents)
awk -F: '($3 == 0) {print}' /etc/passwd

# Authorized SSH keys
find / -name "authorized_keys" 2>/dev/null
find / -name "*.pem" -o -name "id_rsa" 2>/dev/null | grep -v proc

# Last login per user
lastlog | grep -v 'Never'

# SSH daemon effective config (resolves drop-in files, more reliable than grep on sshd_config)
sshd -T 2>/dev/null | grep -E 'passwordauthentication|permitrootlogin|pubkeyauthentication'

# Brute-force protection
systemctl is-active fail2ban 2>/dev/null
fail2ban-client status sshd 2>/dev/null
# Failed login rate (last 100 lines)
journalctl -u ssh --no-pager -n 100 2>/dev/null | grep -i 'failed\|invalid' | wc -l

Step 3 — Sudo and SUID

# Sudo rules
sudo -l 2>/dev/null

# Non-standard SUID binaries (potential privilege escalation paths)
find / -perm -4000 -type f 2>/dev/null | grep -v -E '(/bin/|/sbin/|/usr/bin/|/usr/sbin/)' | sort

# SGID binaries
find / -perm -2000 -type f 2>/dev/null | sort

Step 4 — Firewall state

Run this BEFORE concluding on severity of 0.0.0.0 binds — a bound port may still be blocked by the firewall.

ufw status verbose 2>/dev/null
iptables -L INPUT -v -n 2>/dev/null
firewall-cmd --list-all 2>/dev/null

Step 5 — Services, internal ports, and Docker

# Listening ports (local-only = hidden from external scan)
ss -tulpn 2>/dev/null || netstat -tulpn 2>/dev/null

# Running services
systemctl list-units --type=service --state=running 2>/dev/null

# Running processes
ps aux --sort=-%cpu | head -30

Docker audit (if Docker is present):

# Container port bindings — flag any 0.0.0.0 that should be 127.0.0.1
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}" 2>/dev/null

# Docker daemon socket exposure
ss -tlnp | grep -E '2375|2376'

# Privileged containers (critical finding if present)
docker inspect $(docker ps -q) 2>/dev/null \
  | jq '.[] | select(.HostConfig.Privileged==true) | .Name'

Step 6 — Cron and scheduled tasks

# System cron
cat /etc/crontab 2>/dev/null
ls -la /etc/cron.* 2>/dev/null

# Per-user crontabs
for user in $(cut -f1 -d: /etc/passwd); do
  crontab -u "$user" -l 2>/dev/null | grep -v '^#' | grep -v '^$' \
    && echo "  ^ user: $user"
done

Step 7 — Package updates

# Debian/Ubuntu
apt list --upgradable 2>/dev/null | head -30

# RHEL/CentOS/Fedora
yum check-update 2>/dev/null | head -30 || dnf check-update 2>/dev/null | head -30

Step 8 — Lynis (if available)

which lynis && sudo lynis audit system --quick 2>/dev/null | tail -50

Step 9 — World-writable sensitive files

# World-writable files in /etc
find /etc -writable -type f 2>/dev/null

# World-writable scripts executed by cron
find /etc/cron* /var/spool/cron -writable 2>/dev/null

Step 10 — Internal report

Include in the report:

  • Unexpected users or accounts with UID 0
  • Non-standard SUID/SGID binaries (privilege escalation paths)
  • Internal ports not exposed externally (hidden services)
  • Docker containers binding 0.0.0.0 or running privileged
  • Docker daemon socket exposed on TCP
  • Cron jobs executing world-writable scripts
  • Packages with known vulnerabilities
  • Overly permissive sudo rules
  • Exposed SSH keys
  • SSH with PasswordAuthentication enabled or fail2ban absent
  • Firewall state vs open binds (cross-reference Step 4 with Step 5)
  • Prioritized remediation by severity

Report Generation

After all scan steps are complete, run this script to generate a pre-populated REPORT.md from the scan output files:

#!/usr/bin/env bash
# Usage: bash generate-report.sh <IP> <SCAN_DIR> [external|internal|both]
IP="$1"; SCAN_DIR="$2"; TYPE="${3:-both}"
REPORT="$SCAN_DIR/REPORT.md"

echo "# Security Report — $IP" > "$REPORT"
echo "**Date:** $(date +%Y-%m-%d)  **Type:** $TYPE  **Author:** " >> "$REPORT"
echo "" >> "$REPORT"

# --- Open ports from nmap ---
echo "## Detected Ports and Services" >> "$REPORT"
if [ -f "$SCAN_DIR/01-nmap.txt" ]; then
  echo '```' >> "$REPORT"
  grep -E '^[0-9]+/(tcp|udp)' "$SCAN_DIR/01-nmap.txt" >> "$REPORT"
  echo '```' >> "$REPORT"
else
  echo "_nmap output not found_" >> "$REPORT"
fi
echo "" >> "$REPORT"

# --- Nuclei findings by severity ---
echo "## Findings" >> "$REPORT"
for sev in critical high medium low info; do
  label=$(echo "$sev" | awk '{print toupper(substr($0,1,1)) substr($0,2)}')
  case $sev in
    critical) icon="🔴" ;; high) icon="🟠" ;; medium) icon="🟡" ;;
    low|info) icon="🟢" ;;
  esac
  findings=""
  if [ -f "$SCAN_DIR/03-nuclei.txt" ]; then
    findings=$(grep -i "\[$sev\]" "$SCAN_DIR/03-nuclei.txt" 2>/dev/null)
  fi
  echo "### $icon $label" >> "$REPORT"
  echo "| # | Vulnerability | Service/Path | CVSS | Source | Remediation |" >> "$REPORT"
  echo "|---|---|---|---|---|---|" >> "$REPORT"
  if [ -n "$findings" ]; then
    i=1
    while IFS= read -r line; do
      template=$(echo "$line" | grep -oP '(?<=\[)[^\]]+(?=\])' | head -1)
      target=$(echo "$line"  | grep -oP 'https?://\S+' | head -1)
      echo "| $i | $template | ${target:--} | — | nuclei | TODO |" >> "$REPORT"
      i=$((i+1))
    done <<< "$findings"
  else
    echo "| — | _none_ | | | | |" >> "$REPORT"
  fi
  echo "" >> "$REPORT"
done

# --- Nikto findings (appended as raw block) ---
if ls "$SCAN_DIR"/02-nikto-*.txt 1>/dev/null 2>&1; then
  echo "## Nikto Findings" >> "$REPORT"
  for f in "$SCAN_DIR"/02-nikto-*.txt; do
    port=$(basename "$f" | grep -oP '\d+(?=\.txt)')
    echo "### Port $port" >> "$REPORT"
    echo '```' >> "$REPORT"
    grep -v '^-' "$f" | grep -v '^$' >> "$REPORT"
    echo '```' >> "$REPORT"
    echo "" >> "$REPORT"
  done
fi

echo "> CVSS scores sourced from nuclei/NVD where available; marked "self-assigned" otherwise." >> "$REPORT"
echo "" >> "$REPORT"
echo "## Executive Summary" >> "$REPORT"
echo "_TODO: 2-3 lines — finding count by severity, overall risk assessment_" >> "$REPORT"
echo "" >> "$REPORT"
echo "## Next Steps" >> "$REPORT"
echo "_TODO: prioritized action list_" >> "$REPORT"

echo "Report generated: $REPORT"

The script pre-populates ports and nuclei/nikto findings. Fill in remediation notes and Executive Summary manually.

CVSS score resolution order:

  1. Extract from nuclei output if present (nuclei embeds CVSS in [cvss-score] tag)
  2. If missing, look up the CVE-ID on nvd.nist.gov
  3. If no CVE-ID exists, assign manually and mark as (self-assigned)

Hard Limits — Never do

  • Active exploits or destructive payloads
  • Denial of service (no -T5 on production, no stress testing)
  • Exfiltrate real data from the server
  • Modify files or configurations
  • Create backdoors or accounts
  • Share the report with unauthorized third parties