From 9c33545783192777a9aed47d0dff5cfd99aed8a4 Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:34:58 +0200 Subject: [PATCH 01/12] chore: add LICENSE, .gitignore and .editorconfig --- .editorconfig | 15 +++++++++++++++ .gitignore | 38 ++++++++++++++++++++++++++++++++++++++ LICENSE | 21 +++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 LICENSE diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a73df9f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..271bda7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Enumeration output directories +ad_enum_*/ +output/ +results/ + +# Editor / IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ +.DS_Store +Thumbs.db + +# Python (in case modules grow Python tooling later) +__pycache__/ +*.pyc +*.pyo +.venv/ +venv/ +*.egg-info/ + +# Logs and tmp +*.log +*.tmp +*.bak + +# Tooling internals (project-local Claude instructions, graph dumps) +CLAUDE.md +graphify-out/ + +# Credentials and secrets — defensive, never commit +*.key +*.pem +*.crt +.env +.env.* +!.env.example diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..45d8f66 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Melvin PETIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From dae350c37beda190f3ae486dd66ae2d0dee442e4 Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:44:51 +0200 Subject: [PATCH 02/12] refactor: extract core, ui and installer helpers into lib/ --- lib/core.sh | 56 +++++++++ lib/installer.sh | 142 ++++++++++++++++++++++ lib/ui.sh | 308 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 506 insertions(+) create mode 100644 lib/core.sh create mode 100644 lib/installer.sh create mode 100644 lib/ui.sh diff --git a/lib/core.sh b/lib/core.sh new file mode 100644 index 0000000..938362f --- /dev/null +++ b/lib/core.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# lib/core.sh — Colors, palette, global state and shared constants for Ghostline. +# Sourced by the entry point and by every module; never executed directly. + +if [[ -n "${GHOSTLINE_CORE_LOADED:-}" ]]; then + return 0 +fi +GHOSTLINE_CORE_LOADED=1 + +# --------------------------------------------------------------------------- +# Color palette — TTY-aware. Pipes get plain text, terminals get colors. +# --------------------------------------------------------------------------- +if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 \ + && [[ -n "${TERM:-}" ]] && [[ "${TERM}" != "dumb" ]]; then + RESET="$(tput sgr0)" + BOLD="$(tput bold)" + DIM="$(tput dim)" + + RED="$(tput setaf 1)" + GREEN="$(tput setaf 2)" + YELLOW="$(tput setaf 3)" + BLUE="$(tput setaf 4)" + MAGENTA="$(tput setaf 5)" + CYAN="$(tput setaf 6)" + + BRIGHT_RED="$(tput setaf 9)" + BRIGHT_GREEN="$(tput setaf 10)" + BRIGHT_MAGENTA="$(tput setaf 13)" +else + RESET="" + BOLD="" + DIM="" + RED="" + GREEN="" + YELLOW="" + BLUE="" + MAGENTA="" + CYAN="" + BRIGHT_RED="" + BRIGHT_GREEN="" + BRIGHT_MAGENTA="" +fi +readonly RESET BOLD DIM RED GREEN YELLOW BLUE MAGENTA CYAN +readonly BRIGHT_RED BRIGHT_GREEN BRIGHT_MAGENTA + +# --------------------------------------------------------------------------- +# Global runtime state. Modules read and write these freely. +# --------------------------------------------------------------------------- +GHOSTLINE_TARGET="" +GHOSTLINE_DOMAIN="" +GHOSTLINE_USERNAME="" +GHOSTLINE_PASSWORD="" +GHOSTLINE_OUTPUT_DIR="ad_enum_$(date +%Y%m%d_%H%M%S)" + +# Where third-party tools may be cloned by install.sh. +GHOSTLINE_TOOLS_DIR="${GHOSTLINE_TOOLS_DIR:-/opt}" diff --git a/lib/installer.sh b/lib/installer.sh new file mode 100644 index 0000000..87dc54c --- /dev/null +++ b/lib/installer.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +# lib/installer.sh — Logging, prompting and install primitives. +# Sourced by both the runtime entry point and install.sh. + +if [[ -n "${GHOSTLINE_INSTALLER_LOADED:-}" ]]; then + return 0 +fi +GHOSTLINE_INSTALLER_LOADED=1 + +# --------------------------------------------------------------------------- +# Logging — color-coded, with a consistent prefix. +# --------------------------------------------------------------------------- +log_step() { printf '%b[*]%b %s\n' "${BLUE}" "${RESET}" "$*"; } +log_info() { printf '%b[i]%b %s\n' "${CYAN}" "${RESET}" "$*"; } +log_warn() { printf '%b[!]%b %s\n' "${YELLOW}" "${RESET}" "$*" >&2; } +log_error() { printf '%b[-]%b %s\n' "${RED}" "${RESET}" "$*" >&2; } +log_success() { printf '%b[+]%b %s\n' "${GREEN}" "${RESET}" "$*"; } + +# --------------------------------------------------------------------------- +# Prompting helpers — keep behavior consistent across modules. +# --------------------------------------------------------------------------- +prompt_value() { + local label="$1" + local default="${2:-}" + local response + if [[ -n "$default" ]]; then + read -rp "${label} [${default}]: " response + response="${response:-$default}" + else + read -rp "${label}: " response + fi + printf '%s' "$response" +} + +prompt_password() { + local label="$1" + local response + read -rsp "${label}: " response + echo "" + printf '%s' "$response" +} + +prompt_yesno() { + local label="$1" + local default="${2:-n}" + local hint + case "$default" in + y|Y) hint="Y/n" ;; + *) hint="y/N" ;; + esac + local response + read -rp "${label} [${hint}]: " response + response="${response:-$default}" + [[ "$response" =~ ^[Yy]([Ee][Ss])?$ ]] +} + +press_enter_to_continue() { + read -rp "Press Enter to continue..." _ +} + +# --------------------------------------------------------------------------- +# Runtime — verify a binary is on PATH or warn cleanly. +# Returns 0 if the command exists, 1 otherwise. +# --------------------------------------------------------------------------- +ensure_command() { + local cmd="$1" + local hint="${2:-}" + if command -v "$cmd" >/dev/null 2>&1; then + return 0 + fi + log_warn "Required command not found: ${cmd}" + if [[ -n "$hint" ]]; then + log_info "Hint: ${hint}" + else + log_info "Try running 'sudo ./install.sh' first." + fi + return 1 +} + +# Resolve the first available command from a list of alternatives. +# Echoes the resolved path on stdout, or returns 1 if none are found. +resolve_command() { + local candidate + for candidate in "$@"; do + if command -v "$candidate" >/dev/null 2>&1; then + command -v "$candidate" + return 0 + fi + done + return 1 +} + +# --------------------------------------------------------------------------- +# Install-time primitives — used by install.sh. +# --------------------------------------------------------------------------- +apt_install() { + apt install -y "$@" +} + +pipx_install() { + local package="$1" + pipx install "$package" --force 2>&1 | grep -v "WARNING" || true +} + +# Best-effort pip install with progressive fallbacks for modern Debian/Kali. +pip_install() { + local package="$1" + python3 -m pip install --user --ignore-installed "$package" 2>/dev/null \ + || python3 -m pip install --break-system-packages --ignore-installed "$package" 2>/dev/null \ + || python3 -m pip install --user "$package" 2>/dev/null \ + || python3 -m pip install --break-system-packages "$package" 2>/dev/null +} + +install_pip_requirements() { + local req_dir="$1" + local req_file="${req_dir}/requirements.txt" + [[ -f "$req_file" ]] || return 0 + local pkg + while IFS= read -r pkg; do + [[ -z "$pkg" || "$pkg" =~ ^# ]] && continue + pip_install "$pkg" + done < "$req_file" +} + +# Clone a repo into or git pull --ff-only if it already exists. +clone_or_pull() { + local url="$1" + local dest="$2" + if [[ -d "${dest}/.git" ]]; then + (cd "$dest" || return 1; git pull --ff-only) >/dev/null 2>&1 + else + git clone "$url" "$dest" >/dev/null 2>&1 + fi +} + +# Source check used by install.sh. +require_root() { + if [[ ${EUID} -ne 0 ]]; then + log_error "This script must be run as root (use sudo)." + exit 1 + fi +} diff --git a/lib/ui.sh b/lib/ui.sh new file mode 100644 index 0000000..924227e --- /dev/null +++ b/lib/ui.sh @@ -0,0 +1,308 @@ +#!/usr/bin/env bash +# lib/ui.sh — ASCII art, menus and banner rendering. + +if [[ -n "${GHOSTLINE_UI_LOADED:-}" ]]; then + return 0 +fi +GHOSTLINE_UI_LOADED=1 + +# --------------------------------------------------------------------------- +# Decorative ASCII raven displayed alongside each menu. +# --------------------------------------------------------------------------- +GHOSTLINE_ASCII_ART=$(cat <<'ASCII' +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⢖⣠⣄⡀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣾⣿⣿⠎⠀⠀⠹⡀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣾⣿⣿⣿⣿⢿⣤⠴⠒⢦⡇⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⣿⣿⣿⣿⠿⠛⠁⠸⡇⠀⠀⠀⡇⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣿⣿⣿⣿⠿⠋⠁⠀⠀⠀⠀⣧⠀⠀⠀⡇⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣶⠂⠾⠿⣿⣿⡿⠛⠁⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⢠⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣷⣤⡀⠈⠉⠑⠒⠤⢀⡀⠀⠀⠀⠀⣿⠀⠀⠀⣼⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠟⠿⠿⢿⣿⣿⣆⠀⠀⠀⠀⠀⠈⠑⢄⠀⠀⣿⠀⠀⠀⡏⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⡏⡄⠀⠀⠀⠈⠻⣿⣧⠀⠀⠀⠀⠀⠀⠀⢳⠀⣿⠀⠀⠀⡇⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⡀⣷⠄⠀⠀⠀⠀⠙⢿⣇⡀⠀⠀⠀⠀⠀⠀⣷⡇⠀⠀⢸⡇⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⡿⠐⣁⣀⣀⢀⣾⣤⢤⣶⣿⣿⣦⡀⠀⠀⠀⠀⢸⠇⠀⠀⡾⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⣇⣾⣿⣿⣿⡇⠀⠻⣽⣿⣿⣿⡿⠿⣦⠀⠀⠀⡟⠀⠀⢰⠇⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⡟⣌⡻⠛⠁⢰⠸⡔⣤⣉⡩⠐⠁⠀⢸⡇⠀⢰⠃⠀⠀⡎⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣇⠀⠉⠀⠀⠀⠀⠀⠈⠙⠻⣶⢶⣶⣾⠇⢀⡏⠀⠀⠰⢣⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⢋⣿⣿⣿⣿⣿⣿⣿⣷⠀⠤⠒⠒⠓⠘⠀⢴⢏⡟⠈⣿⠀⡞⠀⠀⢀⡇⠀⠣⡀⠀ +⠀⠀⠀⠀⠀⠀⣼⣿⡿⠁⣸⣿⣿⣿⣿⣿⣿⣿⣿⠀⡰⠞⠛⠛⠛⠳⠶⠿⠁⣰⣿⣼⠃⠀⠀⡼⢿⣶⡀⡇⠀ +⠀⠀⠀⠀⠀⣼⣿⠟⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⣿⠟⠋⠉⠉⠁⠀⠀⢠⣿⣿⠃⠀⠀⠰⠁⠘⢿⠔⢀⣠ +⠀⠀⠀⠀⣰⣿⡟⠀⠀⠀⠸⣿⣿⣿⣿⣿⣿⣿⣿⣏⠻⠖⠂⠈⠒⠂⢀⣠⣿⣿⠏⠀⠀⢀⣧⣶⡶⢖⣿⠇⡿ +⠀⠀⠀⢠⣿⡟⠀⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣿⣏⠩⠭⣼⣿⣿⠏⠀⠀⠀⡼⠛⢉⣴⣿⠏⣸⡇ +⠀⠀⢀⣾⡟⠀⠀⠀⠀⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣿⣿⣿⠏⠀⠀⠀⡼⢁⣴⣿⡿⠁⣰⣿⠁ +⠀⠀⣸⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠿⠿⠿⠛⢛⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⣰⣿⣿⡿⠋⠀⣰⣿⡟⢠ +⠀⠀⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⢠⣿⡿⠛⠁⠀⣼⣿⣿⣧⣿ +⠀⢸⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⡿⠟⢻⠐⠉⠑⠤⣀⢠⣿⣿⠀⠀⢀⣾⣿⣿⣿⣿⣿ +⠀⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⢱⠹⠉⢉⠉⠓⠶⠤⠍⠛⢻⡄⣠⣿⣿⣿⣿⣿⣿⣿ +⢸⠀⠀⠀⠀⠀⠀⢀⣤⣴⡖⠤⣀⠀⠀⠀⣼⣿⣿⣿⠉⠉⠑⠛⠛⠛⠳⢄⣀⠈⢹⠤⣿⣿⣿⣿⣿⣿⣿⣿⣿ +ASCII +) + +# --------------------------------------------------------------------------- +# Title screens for each menu. +# --------------------------------------------------------------------------- +generate_main_menu() { + local -a menu_lines=( + "${BRIGHT_RED} ▄██████▄ ▄█ █▄ ▄██████▄ ▄████████ ███ ▄█ ▄█ ███▄▄▄▄ ▄████████${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███ ███ ▀█████████▄ ███ ███ ███▀▀▀██▄ ███ ███${RESET}" + "${BRIGHT_RED} ███ █▀ ███ ███ ███ ███ ███ █▀ ▀███▀▀██ ███ ███▌ ███ ███ ███ █▀${RESET}" + "${BRIGHT_RED} ▄███ ▄███▄▄▄▄███▄▄ ███ ███ ███ ███ ▀ ███ ███▌ ███ ███ ▄███▄▄▄${RESET}" + "${BRIGHT_RED}▀▀███ ████▄ ▀▀███▀▀▀▀███▀ ███ ███ ▀███████████ ███ ███ ███▌ ███ ███ ▀▀███▀▀▀${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ █▄${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ▄█ ███ ███ ███▌ ▄ ███ ███ ███ ███ ███${RESET}" + "${BRIGHT_RED} ████████▀ ███ █▀ ▀██████▀ ▄████████▀ ▄████▀ █████▄▄██ █▀ ▀█ █▀ ██████████${RESET}" + " " + "${BRIGHT_RED}█" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_MAGENTA}Configuration:${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} Target: ${BRIGHT_MAGENTA}${GHOSTLINE_TARGET:-Not set}${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} Domain: ${BRIGHT_MAGENTA}${GHOSTLINE_DOMAIN:-Not set}${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} User: ${BRIGHT_MAGENTA}${GHOSTLINE_USERNAME:-Not set}${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_MAGENTA}Hunt for Active Directory intelligence:${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[1]${RESET} Configuration Menu" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[2]${RESET} Passive Enumeration" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[3]${RESET} Active Enumeration" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[4]${RESET} Special Actions" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[0]${RESET} Exit" + "${BOLD}${BRIGHT_RED}█${RESET}" + ) + printf '%s\n' "${menu_lines[@]}" +} + +generate_config_menu() { + local -a menu_lines=( + "${BRIGHT_RED} ▄████████ ▄██████▄ ███▄▄▄▄ ▄████████ ▄█ ▄██████▄ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███▀▀▀██▄ ███ ███ ███ ███ ███ ${RESET}" + "${BRIGHT_RED} ███ █▀ ███ ███ ███ ███ ███ █▀ ███▌ ███ █▀ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ▄███▄▄▄ ███▌ ▄███ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ▀▀███▀▀▀ ███▌ ▀▀███ ████▄ ${RESET}" + "${BRIGHT_RED} ███ █▄ ███ ███ ███ ███ ███ ███ ███ ███ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ${RESET}" + "${BRIGHT_RED} ████████▀ ▀██████▀ ▀█ █▀ ███ █▀ ████████▀ ${RESET}" + " " + "${BRIGHT_MAGENTA}Configure your hunting parameters${RESET}" + " " + "${BOLD}${BRIGHT_RED}█${RESET} Current Configuration:" + "${BOLD}${BRIGHT_RED}█${RESET} Target: ${BRIGHT_MAGENTA}${GHOSTLINE_TARGET:-Not set}${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} Domain: ${BRIGHT_MAGENTA}${GHOSTLINE_DOMAIN:-Not set}${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} User: ${BRIGHT_MAGENTA}${GHOSTLINE_USERNAME:-Not set}${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} Output: ${BRIGHT_MAGENTA}${GHOSTLINE_OUTPUT_DIR}${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[1]${RESET} Set Target (IP/Hostname)" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[2]${RESET} Set Domain" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[3]${RESET} Set Credentials" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[4]${RESET} Set Output Directory" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[0]${RESET} Back to Main Menu" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + ) + printf '%s\n' "${menu_lines[@]}" +} + +generate_passive_menu() { + local -a menu_lines=( + "${BRIGHT_RED} ▄███████▄ ▄████████ ▄████████ ▄████████ ▄█ ███ ▄█ █▄ ▄████████${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███ ███ ███ ▀█████████▄ ███ ███ ███ ███${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ █▀ ███ █▀ ███▌ ▀███▀▀██ ███ ███ ███ █▀ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███▌ ███ ▀ ███ ███ ▄███▄▄▄ ${RESET}" + "${BRIGHT_RED}▀█████████▀ ▀███████████ ▀███████████ ▀███████████ ███▌ ███ ███ ███ ▀▀███▀▀▀ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ █▄ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ▄█ ███ ▄█ ███ ███ ███ ███ ███ ███ ███${RESET}" + "${BRIGHT_RED} ▄████▀ ███ █▀ ▄████████▀ ▄████████▀ █▀ ▄████▀ ▀██████▀ ██████████${RESET}" + " " + "${BRIGHT_MAGENTA}Silent reconnaissance without credentials${RESET}" + " " + "${BOLD}${BRIGHT_RED}█${RESET} Target: ${BRIGHT_MAGENTA}${GHOSTLINE_TARGET:-Not set}${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[1]${RESET} Nmap Scan (Port Discovery)" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[2]${RESET} Enum4linux-ng (SMB Enumeration)" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[3]${RESET} RPC Client (Null Session)" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[4]${RESET} LDAP Search (Anonymous)" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[5]${RESET} DNS Enumeration" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[0]${RESET} Back to Main Menu" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + ) + printf '%s\n' "${menu_lines[@]}" +} + +generate_active_menu() { + local -a menu_lines=( + "${BRIGHT_RED} ▄████████ ▄████████ ███ ▄█ ███ ▄█ █▄ ▄████████${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ▀█████████▄ ███ ▀█████████▄ ███ ███ ███ ███${RESET}" + "${BRIGHT_RED} ███ ███ ███ █▀ ▀███▀▀██ ███▌ ▀███▀▀██ ███ ███ ███ █▀ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ▀ ███▌ ███ ▀ ███ ███ ▄███▄▄▄ ${RESET}" + "${BRIGHT_RED} ▀███████████ ███ ███ ███▌ ███ ███ ███ ▀▀███▀▀▀ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ █▄ ███ ███ ███ ███ ███ ███ █▄ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███${RESET}" + "${BRIGHT_RED} ███ █▀ ████████▀ ▄████▀ █▀ ▄████▀ ▀██████▀ ██████████${RESET}" + " " + "${BRIGHT_MAGENTA}Authenticated enumeration with credentials${RESET}" + " " + "${BOLD}${BRIGHT_RED}█${RESET} Credentials: ${BRIGHT_MAGENTA}${GHOSTLINE_USERNAME:-Not set}${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[1]${RESET} BloodHound Collection" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[2]${RESET} CrackMapExec (Full Enum)" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[3]${RESET} AD DNS Dump" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[4]${RESET} GetNPUsers (ASREPRoast)" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[5]${RESET} RID Enumeration" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[0]${RESET} Back to Main Menu" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + ) + printf '%s\n' "${menu_lines[@]}" +} + +generate_special_menu() { + local -a menu_lines=( + "${BRIGHT_RED} ▄████████ ▄███████▄ ▄████████ ▄████████ ▄█ ▄████████ ▄█ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ${RESET}" + "${BRIGHT_RED} ███ █▀ ███ ███ ███ █▀ ███ █▀ ███▌ ███ ███ ███ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ ▄███▄▄▄ ███ ███▌ ███ ███ ███ ${RESET}" + "${BRIGHT_RED}▀███████████ ▀█████████▀ ▀▀███▀▀▀ ███ ███▌ ▀███████████ ███ ${RESET}" + "${BRIGHT_RED} ███ ███ ███ █▄ ███ █▄ ███ ███ ███ ███ ${RESET}" + "${BRIGHT_RED} ▄█ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███▌ ▄ ${RESET}" + "${BRIGHT_RED} ▄████████▀ ▄████▀ ██████████ ████████▀ █▀ ███ █▀ █████▄▄██ ${RESET}" + " " + "${BRIGHT_MAGENTA}Advanced operations and automated workflows${RESET}" + " " + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[1]${RESET} Auto Workflow (Full Scan)" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[2]${RESET} SMB Vulnerabilities Scan" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[3]${RESET} Secrets Dump (Requires Admin)" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[4]${RESET} View Results" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[0]${RESET} Back to Main Menu" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + "${BOLD}${BRIGHT_RED}█${RESET}" + ) + printf '%s\n' "${menu_lines[@]}" +} + +# --------------------------------------------------------------------------- +# Splash screen displayed when the toolkit boots. +# --------------------------------------------------------------------------- +GHOSTLINE_INFO_PANEL=( + "${BRIGHT_RED} ▄██████▄ ▄█ █▄ ▄██████▄ ▄████████ ███ ▄█ ▄█ ███▄▄▄▄ ▄████████${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███ ███ ▀█████████▄ ███ ███ ███▀▀▀██▄ ███ ███${RESET}" + "${BRIGHT_RED} ███ █▀ ███ ███ ███ ███ ███ █▀ ▀███▀▀██ ███ ███▌ ███ ███ ███ █▀${RESET}" + "${BRIGHT_RED} ▄███ ▄███▄▄▄▄███▄▄ ███ ███ ███ ███ ▀ ███ ███▌ ███ ███ ▄███▄▄▄${RESET}" + "${BRIGHT_RED}▀▀███ ████▄ ▀▀███▀▀▀▀███▀ ███ ███ ▀███████████ ███ ███ ███▌ ███ ███ ▀▀███▀▀▀${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ █▄${RESET}" + "${BRIGHT_RED} ███ ███ ███ ███ ███ ███ ▄█ ███ ███ ███▌ ▄ ███ ███ ███ ███ ███${RESET}" + "${BRIGHT_RED} ████████▀ ███ █▀ ▀██████▀ ▄████████▀ ▄████▀ █████▄▄██ █▀ ▀█ █▀ ██████████${RESET}" + "" + "${BRIGHT_MAGENTA}${BOLD}Active Directory OSINT Toolkit${RESET}" + "${DIM}by Melvin PETIT${RESET}" +) + +_strip_ansi() { + sed -E 's/\x1B\[[0-9;?]*[ -/]*[@-~]//g; s/\x1B\][^\a]*\a//g' +} + +display_title_middle_screen() { + local cols rows + cols=$(tput cols 2>/dev/null || echo 80) + rows=$(tput lines 2>/dev/null || echo 24) + + local -a lines=( "${GHOSTLINE_INFO_PANEL[@]}" ) + local h=${#lines[@]} + + local max_w=0 raw visible_len line + for line in "${lines[@]}"; do + raw=$(printf "%s" "$line" | _strip_ansi) + visible_len=${#raw} + (( visible_len > max_w )) && max_w=$visible_len + done + + local top=$(( (rows - h) / 2 )) + (( top < 0 )) && top=0 + local left=$(( (cols - max_w) / 2 )) + (( left < 0 )) && left=0 + + printf "\033c" + local i + for ((i=0; i menu_count ? ascii_count : menu_count )) + + local max_ascii_width=0 line + for line in "${ascii_lines[@]}"; do + (( ${#line} > max_ascii_width )) && max_ascii_width=${#line} + done + + local spacing=" " + local i + for ((i=0; i Date: Wed, 27 May 2026 19:44:54 +0200 Subject: [PATCH 03/12] refactor: split menus into lib/modules/{config,passive,active,special} --- lib/modules/active.sh | 115 +++++++++++++++++++++++++++++++++++++++++ lib/modules/config.sh | 94 +++++++++++++++++++++++++++++++++ lib/modules/passive.sh | 105 +++++++++++++++++++++++++++++++++++++ lib/modules/special.sh | 112 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 426 insertions(+) create mode 100644 lib/modules/active.sh create mode 100644 lib/modules/config.sh create mode 100644 lib/modules/passive.sh create mode 100644 lib/modules/special.sh diff --git a/lib/modules/active.sh b/lib/modules/active.sh new file mode 100644 index 0000000..e50328e --- /dev/null +++ b/lib/modules/active.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +# lib/modules/active.sh — Authenticated enumeration (credentials required). + +if [[ -n "${GHOSTLINE_MODULE_ACTIVE_LOADED:-}" ]]; then + return 0 +fi +GHOSTLINE_MODULE_ACTIVE_LOADED=1 + +active_run_bloodhound() { + printf '\n%bBloodHound Collection%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + require_domain + require_credentials + ensure_command "bloodhound-python" "pipx install bloodhound" || return 0 + ensure_output_dir + log_step "Collecting BloodHound data..." + bloodhound-python \ + -u "${GHOSTLINE_USERNAME}" -p "${GHOSTLINE_PASSWORD}" \ + -d "${GHOSTLINE_DOMAIN}" -ns "${GHOSTLINE_TARGET}" \ + -c all --zip -o "${GHOSTLINE_OUTPUT_DIR}" + log_success "JSON files generated" + press_enter_to_continue +} + +active_run_cme() { + printf '\n%bCrackMapExec%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + require_credentials + local cme + if ! cme=$(resolve_command "crackmapexec" "cme" "nxc"); then + log_warn "Neither crackmapexec, cme nor nxc is installed." + log_info "Hint: pipx install crackmapexec (or pipx install netexec)" + return 0 + fi + ensure_output_dir + log_step "Running ${cme##*/} enumeration..." + "$cme" smb "${GHOSTLINE_TARGET}" \ + -u "${GHOSTLINE_USERNAME}" -p "${GHOSTLINE_PASSWORD}" --shares \ + | tee "${GHOSTLINE_OUTPUT_DIR}/cme_shares.txt" + "$cme" smb "${GHOSTLINE_TARGET}" \ + -u "${GHOSTLINE_USERNAME}" -p "${GHOSTLINE_PASSWORD}" --users \ + | tee "${GHOSTLINE_OUTPUT_DIR}/cme_users.txt" + log_success "Results saved" + press_enter_to_continue +} + +active_run_adidns() { + printf '\n%bAD DNS Dump%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + require_domain + require_credentials + ensure_command "adidnsdump" "pip install adidnsdump" || return 0 + ensure_output_dir + log_step "Dumping DNS records..." + adidnsdump \ + -u "${GHOSTLINE_DOMAIN}\\${GHOSTLINE_USERNAME}" \ + -p "${GHOSTLINE_PASSWORD}" \ + "${GHOSTLINE_TARGET}" -r \ + --output "${GHOSTLINE_OUTPUT_DIR}/dns.csv" + log_success "DNS records saved" + press_enter_to_continue +} + +active_run_getnpusers() { + printf '\n%bGetNPUsers (ASREPRoast)%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + require_domain + local getnp + if ! getnp=$(resolve_command "GetNPUsers.py" "impacket-GetNPUsers"); then + log_warn "GetNPUsers.py / impacket-GetNPUsers is not installed." + log_info "Hint: pipx install impacket" + return 0 + fi + ensure_output_dir + log_step "Searching for AS-REP roastable accounts..." + "$getnp" "${GHOSTLINE_DOMAIN}/" -dc-ip "${GHOSTLINE_TARGET}" -no-pass \ + | tee "${GHOSTLINE_OUTPUT_DIR}/asreproast.txt" + log_success "Results saved" + press_enter_to_continue +} + +active_run_ridenum() { + printf '\n%bRID Enumeration%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + ensure_command "ridenum" "sudo ./install.sh" || return 0 + ensure_output_dir + log_step "Enumerating RIDs (500..10000)..." + ridenum "${GHOSTLINE_TARGET}" 500 10000 \ + | tee "${GHOSTLINE_OUTPUT_DIR}/ridenum.txt" + log_success "Results saved" + press_enter_to_continue +} + +handle_active_menu() { + local choice + while true; do + clear + display_banner_with_menu "active" + prompt_menu_choice "Active Enum" + read -r choice + + case "$choice" in + 1) active_run_bloodhound ;; + 2) active_run_cme ;; + 3) active_run_adidns ;; + 4) active_run_getnpusers ;; + 5) active_run_ridenum ;; + 0) return ;; + *) + printf '\n%bInvalid choice!%b\n' "${BRIGHT_RED}" "${RESET}" + sleep 1 + ;; + esac + done +} diff --git a/lib/modules/config.sh b/lib/modules/config.sh new file mode 100644 index 0000000..af38528 --- /dev/null +++ b/lib/modules/config.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# lib/modules/config.sh — Target, domain, credentials and output configuration. + +if [[ -n "${GHOSTLINE_MODULE_CONFIG_LOADED:-}" ]]; then + return 0 +fi +GHOSTLINE_MODULE_CONFIG_LOADED=1 + +# --------------------------------------------------------------------------- +# Quick checks used by enumeration modules. Each prompts the user if the +# corresponding global is empty. +# --------------------------------------------------------------------------- +require_target() { + if [[ -z "${GHOSTLINE_TARGET}" ]]; then + log_warn "No target set." + GHOSTLINE_TARGET=$(prompt_value "Enter target IP/hostname") + fi +} + +require_domain() { + if [[ -z "${GHOSTLINE_DOMAIN}" ]]; then + log_info "No domain set." + GHOSTLINE_DOMAIN=$(prompt_value "Enter domain (e.g. domain.local)") + fi +} + +require_credentials() { + if [[ -z "${GHOSTLINE_USERNAME}" || -z "${GHOSTLINE_PASSWORD}" ]]; then + log_warn "Credentials not set." + GHOSTLINE_USERNAME=$(prompt_value "Username") + GHOSTLINE_PASSWORD=$(prompt_password "Password") + fi +} + +ensure_output_dir() { + mkdir -p "${GHOSTLINE_OUTPUT_DIR}" +} + +# --------------------------------------------------------------------------- +# Configuration menu actions. +# --------------------------------------------------------------------------- +config_set_target() { + printf '\n%bSetting Target%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + GHOSTLINE_TARGET=$(prompt_value "Target IP/hostname") + log_success "Target set: ${GHOSTLINE_TARGET}" + press_enter_to_continue +} + +config_set_domain() { + printf '\n%bSetting Domain%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + GHOSTLINE_DOMAIN=$(prompt_value "Domain name") + log_success "Domain set: ${GHOSTLINE_DOMAIN}" + press_enter_to_continue +} + +config_set_credentials() { + printf '\n%bSetting Credentials%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + GHOSTLINE_USERNAME=$(prompt_value "Username") + GHOSTLINE_PASSWORD=$(prompt_password "Password") + log_success "Credentials configured" + press_enter_to_continue +} + +config_set_output_dir() { + printf '\n%bSetting Output Directory%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + local custom_dir + custom_dir=$(prompt_value "Directory name" "${GHOSTLINE_OUTPUT_DIR}") + GHOSTLINE_OUTPUT_DIR="${custom_dir}" + mkdir -p "${GHOSTLINE_OUTPUT_DIR}" + log_success "Output: ${GHOSTLINE_OUTPUT_DIR}" + press_enter_to_continue +} + +handle_config_menu() { + local choice + while true; do + clear + display_banner_with_menu "config" + prompt_menu_choice "Configure" + read -r choice + + case "$choice" in + 1) config_set_target ;; + 2) config_set_domain ;; + 3) config_set_credentials ;; + 4) config_set_output_dir ;; + 0) return ;; + *) + printf '\n%bInvalid choice!%b\n' "${BRIGHT_RED}" "${RESET}" + sleep 1 + ;; + esac + done +} diff --git a/lib/modules/passive.sh b/lib/modules/passive.sh new file mode 100644 index 0000000..ab40d85 --- /dev/null +++ b/lib/modules/passive.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +# lib/modules/passive.sh — Passive reconnaissance (no credentials required). + +if [[ -n "${GHOSTLINE_MODULE_PASSIVE_LOADED:-}" ]]; then + return 0 +fi +GHOSTLINE_MODULE_PASSIVE_LOADED=1 + +# Convert a dotted domain (corp.local) into an LDAP base DN (dc=corp,dc=local). +_domain_to_basedn() { + local domain="$1" + printf 'dc=%s' "${domain//./,dc=}" +} + +passive_run_nmap() { + printf '\n%bNmap Scan%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + ensure_command "nmap" "sudo apt install nmap" || return 0 + ensure_output_dir + log_step "Scanning AD ports on ${GHOSTLINE_TARGET}..." + nmap -p 88,135,139,389,445,464,636,3268,3269,5985 -sV -sC \ + -oA "${GHOSTLINE_OUTPUT_DIR}/nmap_ad" "${GHOSTLINE_TARGET}" + log_success "Results saved to ${GHOSTLINE_OUTPUT_DIR}/nmap_ad.*" + press_enter_to_continue +} + +passive_run_enum4linux() { + printf '\n%bEnum4linux-ng%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + ensure_command "enum4linux-ng" "sudo ./install.sh" || return 0 + ensure_output_dir + log_step "Running enum4linux-ng..." + enum4linux-ng -A "${GHOSTLINE_TARGET}" \ + | tee "${GHOSTLINE_OUTPUT_DIR}/enum4linux-ng.txt" + log_success "Results saved" + press_enter_to_continue +} + +passive_run_rpc() { + printf '\n%bRPC Client%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + ensure_command "rpcclient" "sudo apt install samba-common-bin" || return 0 + ensure_output_dir + log_step "Attempting RPC null session..." + { + echo "srvinfo" + echo "enumdomusers" + echo "enumdomgroups" + echo "exit" + } | rpcclient -U "" "${GHOSTLINE_TARGET}" -N \ + | tee "${GHOSTLINE_OUTPUT_DIR}/rpcclient.txt" + log_success "Results saved" + press_enter_to_continue +} + +passive_run_ldap() { + printf '\n%bLDAP Search%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + require_domain + ensure_command "ldapsearch" "sudo apt install ldap-utils" || return 0 + ensure_output_dir + local base_dn + base_dn=$(_domain_to_basedn "${GHOSTLINE_DOMAIN}") + log_step "Querying LDAP (base ${base_dn})..." + ldapsearch -x -H "ldap://${GHOSTLINE_TARGET}" -b "${base_dn}" "(objectclass=*)" \ + | tee "${GHOSTLINE_OUTPUT_DIR}/ldap.txt" + log_success "Results saved" + press_enter_to_continue +} + +passive_run_dns() { + printf '\n%bDNS Enumeration%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + require_domain + ensure_command "dnsrecon" "sudo apt install dnsrecon" || return 0 + ensure_output_dir + log_step "Running dnsrecon..." + dnsrecon -d "${GHOSTLINE_DOMAIN}" -n "${GHOSTLINE_TARGET}" \ + | tee "${GHOSTLINE_OUTPUT_DIR}/dnsrecon.txt" + log_success "Results saved" + press_enter_to_continue +} + +handle_passive_menu() { + local choice + while true; do + clear + display_banner_with_menu "passive" + prompt_menu_choice "Passive Recon" + read -r choice + + case "$choice" in + 1) passive_run_nmap ;; + 2) passive_run_enum4linux ;; + 3) passive_run_rpc ;; + 4) passive_run_ldap ;; + 5) passive_run_dns ;; + 0) return ;; + *) + printf '\n%bInvalid choice!%b\n' "${BRIGHT_RED}" "${RESET}" + sleep 1 + ;; + esac + done +} diff --git a/lib/modules/special.sh b/lib/modules/special.sh new file mode 100644 index 0000000..14d18c8 --- /dev/null +++ b/lib/modules/special.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# lib/modules/special.sh — Automated workflow, SMB vulns, secrets dump, results. + +if [[ -n "${GHOSTLINE_MODULE_SPECIAL_LOADED:-}" ]]; then + return 0 +fi +GHOSTLINE_MODULE_SPECIAL_LOADED=1 + +special_run_workflow() { + printf '\n%b%bAUTO WORKFLOW%b\n' "${BRIGHT_MAGENTA}" "${BOLD}" "${RESET}" + require_target + ensure_output_dir + log_step "Running automated enumeration workflow..." + echo "" + + if ensure_command "nmap" "sudo apt install nmap"; then + log_step "Phase 1: Network Scan" + nmap -p 88,135,139,389,445 -sV "${GHOSTLINE_TARGET}" \ + -oA "${GHOSTLINE_OUTPUT_DIR}/nmap" 2>/dev/null + fi + + if ensure_command "enum4linux-ng" "sudo ./install.sh"; then + log_step "Phase 2: SMB Enumeration" + enum4linux-ng -A "${GHOSTLINE_TARGET}" \ + > "${GHOSTLINE_OUTPUT_DIR}/enum4linux.txt" 2>/dev/null + fi + + if ensure_command "rpcclient" "sudo apt install samba-common-bin"; then + log_step "Phase 3: RPC Enumeration" + { echo "enumdomusers"; echo "exit"; } \ + | rpcclient -U "" "${GHOSTLINE_TARGET}" -N \ + > "${GHOSTLINE_OUTPUT_DIR}/rpc.txt" 2>/dev/null + fi + + if [[ -n "${GHOSTLINE_DOMAIN}" ]] && ensure_command "ldapsearch" "sudo apt install ldap-utils"; then + log_step "Phase 4: LDAP Query" + local base_dn="dc=${GHOSTLINE_DOMAIN//./,dc=}" + ldapsearch -x -H "ldap://${GHOSTLINE_TARGET}" -b "${base_dn}" "(objectclass=user)" \ + > "${GHOSTLINE_OUTPUT_DIR}/ldap.txt" 2>/dev/null + fi + + echo "" + log_success "Workflow complete. Results in ${GHOSTLINE_OUTPUT_DIR}/" + press_enter_to_continue +} + +special_run_smb_vulns() { + printf '\n%bSMB Vulnerabilities%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + ensure_command "nmap" "sudo apt install nmap" || return 0 + ensure_output_dir + log_step "Scanning for SMB vulnerabilities..." + nmap -p 445 --script 'smb-vuln*' "${GHOSTLINE_TARGET}" \ + -oA "${GHOSTLINE_OUTPUT_DIR}/smb_vulns" + log_success "Vulnerability scan complete" + press_enter_to_continue +} + +special_run_secretsdump() { + printf '\n%bSecrets Dump%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + require_target + require_domain + require_credentials + local secretsdump + if ! secretsdump=$(resolve_command "secretsdump.py" "impacket-secretsdump"); then + log_warn "secretsdump.py / impacket-secretsdump is not installed." + log_info "Hint: pipx install impacket" + return 0 + fi + ensure_output_dir + printf '%b%b! This requires elevated privileges!%b\n' \ + "${BRIGHT_RED}" "${BOLD}" "${RESET}" + log_step "Dumping secrets..." + "$secretsdump" \ + "${GHOSTLINE_DOMAIN}/${GHOSTLINE_USERNAME}:${GHOSTLINE_PASSWORD}@${GHOSTLINE_TARGET}" \ + | tee "${GHOSTLINE_OUTPUT_DIR}/secrets.txt" + log_success "Secrets saved" + press_enter_to_continue +} + +special_view_results() { + printf '\n%bResults Viewer%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + if [[ ! -d "${GHOSTLINE_OUTPUT_DIR}" ]]; then + log_warn "No results directory found at ${GHOSTLINE_OUTPUT_DIR}" + else + log_info "Files in ${GHOSTLINE_OUTPUT_DIR}:" + ls -lh "${GHOSTLINE_OUTPUT_DIR}" + fi + press_enter_to_continue +} + +handle_special_menu() { + local choice + while true; do + clear + display_banner_with_menu "special" + prompt_menu_choice "Special Ops" + read -r choice + + case "$choice" in + 1) special_run_workflow ;; + 2) special_run_smb_vulns ;; + 3) special_run_secretsdump ;; + 4) special_view_results ;; + 0) return ;; + *) + printf '\n%bInvalid choice!%b\n' "${BRIGHT_RED}" "${RESET}" + sleep 1 + ;; + esac + done +} From 55582338025722635bdb097c4ce7c6010d40ac24 Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:44:58 +0200 Subject: [PATCH 04/12] refactor: rewrite ghostline.sh as a thin orchestrator --- ghostline.sh | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 ghostline.sh diff --git a/ghostline.sh b/ghostline.sh new file mode 100644 index 0000000..a6fabea --- /dev/null +++ b/ghostline.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Ghostline — Active Directory Enumeration Toolkit. +# Entry point: load the library, then drive the interactive menu loop. + +set -uo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_DIR + +# shellcheck source=lib/core.sh +source "${SCRIPT_DIR}/lib/core.sh" +# shellcheck source=lib/installer.sh +source "${SCRIPT_DIR}/lib/installer.sh" +# shellcheck source=lib/ui.sh +source "${SCRIPT_DIR}/lib/ui.sh" +# shellcheck source=lib/modules/config.sh +source "${SCRIPT_DIR}/lib/modules/config.sh" +# shellcheck source=lib/modules/passive.sh +source "${SCRIPT_DIR}/lib/modules/passive.sh" +# shellcheck source=lib/modules/active.sh +source "${SCRIPT_DIR}/lib/modules/active.sh" +# shellcheck source=lib/modules/special.sh +source "${SCRIPT_DIR}/lib/modules/special.sh" + +main_loop() { + local choice + display_title_middle_screen + sleep 2 + + while true; do + clear + display_banner_with_menu "main" + echo -ne " ${BOLD}${BRIGHT_RED}▪ Choose your line, sir : ${RESET}" + read -r choice + + case "$choice" in + 1) handle_config_menu ;; + 2) handle_passive_menu ;; + 3) handle_active_menu ;; + 4) handle_special_menu ;; + 0) + printf '\n%bExiting Ghostline...%b\n' "${BRIGHT_MAGENTA}" "${RESET}" + exit 0 + ;; + *) + printf '\n%bInvalid choice!%b\n' "${BRIGHT_RED}" "${RESET}" + sleep 1 + ;; + esac + done +} + +main_loop "$@" From d695fdecd4a478f1f429d1c7068c2da48859e909 Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:45:01 +0200 Subject: [PATCH 05/12] refactor(install): translate to English, reuse lib/installer helpers, harden quoting --- install.sh | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 install.sh diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..120644d --- /dev/null +++ b/install.sh @@ -0,0 +1,238 @@ +#!/usr/bin/env bash +# install.sh — Install every dependency required by Ghostline. +# Designed for Debian/Ubuntu/Kali. Run with sudo. + +set -euo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_DIR + +# shellcheck source=lib/core.sh +source "${SCRIPT_DIR}/lib/core.sh" +# shellcheck source=lib/installer.sh +source "${SCRIPT_DIR}/lib/installer.sh" + +require_root + +# --------------------------------------------------------------------------- +# Disable known broken apt repositories. +# --------------------------------------------------------------------------- +disable_broken_repos() { + log_step "Cleaning up broken apt repositories..." + mkdir -p /etc/apt/sources.list.d/disabled + + if ls /etc/apt/sources.list.d/*winehq* >/dev/null 2>&1; then + mv /etc/apt/sources.list.d/*winehq* /etc/apt/sources.list.d/disabled/ \ + 2>/dev/null || true + fi + + if grep -r "zara" /etc/apt/sources.list.d/ >/dev/null 2>&1; then + while IFS= read -r file; do + mv "$file" /etc/apt/sources.list.d/disabled/ 2>/dev/null || true + done < <(grep -rl "zara" /etc/apt/sources.list.d/) + fi + + log_success "Repositories cleaned" +} + +# --------------------------------------------------------------------------- +# Resolve an ldap-utils-equivalent package available on the current distro. +# --------------------------------------------------------------------------- +resolve_ldap_package() { + local pkg + pkg=$(apt-cache search "ldap" 2>/dev/null \ + | grep -E "ldap.*utils|openldap.*client" \ + | head -1 \ + | awk '{print $1}') + if [[ -z "$pkg" ]]; then + pkg="ldap-utils" + fi + printf '%s' "$pkg" +} + +install_base_dependencies() { + log_step "Installing base dependencies..." + + local ldap_pkg + ldap_pkg=$(resolve_ldap_package) + + apt update -y 2>&1 | grep -v "NO_PUBKEY\|not signed" || true + + apt_install \ + git \ + python3 \ + python3-pip \ + python3-venv \ + python3-full \ + pipx \ + samba \ + samba-common-bin \ + smbclient \ + "${ldap_pkg}" \ + nmap \ + dnsrecon \ + dnsenum \ + curl \ + wget \ + build-essential \ + libsasl2-dev \ + libldap2-dev \ + libssl-dev 2>&1 | grep -v "WARNING" || true + + log_success "Base dependencies installed" + + export PATH="${PATH}:/root/.local/bin:${HOME}/.local/bin" + pipx ensurepath 2>/dev/null || true +} + +install_enum4linux_ng() { + log_step "Installing enum4linux-ng..." + local dest="${GHOSTLINE_TOOLS_DIR}/enum4linux-ng" + clone_or_pull "https://github.com/cddmp/enum4linux-ng.git" "$dest" + install_pip_requirements "$dest" + chmod +x "${dest}/enum4linux-ng.py" + ln -sf "${dest}/enum4linux-ng.py" /usr/local/bin/enum4linux-ng + log_success "enum4linux-ng installed" +} + +install_crackmapexec() { + log_step "Installing CrackMapExec..." + if apt_install crackmapexec 2>/dev/null; then + log_success "CrackMapExec installed via apt" + else + pipx_install crackmapexec + log_success "CrackMapExec installed via pipx" + fi +} + +install_adidnsdump() { + log_step "Installing adidnsdump..." + local dest="${GHOSTLINE_TOOLS_DIR}/adidnsdump" + clone_or_pull "https://github.com/dirkjanm/adidnsdump.git" "$dest" + install_pip_requirements "$dest" + (cd "$dest" || exit 1; pip_install ".") + log_success "adidnsdump installed" +} + +install_bloodhound_py() { + log_step "Installing BloodHound.py..." + pipx_install bloodhound || pip_install bloodhound + log_success "BloodHound.py installed" +} + +install_ridenum() { + log_step "Installing ridenum..." + local dest="${GHOSTLINE_TOOLS_DIR}/ridenum" + clone_or_pull "https://github.com/trustedsec/ridenum.git" "$dest" + chmod +x "${dest}/ridenum.py" + ln -sf "${dest}/ridenum.py" /usr/local/bin/ridenum + log_success "ridenum installed" +} + +install_impacket() { + log_step "Installing Impacket..." + pipx_install impacket || pip_install impacket + log_success "Impacket installed" +} + +install_kerbrute() { + log_step "Installing Kerbrute..." + local arch kerbrute_arch + arch=$(uname -m) + case "$arch" in + x86_64) kerbrute_arch="amd64" ;; + aarch64|arm64) kerbrute_arch="arm64" ;; + armv7l) kerbrute_arch="arm" ;; + *) + log_warn "Architecture not supported for Kerbrute: ${arch}" + kerbrute_arch="amd64" + ;; + esac + + local version="1.0.3" + local url="https://github.com/ropnop/kerbrute/releases/download/v${version}/kerbrute_linux_${kerbrute_arch}" + local dest="${GHOSTLINE_TOOLS_DIR}/kerbrute" + + (cd "${GHOSTLINE_TOOLS_DIR}" || exit 1 + wget -q "$url" -O kerbrute 2>/dev/null \ + || curl -sL "$url" -o kerbrute) + + if [[ -f "$dest" ]]; then + chmod +x "$dest" + ln -sf "$dest" /usr/local/bin/kerbrute + log_success "Kerbrute installed" + else + log_warn "Failed to download Kerbrute" + fi +} + +install_ldapdomaindump() { + log_step "Installing ldapdomaindump..." + pip_install ldapdomaindump + log_success "ldapdomaindump installed" +} + +configure_path() { + log_step "Configuring PATH..." + if ! grep -q ".local/bin" /root/.bashrc 2>/dev/null; then + echo 'export PATH="$PATH:$HOME/.local/bin:/root/.local/bin"' >> /root/.bashrc + fi + + if [[ -n "${SUDO_USER:-}" ]]; then + local user_home + user_home=$(getent passwd "$SUDO_USER" | cut -d: -f6) + if [[ -f "${user_home}/.bashrc" ]]; then + if ! grep -q ".local/bin" "${user_home}/.bashrc"; then + echo 'export PATH="$PATH:$HOME/.local/bin"' >> "${user_home}/.bashrc" + chown "${SUDO_USER}:${SUDO_USER}" "${user_home}/.bashrc" + fi + fi + fi + + export PATH="${PATH}:${HOME}/.local/bin:/root/.local/bin" +} + +print_summary() { + echo "" + echo "========================================================================" + log_success "Installation complete." + echo "========================================================================" + echo "" + echo "Installed tools:" + printf " [1] enum4linux-ng -> %s\n" "/usr/local/bin/enum4linux-ng" + printf " [2] ldapsearch -> %s\n" "$(command -v ldapsearch 2>/dev/null || echo 'install manually')" + printf " [3] nmap + NSE -> %s\n" "$(command -v nmap 2>/dev/null || echo 'not found')" + printf " [4] rpcclient -> %s\n" "$(command -v rpcclient 2>/dev/null || echo 'not found')" + printf " [5] CrackMapExec -> %s\n" "$(command -v crackmapexec 2>/dev/null || command -v cme 2>/dev/null || echo '~/.local/bin/')" + printf " [6] adidnsdump -> %s\n" "$(command -v adidnsdump 2>/dev/null || echo '~/.local/bin/')" + printf " [7] BloodHound.py -> %s\n" "$(command -v bloodhound-python 2>/dev/null || echo '~/.local/bin/')" + printf " [8] ridenum -> %s\n" "/usr/local/bin/ridenum" + printf " [9] Impacket -> %s\n" "$(command -v secretsdump.py 2>/dev/null || command -v impacket-secretsdump 2>/dev/null || echo '~/.local/bin/')" + printf " [10] dnsrecon/dnsenum -> %s\n" "$(command -v dnsrecon 2>/dev/null || echo 'not found')" + printf " [11] Kerbrute -> %s\n" "/usr/local/bin/kerbrute" + printf " [12] ldapdomaindump -> %s\n" "$(command -v ldapdomaindump 2>/dev/null || echo '~/.local/bin/')" + echo " [13] GetUserSPNs.py -> (included in Impacket)" + echo "" + echo "IMPORTANT:" + echo " Reload your shell: source ~/.bashrc" + echo " Or restart your terminal." + echo "" + echo "========================================================================" +} + +main() { + disable_broken_repos + install_base_dependencies + install_enum4linux_ng + install_crackmapexec + install_adidnsdump + install_bloodhound_py + install_ridenum + install_impacket + install_kerbrute + install_ldapdomaindump + configure_path + print_summary +} + +main "$@" From 181f80526c4697b4d1d651dd1b65a8df522c56f5 Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:45:22 +0200 Subject: [PATCH 06/12] chore: mark ghostline.sh and install.sh as executable --- ghostline.sh | 0 install.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 ghostline.sh mode change 100644 => 100755 install.sh diff --git a/ghostline.sh b/ghostline.sh old mode 100644 new mode 100755 diff --git a/install.sh b/install.sh old mode 100644 new mode 100755 From 5c32620f4bfd954a3d249dc5b5420028cb5c09b1 Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:46:33 +0200 Subject: [PATCH 07/12] ci: add shellcheck, syntax and smoke-test GitHub Actions workflow Also adds issue templates (bug report, tool request) and PR template. --- .github/ISSUE_TEMPLATE/bug_report.yml | 82 ++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 ++ .github/ISSUE_TEMPLATE/tool_request.yml | 57 ++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 32 ++++++++ .github/workflows/ci.yml | 99 +++++++++++++++++++++++++ 5 files changed, 275 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/tool_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/ci.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..f6834a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,82 @@ +name: Bug report +description: Report a bug or unexpected behavior in Ghostline +title: "[Bug] " +labels: ["bug"] +body: + - type: textarea + id: summary + attributes: + label: Summary + description: One or two sentences describing the bug. + placeholder: "RID enumeration crashes when target is unreachable." + validations: + required: true + + - type: input + id: version + attributes: + label: Ghostline version + description: "Commit SHA or release tag. Run `git rev-parse --short HEAD` in the repo root." + placeholder: "e.g. v1.0.0 or abc1234" + validations: + required: true + + - type: input + id: os + attributes: + label: Operating system + description: "Distribution and version." + placeholder: "e.g. Kali Linux 2025.3" + validations: + required: true + + - type: dropdown + id: module + attributes: + label: Affected module + options: + - lib/core.sh + - lib/ui.sh + - lib/installer.sh + - lib/modules/config.sh + - lib/modules/passive.sh + - lib/modules/active.sh + - lib/modules/special.sh + - install.sh + - ghostline.sh (entry point) + - Not sure + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: Exact menu path, inputs, and target configuration. + placeholder: | + 1. Launch ./ghostline.sh + 2. Main Menu → [3] Active Enumeration → [5] RID Enumeration + 3. Target was set to 10.10.10.10 (offline) + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual behavior + description: Paste error output. Use code fences (```) for logs. + validations: + required: true + + - type: textarea + id: extra + attributes: + label: Additional context + description: Logs, screenshots, related issues — anything else that helps. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..ac4a7ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability + url: https://github.com/WhiteMuush/GhostLine/security/advisories/new + about: Report a security issue privately via GitHub Security Advisories. diff --git a/.github/ISSUE_TEMPLATE/tool_request.yml b/.github/ISSUE_TEMPLATE/tool_request.yml new file mode 100644 index 0000000..ee54279 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tool_request.yml @@ -0,0 +1,57 @@ +name: Tool request +description: Propose a new tool or module for Ghostline +title: "[Tool] " +labels: ["enhancement", "tool-request"] +body: + - type: input + id: tool_name + attributes: + label: Tool name + placeholder: "e.g. certipy" + validations: + required: true + + - type: input + id: tool_url + attributes: + label: Upstream URL + placeholder: "https://github.com/ly4k/Certipy" + validations: + required: true + + - type: textarea + id: rationale + attributes: + label: Why this tool fits Ghostline + description: How does it help with Active Directory enumeration or exploitation? + validations: + required: true + + - type: dropdown + id: target_module + attributes: + label: Suggested module placement + options: + - lib/modules/passive.sh + - lib/modules/active.sh + - lib/modules/special.sh + - New module + validations: + required: true + + - type: textarea + id: install_hint + attributes: + label: Installation hint + description: How is the tool installed on Debian/Kali? (apt, pipx, git clone, ...) + placeholder: | + pipx install certipy-ad + validations: + required: true + + - type: textarea + id: example_usage + attributes: + label: Example invocation + description: A representative command, ideally usable as the body of the new module function. + render: bash diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..4f7ef37 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,32 @@ + + +## Summary + + + +## Type of change + +- [ ] New tool / module +- [ ] Bug fix +- [ ] Refactor / cleanup +- [ ] Documentation +- [ ] CI / tooling + +## Checklist + +- [ ] `bash -n` passes on every modified `.sh` +- [ ] `shellcheck` passes (CI will verify) +- [ ] `lib/` source chain still loads (smoke test in CI) +- [ ] No French text introduced (project is English-only) +- [ ] If adding a tool: I followed `docs/ADDING_A_TOOL.md` +- [ ] `README.md` updated if the menus, CLI or install steps changed + +## Test plan + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d8bf675 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,99 @@ +name: ci + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + shellcheck: + name: ShellCheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run shellcheck + uses: ludeeus/action-shellcheck@master + env: + SHELLCHECK_OPTS: -e SC1091 -e SC2034 -e SC2154 + with: + severity: warning + ignore_paths: >- + graphify-out + + syntax: + name: bash -n + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Syntax check every shell script + shell: bash + run: | + set -euo pipefail + failed=0 + while IFS= read -r -d '' file; do + if ! bash -n "$file"; then + echo "SYNTAX ERROR: $file" + failed=1 + fi + done < <(find . -name '*.sh' -not -path './.git/*' -print0) + exit "$failed" + + smoke: + name: Source chain smoke test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Verify expected functions are exported + shell: bash + run: | + set -uo pipefail + # shellcheck disable=SC1091 + source ./lib/core.sh + # shellcheck disable=SC1091 + source ./lib/installer.sh + # shellcheck disable=SC1091 + source ./lib/ui.sh + # shellcheck disable=SC1091 + source ./lib/modules/config.sh + # shellcheck disable=SC1091 + source ./lib/modules/passive.sh + # shellcheck disable=SC1091 + source ./lib/modules/active.sh + # shellcheck disable=SC1091 + source ./lib/modules/special.sh + + expected=( + log_step log_info log_warn log_error log_success + prompt_value prompt_password prompt_yesno press_enter_to_continue + ensure_command resolve_command + clone_or_pull pip_install pipx_install apt_install + generate_main_menu generate_config_menu generate_passive_menu + generate_active_menu generate_special_menu + display_banner_with_menu display_title_middle_screen prompt_menu_choice + require_target require_domain require_credentials ensure_output_dir + handle_config_menu handle_passive_menu handle_active_menu handle_special_menu + passive_run_nmap passive_run_enum4linux passive_run_rpc + passive_run_ldap passive_run_dns + active_run_bloodhound active_run_cme active_run_adidns + active_run_getnpusers active_run_ridenum + special_run_workflow special_run_smb_vulns special_run_secretsdump + special_view_results + ) + + missing=0 + for fn in "${expected[@]}"; do + if ! declare -F "$fn" >/dev/null; then + echo "MISSING: $fn" + missing=$((missing+1)) + fi + done + + if (( missing > 0 )); then + echo "Smoke test FAILED: $missing function(s) missing" + exit 1 + fi + echo "Smoke test PASSED — ${#expected[@]} functions present" From 4ad837c69f2e1cb3ef741edac62ee4b6dc379685 Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:47:29 +0200 Subject: [PATCH 08/12] docs: add CONTRIBUTING, CODE_OF_CONDUCT and SECURITY --- CODE_OF_CONDUCT.md | 13 +++++ CONTRIBUTING.md | 129 +++++++++++++++++++++++++++++++++++++++++++++ SECURITY.md | 43 +++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..fd60d9f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,13 @@ +# Code of Conduct + +This project adopts the **Contributor Covenant**, version 2.1. + +The full text is published at +. + +By participating in this project (issues, pull requests, discussions) +you agree to abide by its terms. + +To report a concern, contact the maintainer via the email address on +their GitHub profile, or privately through +[GitHub Security Advisories](https://github.com/WhiteMuush/GhostLine/security/advisories/new). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..fc6f88e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,129 @@ +# Contributing to Ghostline + +Thanks for the interest. Ghostline is an interactive Active Directory +enumeration toolkit built on top of well-known security tools. The codebase +is plain Bash and aims to stay small, readable and contributor-friendly. + +This guide covers the conventions that make a contribution easy to review +and merge. + +--- + +## Local setup + +```bash +git clone https://github.com/WhiteMuush/GhostLine.git +cd GhostLine +sudo ./install.sh # installs every supported tool system-wide +./ghostline.sh # launch the interactive menu +``` + +Ghostline targets **Debian / Ubuntu / Kali**. Other distros may work but +are not part of CI. + +Optional local checks before opening a PR: + +```bash +# Syntax check on every bash script +find . -name '*.sh' -not -path './.git/*' -exec bash -n {} \; + +# Shellcheck (matches what CI runs) +shellcheck -e SC1091 -e SC2034 -e SC2154 \ + ghostline.sh install.sh lib/*.sh lib/modules/*.sh +``` + +--- + +## Project layout + +``` +ghostline.sh Thin entry point; loads lib/ and drives the loop. +install.sh Installs every supported tool. Run with sudo. +lib/core.sh Colors (TTY-aware), globals, palette. +lib/ui.sh ASCII art and menu rendering. +lib/installer.sh Logging, prompting and install primitives. +lib/modules/config.sh Target / domain / credentials / output config. +lib/modules/passive.sh Unauthenticated reconnaissance. +lib/modules/active.sh Authenticated enumeration. +lib/modules/special.sh Workflows, vuln scans, secrets dump. +``` + +See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the full description +and [docs/ADDING_A_TOOL.md](docs/ADDING_A_TOOL.md) if you want to plug in +a new tool. + +--- + +## Code conventions + +### Shell + +- `#!/usr/bin/env bash` shebang on every executable script. +- Strict mode at the entry point only: `set -uo pipefail` for the + interactive `ghostline.sh`, `set -euo pipefail` for the non-interactive + `install.sh`. **Do not** add `set -e` to interactive menus — a single + non-zero exit code kills the whole loop. +- Always quote variable expansions: `"${var}"`, not `$var`. +- Function names are `snake_case` and prefixed by their module: + `passive_run_nmap`, `active_run_cme`, etc. +- Global state lives in `GHOSTLINE_*` variables defined in `lib/core.sh`. +- Logging goes through `log_step / log_info / log_warn / log_error / + log_success`. Do not emit raw `echo -e ${RED}...${RESET}` from + application code. +- User prompts go through `prompt_value / prompt_password / prompt_yesno`. +- Tool presence is checked with `ensure_command` (and `resolve_command` + for binaries with multiple possible names). +- Use `mapfile -t arr <<<"$STRING"` or `mapfile -t arr < <(cmd)` for + array splitting. Avoid the legacy `IFS=$'\n' read -r -d '' -a` pattern. + +### Language + +Every file in the repository is **English only** — code, comments, log +messages, prompts, README, docs, commit messages. PRs that introduce +non-English content will be asked to translate before merge. + +### Comments + +Default to writing no comments. Only add one when the *why* is +non-obvious. Don't restate what well-named code already says. + +### Commit messages + +Conventional Commits: + +``` +type: short imperative summary +``` + +Common types: `feat`, `fix`, `refactor`, `docs`, `ci`, `chore`, `test`. + +--- + +## CI + +Every PR runs three checks: + +1. **shellcheck** with warning severity and a small ignore list + (`SC1091`, `SC2034`, `SC2154`). +2. **bash -n** on every `.sh` for syntax. +3. **Smoke test** that sources the full `lib/` chain and asserts that + every public function is defined. + +All three must pass before review. + +--- + +## Reporting bugs and proposing tools + +- Bugs: open an issue with the **Bug report** template. +- New tools: open an issue with the **Tool request** template. Bonus + points for opening the PR that wires the tool in. +- Security issues: see [SECURITY.md](SECURITY.md). + +--- + +## Authorized use only + +Ghostline is for **authorized security testing** (pentest engagements, +CTFs, lab environments). Don't open issues asking for help against +networks you do not own or have written permission to test. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..a7c3f71 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,43 @@ +# Security Policy + +Ghostline is an **offensive security toolkit**. It is meant to be run by +authorized testers against Active Directory environments they own or +have explicit written permission to test. Misuse is the responsibility +of the operator. + +## Scope of this policy + +This policy covers vulnerabilities **in the Ghostline wrapper itself** — +the entry point, the library, the menus and the installer. Examples: + +- Command injection via a configuration field that isn't properly quoted. +- Path traversal in the output directory handling. +- Credentials accidentally written to disk or logs. +- Privilege escalation through the installer. + +It does **not** cover vulnerabilities in the third-party tools +Ghostline wraps (`nmap`, `enum4linux-ng`, `bloodhound-python`, +`crackmapexec`, `impacket`, etc.). Report those upstream. + +## Reporting a vulnerability + +**Please do not open a public issue.** Use one of the private channels: + +1. [GitHub Security Advisories](https://github.com/WhiteMuush/GhostLine/security/advisories/new) + — preferred, lets us collaborate on a fix. +2. Direct contact via the email address on the maintainer's GitHub + profile. + +Include: + +- The Ghostline version (commit SHA or release tag). +- A clear description of the issue and the impact. +- A reproduction recipe — exact menu path, target setup, payload. +- (Optional) a suggested fix. + +## What to expect + +- Acknowledgement within 7 days. +- A discussion of the impact and the proposed fix. +- Coordinated disclosure once a patch is ready. Credit goes to the + reporter unless they prefer to stay anonymous. From eae2956f697b2d6482b64b30d363451527960e6f Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:48:43 +0200 Subject: [PATCH 09/12] docs: add ARCHITECTURE and ADDING_A_TOOL guides --- docs/ADDING_A_TOOL.md | 158 ++++++++++++++++++++++++++++++++++++++++++ docs/ARCHITECTURE.md | 158 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 docs/ADDING_A_TOOL.md create mode 100644 docs/ARCHITECTURE.md diff --git a/docs/ADDING_A_TOOL.md b/docs/ADDING_A_TOOL.md new file mode 100644 index 0000000..0ac61ce --- /dev/null +++ b/docs/ADDING_A_TOOL.md @@ -0,0 +1,158 @@ +# Adding a tool to Ghostline + +Most contributions add a new tool to one of the existing modules. The +process is intentionally short — a single function, a single menu line, +optionally a few lines in `install.sh`. + +This document walks through the recipe step by step. + +--- + +## Pick a module + +| Module | Use case | +|------------------------------|-------------------------------------------------------| +| `lib/modules/passive.sh` | Runs without credentials | +| `lib/modules/active.sh` | Requires a domain user / password | +| `lib/modules/special.sh` | Workflows, vuln scans, post-exploitation | + +If your tool doesn't fit any of the above, open an issue first so we can +agree on whether to create a new module. + +--- + +## Anatomy of a module function + +Every module function follows the same five-line shape: + +```bash +_run_() { + require_target + [require_domain] # if needed + [require_credentials] # if needed + ensure_command "" "" || return 0 + ensure_output_dir + + log_step "Running ..." + "${GHOSTLINE_TARGET}" ... \ + | tee "${GHOSTLINE_OUTPUT_DIR}/" + log_success "Results saved" + press_enter_to_continue +} +``` + +That's it. The framework handles colors, prompting, missing-tool +warnings and output directory creation. + +If the binary may be packaged under several names (for example +`crackmapexec` vs. `cme` vs. `nxc`), use `resolve_command` instead of +`ensure_command`: + +```bash +local cme +if ! cme=$(resolve_command "crackmapexec" "cme" "nxc"); then + log_warn "Neither crackmapexec, cme nor nxc is installed." + log_info "Hint: pipx install netexec" + return 0 +fi +"$cme" smb "${GHOSTLINE_TARGET}" -u "${GHOSTLINE_USERNAME}" ... +``` + +--- + +## Wire it into the menu + +Two edits in the same module file: + +### 1. Update `generate__menu` in `lib/ui.sh` + +Add one line in the `menu_lines` array for the new entry: + +```bash +"${BOLD}${BRIGHT_RED}█${RESET} ${BRIGHT_RED}[6]${RESET} My Cool Tool" +``` + +### 2. Update `handle__menu` in `lib/modules/.sh` + +Add the matching case: + +```bash +case "$choice" in + 1) passive_run_nmap ;; + 2) passive_run_enum4linux ;; + ... + 6) passive_run_my_cool_tool ;; + 0) return ;; +esac +``` + +--- + +## Update `install.sh` if the tool needs installing + +If the tool ships in the standard apt repos: + +```bash +install_my_cool_tool() { + log_step "Installing my-cool-tool..." + apt_install my-cool-tool + log_success "my-cool-tool installed" +} +``` + +If it's a pipx package: + +```bash +install_my_cool_tool() { + log_step "Installing my-cool-tool..." + pipx_install my-cool-tool + log_success "my-cool-tool installed" +} +``` + +If it's a GitHub project that needs cloning + symlink: + +```bash +install_my_cool_tool() { + log_step "Installing my-cool-tool..." + local dest="${GHOSTLINE_TOOLS_DIR}/my-cool-tool" + clone_or_pull "https://github.com/owner/my-cool-tool.git" "$dest" + install_pip_requirements "$dest" + chmod +x "${dest}/main.py" + ln -sf "${dest}/main.py" /usr/local/bin/my-cool-tool + log_success "my-cool-tool installed" +} +``` + +Then add the function to the `main` block at the bottom of `install.sh`. + +--- + +## Checklist before opening the PR + +- [ ] The new module function follows the five-line shape above. +- [ ] Tool presence is checked with `ensure_command` or `resolve_command`. +- [ ] User input goes through `prompt_value` / `prompt_password`, + not raw `read`. +- [ ] Logs go through `log_*`, not `echo -e ${RED}...${RESET}`. +- [ ] Every variable expansion is quoted (`"${var}"`, not `$var`). +- [ ] `bash -n` passes locally on the changed `.sh` files. +- [ ] The menu line and the case in `handle__menu` are updated + in lockstep. +- [ ] If the tool needs installing, `install.sh` is updated. +- [ ] The `README.md` tool list is updated. + +--- + +## Don't / Do + +| Don't | Do | +|--------------------------------------------------------|-------------------------------------------------------------| +| `echo -e "${RED}Running...${RESET}"` | `log_step "Running..."` | +| `read -p "Target: " TARGET` | `target=$(prompt_value "Target")` | +| `command -v foo \|\| { echo missing; return; }` | `ensure_command "foo" "apt install foo" \|\| return 0` | +| `IFS=$'\n' read -r -d '' -a arr <<<"$STR"` | `mapfile -t arr <<<"$STR"` | +| `cd /opt/foo && do_stuff && cd -` | `(cd /opt/foo \|\| exit 1; do_stuff)` | +| `arr=( $(find . -name '*.txt') )` | `mapfile -t arr < <(find . -name '*.txt')` | +| `$CMD --flag $TARGET` | `"$CMD" --flag "${GHOSTLINE_TARGET}"` | +| French comments / log messages / docs | English everywhere | diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..5abfd2f --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,158 @@ +# Architecture + +Ghostline is a bash wrapper around ten-plus Active Directory enumeration +tools. The goal is to expose a single, predictable menu instead of +asking the operator to remember every flag of every tool. + +This document explains the layout of the codebase so that a new +contributor can find the right file in seconds. + +--- + +## File layout + +``` +ghostline.sh Entry point. ~50 lines. +install.sh Installer for Debian / Ubuntu / Kali. +lib/ +├── core.sh Colors (TTY-aware), palette, global state. +├── ui.sh ASCII art, menus, banner rendering. +├── installer.sh Logging, prompting and install primitives. +└── modules/ + ├── config.sh Target / domain / credentials / output config. + ├── passive.sh Unauthenticated reconnaissance. + ├── active.sh Authenticated enumeration. + └── special.sh Automated workflow, vuln scans, secrets dump. +.github/ +├── workflows/ci.yml shellcheck + bash -n + smoke test. +├── ISSUE_TEMPLATE/ Structured issue forms. +└── PULL_REQUEST_TEMPLATE.md +docs/ +├── ARCHITECTURE.md This document. +└── ADDING_A_TOOL.md How to plug in a new tool. +``` + +--- + +## Boot sequence + +``` +./ghostline.sh + │ + ├── set -uo pipefail (strict-ish: -e omitted on purpose) + ├── SCRIPT_DIR= + │ + ├── source lib/core.sh # palette + globals + ├── source lib/installer.sh # logging + prompts + helpers + ├── source lib/ui.sh # ascii art + menus + ├── source lib/modules/config.sh + ├── source lib/modules/passive.sh + ├── source lib/modules/active.sh + ├── source lib/modules/special.sh + │ + └── main_loop + ├── display_title_middle_screen + ├── while true: + │ display_banner_with_menu "main" + │ read choice + │ case → handle_{config,passive,active,special}_menu + └── 0 → exit 0 +``` + +`install.sh` follows the same source chain but only loads `core.sh` and +`installer.sh` (it does not need the UI or modules). + +--- + +## Why no `set -e` + +The interactive `ghostline.sh` deliberately uses `set -uo pipefail` and +**not** `set -e`. Inside a menu loop, any tool that returns a non-zero +exit code (for example `nmap` against an offline host, or `enum4linux-ng` +on a closed SMB port) would otherwise kill the entire toolkit. + +`install.sh` is non-interactive and uses the full `set -euo pipefail`. + +--- + +## Color handling + +`lib/core.sh` defines the palette. Colors are emitted **only** when +stdout is a TTY (`[[ -t 1 ]]`) and `tput` is available. When piped or +redirected, every color variable becomes the empty string so the output +stays clean. + +--- + +## Global state + +All shared runtime state lives in five variables defined in +`lib/core.sh`: + +| Variable | Set by | Read by | +|-----------------------------|---------------------------------------|-------------------------------------------| +| `GHOSTLINE_TARGET` | `config_set_target`, `require_target` | All passive/active/special modules | +| `GHOSTLINE_DOMAIN` | `config_set_domain`, `require_domain` | passive (ldap, dns), active, special | +| `GHOSTLINE_USERNAME` | `config_set_credentials` | active modules, secrets dump | +| `GHOSTLINE_PASSWORD` | `config_set_credentials` | active modules, secrets dump | +| `GHOSTLINE_OUTPUT_DIR` | `config_set_output_dir` | every module that writes to disk | + +Modules never touch globals owned by another module. + +--- + +## Public helpers (`lib/installer.sh`) + +| Helper | Purpose | +|------------------------------|----------------------------------------------------| +| `log_step / log_info / log_warn / log_error / log_success` | Color-coded logging | +| `prompt_value LABEL [DEFAULT]` | Free-form input with optional default | +| `prompt_password LABEL` | Silent password prompt | +| `prompt_yesno LABEL [DEFAULT]` | y/N prompt that returns 0/1 | +| `press_enter_to_continue` | Pause and wait for Enter | +| `ensure_command CMD [HINT]` | Warn if `CMD` is missing, return 1 | +| `resolve_command CMD...` | Echo first available command from a list | +| `clone_or_pull URL DEST` | Git clone or fast-forward pull | +| `pip_install PKG` | Best-effort pip install with PEP 668 fallbacks | +| `pipx_install PKG` | Pipx install with `--force` and warning filter | +| `apt_install PKG...` | Wrapper around `apt install -y` | +| `require_root` | Exit if EUID != 0 | + +--- + +## Public helpers (`lib/modules/config.sh`) + +| Helper | Purpose | +|------------------------------|----------------------------------------------------| +| `require_target` | Prompt if `GHOSTLINE_TARGET` is empty | +| `require_domain` | Prompt if `GHOSTLINE_DOMAIN` is empty | +| `require_credentials` | Prompt if username/password are empty | +| `ensure_output_dir` | `mkdir -p "${GHOSTLINE_OUTPUT_DIR}"` | + +Every module function follows the same shape: + +```bash +passive_run_() { + require_target + [require_domain | require_credentials] + ensure_command "" "" || return 0 + ensure_output_dir + log_step "..." + ... | tee "${GHOSTLINE_OUTPUT_DIR}/" + log_success "Results saved" + press_enter_to_continue +} +``` + +--- + +## CI + +Three jobs run on every push and PR: + +1. `shellcheck` — `severity: warning`, with + `SHELLCHECK_OPTS: -e SC1091 -e SC2034 -e SC2154`. +2. `bash -n` — syntax check on every `.sh` in the tree. +3. Smoke test — sources the full `lib/` chain and asserts that every + public function from `core.sh`, `installer.sh`, `ui.sh` and the four + modules is defined. From 7bc6dbb0cdfec2c9b9b22b96de5d796ae358a45b Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:50:35 +0200 Subject: [PATCH 10/12] docs: rename Readme.md to README.md, add badges and Project Layout Also drops the unimplemented PowerShell promise from the trailer. --- README.md | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..0819380 --- /dev/null +++ b/README.md @@ -0,0 +1,253 @@ +![Gif](https://github.com/user-attachments/assets/d71682ce-9cd3-4151-9f36-0a7b29d5985f) + +

