tommasinigiovanni/security-skills · Archived

server-security-scan

Use when given an IP address (and optionally SSH credentials) to run a non-destructive security audit — external attack-surface analysis from IP alone, or internal privilege/configuration scan when SSH access credentials are provided.

First seen May 19, 2026

Installation

$ npx skills add tommasinigiovanni/security-skills --skill server-security-scan

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

License MIT
Default branch main
Open issues 0
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 10,601 B
  • docs SUMMARY.md 264 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 4 installs

SKILL.md

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 $SCANDIR as base for all -oN / -output / -o flags below. At the end, generate $SCANDIR/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