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/.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..4a1cd3b --- /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" 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/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7921489 --- /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..3bec656 --- /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/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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bb898cc --- /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. diff --git a/Readme.md b/Readme.md deleted file mode 100644 index 6ae91ec..0000000 --- a/Readme.md +++ /dev/null @@ -1,232 +0,0 @@ -![Gif](https://github.com/user-attachments/assets/d71682ce-9cd3-4151-9f36-0a7b29d5985f) - -**GhostLine** is an interactive bash toolkit that automates Active Directory enumeration by integrating 10+ professional security tools into a beautiful, easy-to-use interface. Inspired by the aesthetics of "Feed Your Spider", it provides both passive and active reconnaissance capabilities. - -## Features - -### Configuration Management -- Persistent target configuration (IP/Hostname, Domain, Credentials) -- Custom output directory naming -- Configuration displayed in all menus - -### 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 - ---- - -Capture d’écran 2026-01-05 204247 - - -## Installation - -### Prerequisites - -GhostLine requires the following tools to be installed: - -```bash -# Install all tools at once with the installation script -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 - -# Install Python tools -pipx install crackmapexec -pipx install bloodhound -pipx install impacket - -# Install from GitHub -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 -# Clone the repository -git clone https://github.com/WhiteMuush/GhostLine.git -cd GhostLine - -# Make executable -chmod +x ghostline.sh - -# Run -./ghostline.sh -``` - ---- - -## Quick Start - -### Basic Usage - -```bash -# Launch GhostLine -./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/ -``` - ---- - -## 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 -# Import JSON files into BloodHound -neo4j console -# Then in BloodHound GUI: Upload Data → Select .json files -``` - -**Nmap XML:** -```bash -# Open in various tools -xsltproc nmap_ad.xml -o report.html -nmap -iL nmap_ad.xml --resume -``` - ---- -## Contributing - -Contributions are welcome! Here's how you can help: - -### Reporting Bugs -Open an issue with: -- GhostLine version -- Operating system -- Steps to reproduce -- Expected vs actual behavior - -### Suggesting Features -Open an issue with: -- Feature description -- Use case -- Expected benefits - ---- - -### 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 Corporation - Collection of Python scripts for low-level network protocol interaction. Includes tools such as 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. - ---- - -Same script in powershell coming soon ! diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..78aa935 --- /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. 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. diff --git a/ghostline.sh b/ghostline.sh old mode 100644 new mode 100755 index c6ae106..a6fabea --- a/ghostline.sh +++ b/ghostline.sh @@ -1,681 +1,53 @@ -#!/bin/bash +#!/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" -################################################################################ -# GhostLine - Active Directory Enumeration Toolkit -# Interactive toolkit for Active Directory enumeration -# Author: Melvin PETIT -################################################################################ - -# ============================================================================ -# COLOR DEFINITIONS -# ============================================================================ -readonly RESET="$(tput sgr0)" -readonly BOLD="$(tput bold)" -readonly DIM="$(tput dim)" - -readonly RED="$(tput setaf 1)" -readonly GREEN="$(tput setaf 2)" -readonly MAGENTA="$(tput setaf 5)" - -readonly BRIGHT_RED="$(tput setaf 9)" -readonly BRIGHT_GREEN="$(tput setaf 10)" -readonly BRIGHT_MAGENTA="$(tput setaf 13)" - -# ============================================================================ -# ASCII ART BANNER -# ============================================================================ -readonly ASCII_ART=$(cat <<'ASCII' -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⢖⣠⣄⡀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣾⣿⣿⠎⠀⠀⠹⡀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣾⣿⣿⣿⣿⢿⣤⠴⠒⢦⡇⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⣿⣿⣿⣿⠿⠛⠁⠸⡇⠀⠀⠀⡇⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣿⣿⣿⣿⠿⠋⠁⠀⠀⠀⠀⣧⠀⠀⠀⡇⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣶⠂⠾⠿⣿⣿⡿⠛⠁⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⢠⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣷⣤⡀⠈⠉⠑⠒⠤⢀⡀⠀⠀⠀⠀⣿⠀⠀⠀⣼⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠟⠿⠿⢿⣿⣿⣆⠀⠀⠀⠀⠀⠈⠑⢄⠀⠀⣿⠀⠀⠀⡏⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⡏⡄⠀⠀⠀⠈⠻⣿⣧⠀⠀⠀⠀⠀⠀⠀⢳⠀⣿⠀⠀⠀⡇⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⡀⣷⠄⠀⠀⠀⠀⠙⢿⣇⡀⠀⠀⠀⠀⠀⠀⣷⡇⠀⠀⢸⡇⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⡿⠐⣁⣀⣀⢀⣾⣤⢤⣶⣿⣿⣦⡀⠀⠀⠀⠀⢸⠇⠀⠀⡾⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⣇⣾⣿⣿⣿⡇⠀⠻⣽⣿⣿⣿⡿⠿⣦⠀⠀⠀⡟⠀⠀⢰⠇⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⡟⣌⡻⠛⠁⢰⠸⡔⣤⣉⡩⠐⠁⠀⢸⡇⠀⢰⠃⠀⠀⡎⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣇⠀⠉⠀⠀⠀⠀⠀⠈⠙⠻⣶⢶⣶⣾⠇⢀⡏⠀⠀⠰⢣⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⢋⣿⣿⣿⣿⣿⣿⣿⣷⠀⠤⠒⠒⠓⠘⠀⢴⢏⡟⠈⣿⠀⡞⠀⠀⢀⡇⠀⠣⡀⠀ -⠀⠀⠀⠀⠀⠀⣼⣿⡿⠁⣸⣿⣿⣿⣿⣿⣿⣿⣿⠀⡰⠞⠛⠛⠛⠳⠶⠿⠁⣰⣿⣼⠃⠀⠀⡼⢿⣶⡀⡇⠀ -⠀⠀⠀⠀⠀⣼⣿⠟⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⣿⠟⠋⠉⠉⠁⠀⠀⢠⣿⣿⠃⠀⠀⠰⠁⠘⢿⠔⢀⣠ -⠀⠀⠀⠀⣰⣿⡟⠀⠀⠀⠸⣿⣿⣿⣿⣿⣿⣿⣿⣏⠻⠖⠂⠈⠒⠂⢀⣠⣿⣿⠏⠀⠀⢀⣧⣶⡶⢖⣿⠇⡿ -⠀⠀⠀⢠⣿⡟⠀⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣿⣏⠩⠭⣼⣿⣿⠏⠀⠀⠀⡼⠛⢉⣴⣿⠏⣸⡇ -⠀⠀⢀⣾⡟⠀⠀⠀⠀⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣿⣿⣿⠏⠀⠀⠀⡼⢁⣴⣿⡿⠁⣰⣿⠁ -⠀⠀⣸⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠿⠿⠿⠛⢛⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⣰⣿⣿⡿⠋⠀⣰⣿⡟⢠ -⠀⠀⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⢠⣿⡿⠛⠁⠀⣼⣿⣿⣧⣿ -⠀⢸⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⡿⠟⢻⠐⠉⠑⠤⣀⢠⣿⣿⠀⠀⢀⣾⣿⣿⣿⣿⣿ -⠀⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⢱⠹⠉⢉⠉⠓⠶⠤⠍⠛⢻⡄⣠⣿⣿⣿⣿⣿⣿⣿ -⢸⠀⠀⠀⠀⠀⠀⢀⣤⣴⡖⠤⣀⠀⠀⠀⣼⣿⣿⣿⠉⠉⠑⠛⠛⠛⠳⢄⣀⠈⢹⠤⣿⣿⣿⣿⣿⣿⣿⣿⣿ -ASCII -) - -# ============================================================================ -# CONFIGURATION -# ============================================================================ -TARGET="" -DOMAIN="" -USERNAME="" -PASSWORD="" -OUTPUT_DIR="ad_enum_$(date +%Y%m%d_%H%M%S)" - -# ============================================================================ -# MAIN 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}${TARGET:-Not set}${RESET}" - "${BOLD}${BRIGHT_RED}█${RESET} Domain: ${BRIGHT_MAGENTA}${DOMAIN:-Not set}${RESET}" - "${BOLD}${BRIGHT_RED}█${RESET} User: ${BRIGHT_MAGENTA}${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[@]}" -} - -# ============================================================================ -# CONFIGURATION SUBMENU -# ============================================================================ -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}${TARGET:-Not set}${RESET}" - "${BOLD}${BRIGHT_RED}█${RESET} Domain: ${BRIGHT_MAGENTA}${DOMAIN:-Not set}${RESET}" - "${BOLD}${BRIGHT_RED}█${RESET} User: ${BRIGHT_MAGENTA}${USERNAME:-Not set}${RESET}" - "${BOLD}${BRIGHT_RED}█${RESET} Output: ${BRIGHT_MAGENTA}${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[@]}" -} - -# ============================================================================ -# PASSIVE ENUMERATION SUBMENU -# ============================================================================ -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}${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[@]}" -} - -# ============================================================================ -# ACTIVE ENUMERATION SUBMENU -# ============================================================================ -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}${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[@]}" -} - -# ============================================================================ -# SPECIAL ACTIONS SUBMENU -# ============================================================================ -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[@]}" -} - -# ============================================================================ -# TITLE SCREEN -# ============================================================================ -readonly 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}" -) - -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=( "${INFO_PANEL[@]}" ) - local h=${#lines[@]} - - strip_esc() { - sed -E 's/\x1B\[[0-9;?]*[ -/]*[@-~]//g; s/\x1B\][^\a]*\a//g' - } - - local max_w=0 raw visible_len line - for line in "${lines[@]}"; do - raw=$(printf "%s" "$line" | strip_esc) - 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" - for ((i=0; i menu_count ? ascii_count : menu_count)) - - local max_ascii_width=0 - for line in "${ascii_lines[@]}"; do - ((${#line} > max_ascii_width)) && max_ascii_width=${#line} - done - - local spacing=" " - - for ((i=0; i/dev/null - - echo -e "${BRIGHT_MAGENTA}► Phase 2: SMB Enumeration${RESET}" - enum4linux-ng -A "$TARGET" > "$OUTPUT_DIR/enum4linux.txt" 2>/dev/null - - echo -e "${BRIGHT_MAGENTA}► Phase 3: RPC Enumeration${RESET}" - { echo "enumdomusers"; echo "exit"; } | rpcclient -U "" "$TARGET" -N > "$OUTPUT_DIR/rpc.txt" 2>/dev/null - - [ -n "$DOMAIN" ] && { - echo -e "${BRIGHT_MAGENTA}► Phase 4: LDAP Query${RESET}" - BASE_DN=$(echo "$DOMAIN" | sed 's/\./,dc=/g' | sed 's/^/dc=/') - ldapsearch -x -H ldap://"$TARGET" -b "$BASE_DN" "(objectclass=user)" > "$OUTPUT_DIR/ldap.txt" 2>/dev/null - } - - echo "" - echo -e "${BRIGHT_GREEN}✓ Workflow complete! All results in ${OUTPUT_DIR}/${RESET}" - read -r - ;; - 2) - echo -e "\n${BRIGHT_MAGENTA}SMB Vulnerabilities${RESET}" - check_target - mkdir -p "$OUTPUT_DIR" - echo -e "${BRIGHT_MAGENTA}Scanning for SMB vulnerabilities...${RESET}" - nmap -p 445 --script smb-vuln* "$TARGET" -oA "$OUTPUT_DIR/smb_vulns" - echo -e "${BRIGHT_GREEN}✓ Vulnerability scan complete${RESET}" - read -r - ;; - 3) - echo -e "\n${BRIGHT_MAGENTA}Secrets Dump${RESET}" - check_target - check_domain - check_credentials - mkdir -p "$OUTPUT_DIR" - SECRETSDUMP=$(command -v secretsdump.py || command -v impacket-secretsdump) - echo -e "${BRIGHT_RED}${BOLD}⚠ This requires elevated privileges!${RESET}" - echo -e "${BRIGHT_MAGENTA}Dumping secrets...${RESET}" - $SECRETSDUMP "$DOMAIN/$USERNAME:$PASSWORD@$TARGET" | tee "$OUTPUT_DIR/secrets.txt" - echo -e "${BRIGHT_GREEN}✓ Secrets saved${RESET}" - read -r - ;; - 4) - echo -e "\n${BRIGHT_MAGENTA}Results Viewer${RESET}" - if [ ! -d "$OUTPUT_DIR" ]; then - echo -e "${BRIGHT_RED}No results directory found${RESET}" - else - echo -e "${BRIGHT_MAGENTA}Files in $OUTPUT_DIR:${RESET}" - ls -lh "$OUTPUT_DIR" - fi - read -r - ;; - 0) return ;; - *) - echo -e "\n${BRIGHT_RED}Invalid choice!${RESET}" - sleep 1 - ;; - esac - done -} - -# ============================================================================ -# MAIN MENU HANDLER -# ============================================================================ -handle_main_menu() { - local choice=$1 - - case $choice in - 1) handle_config_menu ;; - 2) handle_passive_menu ;; - 3) handle_active_menu ;; - 4) handle_special_menu ;; - 0) - echo -e "\n${BRIGHT_MAGENTA}Exiting GhostLine...${RESET}" - exit 0 - ;; - *) - echo -e "\n${BRIGHT_RED}Invalid choice!${RESET}" - sleep 1 - ;; - esac -} - -# ============================================================================ -# MAIN LOOP -# ============================================================================ -main_loop() { - 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 - - handle_main_menu "$choice" - done -} - -main_loop \ No newline at end of file +main_loop "$@" diff --git a/install.sh b/install.sh old mode 100644 new mode 100755 index 1689859..7eb368a --- a/install.sh +++ b/install.sh @@ -1,313 +1,238 @@ -#!/bin/bash - -################################################################################ -# Script d'installation des outils d'énumération Active Directory -# Version simplifiée - Installation uniquement -################################################################################ - -set -e - -# Couleurs -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -print_status() { echo -e "${BLUE}[*]${NC} $1"; } -print_success() { echo -e "${GREEN}[+]${NC} $1"; } -print_error() { echo -e "${RED}[-]${NC} $1"; } -print_warning() { echo -e "${YELLOW}[!]${NC} $1"; } - -################################################################################ -# VÉRIFICATION ROOT -################################################################################ - -if [[ $EUID -ne 0 ]]; then - print_error "Ce script doit être exécuté en tant que root (sudo)" - exit 1 -fi - -################################################################################ -# CORRECTION DES DÉPÔTS PROBLÉMATIQUES -################################################################################ - -print_status "Correction des dépôts problématiques..." - -# Créer le dossier de sauvegarde -mkdir -p /etc/apt/sources.list.d/disabled 2>/dev/null - -# Désactiver WineHQ -if ls /etc/apt/sources.list.d/*winehq* 1> /dev/null 2>&1; then - mv /etc/apt/sources.list.d/*winehq* /etc/apt/sources.list.d/disabled/ 2>/dev/null || true -fi - -# Désactiver Docker avec version incorrecte -if grep -r "zara" /etc/apt/sources.list.d/ 2>/dev/null; then - grep -rl "zara" /etc/apt/sources.list.d/ | while read file; do - mv "$file" /etc/apt/sources.list.d/disabled/ 2>/dev/null || true - done -fi - -print_success "Dépôts corrigés" - -################################################################################ -# MISE À JOUR DU SYSTÈME -################################################################################ - -print_status "Mise à jour du système..." -apt update -y 2>&1 | grep -v "NO_PUBKEY\|pas signé" || true -print_success "Système mis à jour" - -################################################################################ -# INSTALLATION DES DÉPENDANCES DE BASE -################################################################################ - -print_status "Installation des dépendances de base..." - -# Recherche du paquet LDAP disponible -LDAP_PKG=$(apt-cache search "ldap" 2>/dev/null | grep -E "ldap.*utils|openldap.*client" | head -1 | awk '{print $1}') -[ -z "$LDAP_PKG" ] && LDAP_PKG="ldap-utils" - -apt install -y \ - 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 - -print_success "Dépendances de base installées" - -# Configuration pipx -export PATH="$PATH:/root/.local/bin:$HOME/.local/bin" -pipx ensurepath 2>/dev/null || true - -################################################################################ -# FONCTION D'INSTALLATION PYTHON -################################################################################ - -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 -} - -################################################################################ -# 1. ENUM4LINUX-NG -################################################################################ - -print_status "Installation de enum4linux-ng..." - -cd /opt -if [ -d "enum4linux-ng" ]; then - cd enum4linux-ng && git pull -else - git clone https://github.com/cddmp/enum4linux-ng.git - cd enum4linux-ng -fi - -if [ -f "requirements.txt" ]; then - while IFS= read -r pkg; do - [ -z "$pkg" ] || [[ "$pkg" =~ ^# ]] && continue - pip_install "$pkg" - done < requirements.txt -fi - -chmod +x enum4linux-ng.py -ln -sf /opt/enum4linux-ng/enum4linux-ng.py /usr/local/bin/enum4linux-ng - -print_success "enum4linux-ng installé" - -################################################################################ -# 2. CRACKMAPEXEC -################################################################################ - -print_status "Installation de CrackMapExec..." - -if apt install -y crackmapexec 2>/dev/null; then - print_success "CrackMapExec installé via apt" -else - pipx install crackmapexec --force 2>&1 | grep -v "WARNING" || true - print_success "CrackMapExec installé via pipx" -fi - -################################################################################ -# 3. ADIDNSDUMP -################################################################################ - -print_status "Installation de adidnsdump..." +#!/usr/bin/env bash +# install.sh — Install every dependency required by Ghostline. +# Designed for Debian/Ubuntu/Kali. Run with sudo. -cd /opt -if [ -d "adidnsdump" ]; then - cd adidnsdump && git pull -else - git clone https://github.com/dirkjanm/adidnsdump.git - cd adidnsdump -fi +set -euo pipefail -if [ -f "requirements.txt" ]; then - while IFS= read -r pkg; do - [ -z "$pkg" ] || [[ "$pkg" =~ ^# ]] && continue - pip_install "$pkg" - done < requirements.txt -fi +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_DIR -pip_install "." +# shellcheck source=lib/core.sh +source "${SCRIPT_DIR}/lib/core.sh" +# shellcheck source=lib/installer.sh +source "${SCRIPT_DIR}/lib/installer.sh" -print_success "adidnsdump installé" +require_root -################################################################################ -# 4. BLOODHOUND.PY -################################################################################ +# --------------------------------------------------------------------------- +# Disable known broken apt repositories. +# --------------------------------------------------------------------------- +disable_broken_repos() { + log_step "Cleaning up broken apt repositories..." + mkdir -p /etc/apt/sources.list.d/disabled -print_status "Installation de BloodHound.py..." - -pipx install bloodhound --force 2>&1 | grep -v "WARNING" || pip_install "bloodhound" - -print_success "BloodHound.py installé" - -################################################################################ -# 5. RIDENUM -################################################################################ - -print_status "Installation de ridenum..." - -cd /opt -if [ -d "ridenum" ]; then - cd ridenum && git pull -else - git clone https://github.com/trustedsec/ridenum.git -fi + 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 -chmod +x /opt/ridenum/ridenum.py -ln -sf /opt/ridenum/ridenum.py /usr/local/bin/ridenum + 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 -print_success "ridenum installé" + log_success "Repositories cleaned" +} -################################################################################ -# 6. IMPACKET -################################################################################ +# --------------------------------------------------------------------------- +# 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" +} -print_status "Installation de Impacket..." +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 +} -pipx install impacket --force 2>&1 | grep -v "WARNING" || pip_install "impacket" +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" +} -print_success "Impacket installé" +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 +} -################################################################################ -# 7. KERBRUTE -################################################################################ +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" +} -print_status "Installation de Kerbrute..." - -# Détecter l'architecture -ARCH=$(uname -m) -case "$ARCH" in - x86_64) - KERBRUTE_ARCH="amd64" - ;; - aarch64|arm64) - KERBRUTE_ARCH="arm64" - ;; - armv7l) - KERBRUTE_ARCH="arm" - ;; - *) - print_warning "Architecture non supportée pour Kerbrute: $ARCH" - KERBRUTE_ARCH="amd64" - ;; -esac +install_bloodhound_py() { + log_step "Installing BloodHound.py..." + pipx_install bloodhound || pip_install bloodhound + log_success "BloodHound.py installed" +} -# Télécharger la dernière version -KERBRUTE_VERSION="1.0.3" -KERBRUTE_URL="https://github.com/ropnop/kerbrute/releases/download/v${KERBRUTE_VERSION}/kerbrute_linux_${KERBRUTE_ARCH}" +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" +} -cd /opt -wget -q "$KERBRUTE_URL" -O kerbrute 2>/dev/null || curl -sL "$KERBRUTE_URL" -o kerbrute - -if [ -f "kerbrute" ]; then - chmod +x kerbrute - ln -sf /opt/kerbrute /usr/local/bin/kerbrute - print_success "Kerbrute installé" -else - print_warning "Échec du téléchargement de Kerbrute" -fi +install_impacket() { + log_step "Installing Impacket..." + pipx_install impacket || pip_install impacket + log_success "Impacket installed" +} -################################################################################ -# 8. LDAPDOMAINDUMP -################################################################################ +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 +} -print_status "Installation de ldapdomaindump..." +install_ldapdomaindump() { + log_step "Installing ldapdomaindump..." + pip_install ldapdomaindump + log_success "ldapdomaindump installed" +} -pip_install "ldapdomaindump" +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 -print_success "ldapdomaindump installé" + 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 -################################################################################ -# CONFIGURATION FINALE -################################################################################ + export PATH="${PATH}:${HOME}/.local/bin:/root/.local/bin" +} -print_status "Configuration du PATH..." +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 "${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 "${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 "${HOME}/.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 "========================================================================" +} -# Ajout au bashrc -if ! grep -q ".local/bin" /root/.bashrc 2>/dev/null; then - echo 'export PATH="$PATH:$HOME/.local/bin:/root/.local/bin"' >> /root/.bashrc -fi +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 +} -if [ -n "$SUDO_USER" ]; then - USER_HOME=$(eval echo ~$SUDO_USER) - 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" - -################################################################################ -# RÉSUMÉ -################################################################################ - -echo "" -echo "========================================================================" -echo -e "${GREEN}✓ Installation terminée avec succès!${NC}" -echo "========================================================================" -echo "" -echo "Outils installés:" -echo " [1] enum4linux-ng -> /usr/local/bin/enum4linux-ng" -echo " [2] ldapsearch -> $(which ldapsearch 2>/dev/null || echo 'À installer manuellement')" -echo " [3] nmap + NSE -> $(which nmap 2>/dev/null || echo 'Non trouvé')" -echo " [4] rpcclient -> $(which rpcclient 2>/dev/null || echo 'Non trouvé')" -echo " [5] CrackMapExec -> $(which crackmapexec 2>/dev/null || which cme 2>/dev/null || echo '~/.local/bin/')" -echo " [6] adidnsdump -> $(which adidnsdump 2>/dev/null || echo '~/.local/bin/')" -echo " [7] BloodHound.py -> $(which bloodhound-python 2>/dev/null || echo '~/.local/bin/')" -echo " [8] ridenum -> /usr/local/bin/ridenum" -echo " [9] Impacket -> $(which secretsdump.py 2>/dev/null || which impacket-secretsdump 2>/dev/null || echo '~/.local/bin/')" -echo " [10] dnsrecon/dnsenum -> $(which dnsrecon 2>/dev/null || echo 'Non trouvé')" -echo " [11] Kerbrute -> /usr/local/bin/kerbrute" -echo " [12] ldapdomaindump -> $(which ldapdomaindump 2>/dev/null || echo '~/.local/bin/')" -echo " [13] GetUserSPNs.py -> (Included in Impacket)" -echo "" -echo "IMPORTANT:" -echo " Rechargez votre shell: source ~/.bashrc" -echo " Ou redémarrez votre terminal" -echo "" -echo "========================================================================" \ No newline at end of file +main "$@" 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/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 +} 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