+ License: MIT + CI + PRs welcome + Shellcheck +

+ +**Ghostline** is an interactive bash toolkit that automates Active Directory +enumeration by integrating 10+ professional security tools into a single, +easy-to-use menu. It supports both passive reconnaissance (no credentials) +and active enumeration (with credentials). + +## Features + +### Configuration management +- Persistent target configuration (IP / hostname, domain, credentials). +- Custom output directory naming. +- Configuration displayed in every menu header. + +### Passive enumeration (no credentials required) +- Network scanning (Nmap). +- SMB enumeration (enum4linux-ng). +- RPC null session attacks (rpcclient). +- Anonymous LDAP queries (ldapsearch). +- DNS enumeration (dnsrecon). + +### Active enumeration (credentials required) +- BloodHound data collection. +- Comprehensive SMB enumeration (CrackMapExec). +- AD-integrated DNS dumping (adidnsdump). +- Kerberos pre-auth attacks (GetNPUsers). +- RID cycling enumeration (ridenum). + +### Special actions +- Automated full workflow. +- SMB vulnerability scanning. +- Domain secrets extraction (secretsdump). +- Results viewer. + +--- + +Screenshot + +## Installation + +### Prerequisites + +Ghostline targets Debian / Ubuntu / Kali and bundles an installer for every +supported tool: + +```bash +sudo ./install.sh +``` + +Or install manually: + +```bash +# Debian / Ubuntu / Kali +sudo apt update +sudo apt install -y \ + nmap \ + samba-common-bin \ + ldap-utils \ + dnsrecon \ + python3 \ + python3-pip \ + pipx + +# Python tools +pipx install crackmapexec +pipx install bloodhound +pipx install impacket + +# GitHub-hosted tools +git clone https://github.com/cddmp/enum4linux-ng.git /opt/enum4linux-ng +git clone https://github.com/dirkjanm/adidnsdump.git /opt/adidnsdump +git clone https://github.com/trustedsec/ridenum.git /opt/ridenum +``` + +### Installing Ghostline + +```bash +git clone https://github.com/WhiteMuush/GhostLine.git +cd GhostLine +chmod +x ghostline.sh +./ghostline.sh +``` + +--- + +## Quick start + +### Basic usage + +```bash +./ghostline.sh + +# 1. Configure your target +Main Menu → [1] Configuration Menu + → [1] Set Target: 192.168.1.10 + → [2] Set Domain: corp.local + → [0] Back + +# 2. Run automated reconnaissance +Main Menu → [4] Special Actions + → [1] Auto Workflow + +# 3. View results +Main Menu → [4] Special Actions + → [4] View Results +``` + +### With credentials + +```bash +# 1. Configure credentials +Main Menu → [1] Configuration Menu + → [3] Set Credentials + Username: john.doe + Password: ******** + +# 2. Run BloodHound collection +Main Menu → [3] Active Enumeration + → [1] BloodHound Collection + +# Results saved in: ad_enum_YYYYMMDD_HHMMSS/ +``` + +--- + +## Project layout + +``` +ghostline.sh Entry point (~50 lines). +install.sh Installs every supported tool. +lib/ +├── core.sh Colors (TTY-aware), palette, globals. +├── ui.sh ASCII art and menu rendering. +├── installer.sh Logging, prompting, install primitives. +└── modules/ + ├── config.sh Target / domain / credentials / output. + ├── passive.sh Unauthenticated reconnaissance. + ├── active.sh Authenticated enumeration. + └── special.sh Workflows, vuln scans, secrets dump. +docs/ +├── ARCHITECTURE.md Layout, boot sequence, helpers, CI. +└── ADDING_A_TOOL.md Recipe for plugging in a new tool. +.github/ +├── workflows/ci.yml shellcheck + bash -n + smoke test. +├── ISSUE_TEMPLATE/ Structured bug and tool-request forms. +└── PULL_REQUEST_TEMPLATE.md +``` + +See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for details and +[CONTRIBUTING.md](CONTRIBUTING.md) for the contribution workflow. + +--- + +## Output structure + +All results are saved in a timestamped directory: + +``` +ad_enum_20231220_143022/ +├── nmap_ad.nmap Nmap normal output +├── nmap_ad.xml Nmap XML (importable) +├── nmap_ad.gnmap Nmap greppable +├── enum4linux-ng.txt Full SMB enumeration +├── rpcclient.txt RPC enumeration results +├── ldap.txt LDAP query results +├── dnsrecon.txt DNS records +├── cme_shares.txt CrackMapExec shares +├── cme_users.txt CrackMapExec users +├── dns.csv AD-integrated DNS dump +├── asreproast.txt AS-REP roastable accounts +├── ridenum.txt RID enumeration +├── smb_vulns.nmap SMB vulnerability scan +├── secrets.txt Domain secrets (NTLM hashes) +└── *.json BloodHound data files +``` + +### Importing results + +**BloodHound:** + +```bash +neo4j console +# Then in BloodHound GUI: Upload Data → select the .json files +``` + +**Nmap XML:** + +```bash +xsltproc nmap_ad.xml -o report.html +nmap -iL nmap_ad.xml --resume +``` + +--- + +## Contributing + +Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for the +local setup, the conventions and the PR checklist. To plug in a new tool, +[docs/ADDING_A_TOOL.md](docs/ADDING_A_TOOL.md) walks through the recipe in +under a page. + +- Bug reports and tool requests use the templates in + [.github/ISSUE_TEMPLATE/](.github/ISSUE_TEMPLATE/). +- Security issues should be reported privately — see + [SECURITY.md](SECURITY.md). + +--- + +## Tools integrated + +- [Nmap](https://github.com/nmap/nmap) — by Gordon Lyon + Network discovery and security auditing tool. Used with NSE scripts for + SMB, LDAP, Kerberos and AD enumeration. +- [enum4linux-ng](https://github.com/cddmp/enum4linux-ng) — by cddmp + Modern SMB enumeration tool (users, groups, shares, policies). +- [ldapsearch (OpenLDAP)](https://git.openldap.org/openldap/openldap) + Native LDAP query utility for extracting domain objects and attributes. +- [rpcclient (Samba)](https://github.com/samba-team/samba) + RPC interaction tool for querying domain users, groups and SIDs via SMB. +- [CrackMapExec](https://github.com/Porchetta-Industries/CrackMapExec) — by byt3bl33d3r + Swiss army knife for Active Directory: SMB, LDAP, WinRM, MSSQL, and more. +- [Impacket](https://github.com/SecureAuthCorp/impacket) — by SecureAuth + Collection of Python scripts for low-level network protocol interaction. + Includes `GetUserSPNs.py` and `secretsdump.py`. +- [BloodHound](https://github.com/BloodHoundAD/BloodHound) — by SpecterOps + Graph-based Active Directory attack path analysis. Uses bloodhound-python + as the data ingestor. +- [bloodhound-python](https://github.com/fox-it/BloodHound.py) — data ingestor + CLI collector used by BloodHound. +- [adidnsdump](https://github.com/dirkjanm/adidnsdump) — by dirkjanm + Enumerates Active Directory–integrated DNS records via LDAP. +- [ridenum](https://github.com/trustedsec/ridenum) — by TrustedSec + RID cycling tool for enumerating domain users. +- [dnsrecon](https://github.com/darkoperator/dnsrecon) — by DarkOperator + DNS reconnaissance tool (alternative: dnsenum). +- [Kerbrute](https://github.com/ropnop/kerbrute) — by ropnop + Kerberos-based user enumeration and password spraying tool. +- [ldapdomaindump](https://github.com/dirkjanm/ldapdomaindump) — by dirkjanm + Dumps LDAP domain information into human-readable reports. + +--- + +## License + +Ghostline is released under the [MIT License](LICENSE). Use only against +systems you own or have explicit written permission to test. From ce3adfcff602466c98832b7f1e10c021f7d7c5a6 Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:53:38 +0200 Subject: [PATCH 11/12] docs: align repository URLs with canonical Ghostline casing --- .github/ISSUE_TEMPLATE/config.yml | 2 +- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 4 ++-- README.md | 6 +++--- SECURITY.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index ac4a7ce..4a1cd3b 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Security vulnerability - url: https://github.com/WhiteMuush/GhostLine/security/advisories/new + url: https://github.com/WhiteMuush/Ghostline/security/advisories/new about: Report a security issue privately via GitHub Security Advisories. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index fd60d9f..7921489 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -10,4 +10,4 @@ you agree to abide by its terms. To report a concern, contact the maintainer via the email address on their GitHub profile, or privately through -[GitHub Security Advisories](https://github.com/WhiteMuush/GhostLine/security/advisories/new). +[GitHub Security Advisories](https://github.com/WhiteMuush/Ghostline/security/advisories/new). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc6f88e..3bec656 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,8 +12,8 @@ and merge. ## Local setup ```bash -git clone https://github.com/WhiteMuush/GhostLine.git -cd GhostLine +git clone https://github.com/WhiteMuush/Ghostline.git +cd Ghostline sudo ./install.sh # installs every supported tool system-wide ./ghostline.sh # launch the interactive menu ``` diff --git a/README.md b/README.md index 0819380..bb898cc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

License: MIT - CI + CI PRs welcome Shellcheck

@@ -82,8 +82,8 @@ git clone https://github.com/trustedsec/ridenum.git /opt/ridenum ### Installing Ghostline ```bash -git clone https://github.com/WhiteMuush/GhostLine.git -cd GhostLine +git clone https://github.com/WhiteMuush/Ghostline.git +cd Ghostline chmod +x ghostline.sh ./ghostline.sh ``` diff --git a/SECURITY.md b/SECURITY.md index a7c3f71..78aa935 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ Ghostline wraps (`nmap`, `enum4linux-ng`, `bloodhound-python`, **Please do not open a public issue.** Use one of the private channels: -1. [GitHub Security Advisories](https://github.com/WhiteMuush/GhostLine/security/advisories/new) +1. [GitHub Security Advisories](https://github.com/WhiteMuush/Ghostline/security/advisories/new) — preferred, lets us collaborate on a fix. 2. Direct contact via the email address on the maintainer's GitHub profile. From 9e5f3b2be6510dc0ec3ff91c1026c07fcf778630 Mon Sep 17 00:00:00 2001 From: Melvin_Petiit Date: Wed, 27 May 2026 19:55:18 +0200 Subject: [PATCH 12/12] fix(install): use ${HOME} instead of unquoted tilde in summary fallbacks Resolves SC2088 warnings from shellcheck. The fallback paths now expand correctly at runtime instead of being displayed as literal strings. --- install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/install.sh b/install.sh index 120644d..7eb368a 100755 --- a/install.sh +++ b/install.sh @@ -203,14 +203,14 @@ print_summary() { printf " [2] ldapsearch -> %s\n" "$(command -v ldapsearch 2>/dev/null || echo 'install manually')" printf " [3] nmap + NSE -> %s\n" "$(command -v nmap 2>/dev/null || echo 'not found')" printf " [4] rpcclient -> %s\n" "$(command -v rpcclient 2>/dev/null || echo 'not found')" - printf " [5] CrackMapExec -> %s\n" "$(command -v crackmapexec 2>/dev/null || command -v cme 2>/dev/null || echo '~/.local/bin/')" - printf " [6] adidnsdump -> %s\n" "$(command -v adidnsdump 2>/dev/null || echo '~/.local/bin/')" - printf " [7] BloodHound.py -> %s\n" "$(command -v bloodhound-python 2>/dev/null || echo '~/.local/bin/')" + printf " [5] CrackMapExec -> %s\n" "$(command -v crackmapexec 2>/dev/null || command -v cme 2>/dev/null || echo "${HOME}/.local/bin/")" + printf " [6] adidnsdump -> %s\n" "$(command -v adidnsdump 2>/dev/null || echo "${HOME}/.local/bin/")" + printf " [7] BloodHound.py -> %s\n" "$(command -v bloodhound-python 2>/dev/null || echo "${HOME}/.local/bin/")" printf " [8] ridenum -> %s\n" "/usr/local/bin/ridenum" - printf " [9] Impacket -> %s\n" "$(command -v secretsdump.py 2>/dev/null || command -v impacket-secretsdump 2>/dev/null || echo '~/.local/bin/')" + printf " [9] Impacket -> %s\n" "$(command -v secretsdump.py 2>/dev/null || command -v impacket-secretsdump 2>/dev/null || echo "${HOME}/.local/bin/")" printf " [10] dnsrecon/dnsenum -> %s\n" "$(command -v dnsrecon 2>/dev/null || echo 'not found')" printf " [11] Kerbrute -> %s\n" "/usr/local/bin/kerbrute" - printf " [12] ldapdomaindump -> %s\n" "$(command -v ldapdomaindump 2>/dev/null || echo '~/.local/bin/')" + printf " [12] ldapdomaindump -> %s\n" "$(command -v ldapdomaindump 2>/dev/null || echo "${HOME}/.local/bin/")" echo " [13] GetUserSPNs.py -> (included in Impacket)" echo "" echo "IMPORTANT:"