Skip to content
naturalstatePublic

About

A staged orchestration layer over feroxbuster, ffuf, and hashcat with project tracking and HTML reporting

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

92 Commits

Folders and files

Repository files navigation

Obliquity

Python License: MIT Status Tests feroxbuster ffuf hashcat

A staged orchestration layer over feroxbuster, ffuf, hashcat, and thc-hydra -- project-aware, resumable, and reported in one place instead of four.

Quickstart  ·  Features  ·  Usage  ·  Roadmap  ·  Contributing


Obliquity

Screenshot placeholder -- add docs/images/demo.png (a terminal run or the HTML report) and this shows it.


Table of contents


What is Obliquity?

Obliquity is a CLI for staged penetration-testing tool orchestration, built around four pillars, each wrapping one underlying tool:

Pillar Tool Job
bust feroxbuster Staged directory/file content discovery
fuzz ffuf Parameter, endpoint, and raw-request fuzzing
crack hashcat Staged offline password/hash cracking
brute thc-hydra Staged online login/credential attacks

It is not a replacement for any of those tools -- it's a thin, honest wrapper that adds the project management layer they don't try to be:

  • project + host/job tracking, with per-host metadata (--tech, --server, --profile)
  • JSON gameplans: an ordered sequence of stages instead of one flag-heavy command
  • completed-stage skipping -- rerunning a finished gameplan doesn't redo the work
  • stop-and-warn instead of silently repeating scans, with a suggested next step
  • raw output archiving + JSONL parsing into one shared SQLite database per project
  • unified reporting across all four pillars: HTML, JSON, CSV, and Markdown
  • a small downloadable wordlist catalog, so you're not manually git clone-ing SecLists

The pain it solves

A real engagement rarely means one content-discovery scan. For a single host you end up running feroxbuster over and over: common.txt, then big.txt, then raft-medium, then raft-large, then the same lists again with .bak/.old/.zip/.config extensions, then again recursively into each interesting directory you just found (/admin/, /backup/, /api/). That's easily 4-6 wordlist passes per host -- and then you multiply the whole thing by every host in scope, and again by every promising subdirectory.

Doing that by hand means babysitting a queue of near-identical commands, manually remembering which wordlist/extension/depth combination you've already thrown at which host and which path, and starting over from scratch whenever a scan gets interrupted or you close the laptop. It's slow, it's easy to lose your place in, and it's miserable to resume cleanly.

Obliquity turns that into a single declarative gameplan per host. It runs the passes in order, fingerprints each one so it never repeats a combination it's already completed, resumes where you left off, and logs every finding from every host into one project database and one report -- so the busywork disappears and you just read results.

Features

  • Project-based target management -- one project, many hosts, each with its own server/tech/profile metadata that Obliquity actually uses (see Host/Application Awareness below)
  • Resumable by design -- every stage is fingerprinted on its exact parameters (wordlist, extensions, recursion, hash type, attack mode, ...) and skipped once completed
  • Smart-ish rerun handling -- trying to rerun a fully completed gameplan stops, tells you what it already found and when, and offers a sensible escalation instead of either silently doing nothing or silently redoing everything
  • Gameplan escalation ladders -- generic-quick -> generic-standard -> generic-deep, quick-dictionary -> standard, offered automatically
  • Unified scan history across all four pillars in one readable log
  • Multi-format reporting -- HTML (with cracked-hash, crack-run, and found-credential sections), JSON, CSV, and Markdown, all from the same underlying data
  • Downloadable wordlist catalog -- no more manually cloning all of SecLists just to get common.txt and rockyou.txt
  • Dry-run mode everywhere, and Burp-friendly proxy/header passthrough

Requirements

  • Python 3.10+
  • feroxbuster, ffuf, hashcat, and hydra installed and available on PATH (obliquity doctor checks them for you; each pillar only needs its own tool, so a missing one just disables that pillar)
  • SecLists content for the default profiles -- or just let Obliquity download the handful of files it actually needs, see Wordlist setup

Installing Python 3.10+

If you don't already have it (python3 --version / py --version):

Platform Command
macOS (Homebrew) brew install python
Windows (winget) winget install Python.Python.3.12
Debian / Ubuntu / Kali sudo apt install python3 python3-pip python3-venv
Fedora / RHEL sudo dnf install python3 python3-pip
Arch sudo pacman -S python python-pip

pip and venv ship with Python on Windows/macOS; on Debian/Ubuntu they're the separate python3-pip / python3-venv packages shown above.

Installing feroxbuster, ffuf, hashcat, and hydra

Each pillar only needs its own tool; install just the ones you'll use.

macOS (Homebrew):

brew install feroxbuster ffuf hashcat hydra

Windows (winget):

winget install epi052.feroxbuster
winget install ffuf.ffuf
# hashcat has no winget package -- grab the portable build from hashcat.net,
# unzip it, and add the folder to your PATH:
curl.exe -L -o hashcat.7z https://hashcat.net/files/hashcat-6.2.6.7z
# (check https://hashcat.net/hashcat/ for the current version number)
# thc-hydra has no native Windows build; run it under WSL:
#   wsl sudo apt install hydra

Debian / Ubuntu / Kali (apt):

sudo apt install feroxbuster ffuf hashcat hydra

Fedora / RHEL (dnf): hashcat and hydra are packaged; feroxbuster and ffuf aren't, so install those from upstream:

sudo dnf install hashcat hydra
curl -sL https://raw.githubusercontent.com/epi052/feroxbuster/main/install-nix.sh | bash -s $HOME/.local/bin
go install github.com/ffuf/ffuf/v2@latest   # or grab a prebuilt binary from ffuf's releases page

Arch (pacman):

sudo pacman -S feroxbuster hashcat hydra
go install github.com/ffuf/ffuf/v2@latest   # ffuf: via Go, or the AUR (yay -S ffuf)

The manual: obliquity manual prints the full command reference in the terminal on any platform (pipe to a pager: obliquity manual | less -R). On Linux/macOS you can also install the man page for man obliquity:

man ./docs/obliquity.1                                  # view without installing
sudo install -Dm644 docs/obliquity.1 /usr/local/share/man/man1/obliquity.1   # install

Any platform, no package manager: feroxbuster/ffuf/hashcat ship prebuilt binaries on their release pages -- download, extract, and put them on your PATH: feroxbuster, ffuf, hashcat. thc-hydra is built from source (vanhauser-thc/thc-hydra) or run under WSL on Windows.

Installation

From inside this repo:

python3 -m pip install -e .

Or run it without installing at all:

python3 -m obliquity.cli --help

Using a virtual environment

You'll very likely need one. On recent Python installs -- Homebrew Python on macOS, and most current Linux distros -- pip install directly into the system Python refuses to run at all, with an externally-managed-environment error. Even where it's allowed, installing project dependencies system-wide is how unrelated projects end up breaking each other's package versions. A virtual environment (venv) is just a private, disposable copy of Python + pip scoped to this project only, so none of that happens.

python3 -m venv .venv
source .venv/bin/activate      # macOS/Linux
# .venv\Scripts\activate       # Windows (PowerShell: .venv\Scripts\Activate.ps1)
python3 -m pip install -e .

A venv only needs activating once per terminal session -- not before every single command. Once you source .venv/bin/activate, your prompt gets a (.venv) prefix and obliquity/python3/pip all resolve inside it until you close that terminal tab or run deactivate. Opening a new terminal tab starts a fresh shell that isn't activated, so you'll need to run the source line again there -- that's a property of terminals, not something wrong with the venv.

Making it persistent, if re-typing source .venv/bin/activate in every new terminal gets old, pick one:

Option A -- auto-activate with direnv (recommended if you work in this repo a lot)
brew install direnv          # or your package manager
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc   # or ~/.bashrc for bash
echo "source .venv/bin/activate" > .envrc
direnv allow

Now the venv activates automatically the moment you cd into this directory, and deactivates when you leave it. Nothing to remember.

Option B -- install with pipx (recommended if you just want the obliquity command, and don't care about venvs at all)
brew install pipx            # or: python3 -m pip install --user pipx
pipx install --editable .

pipx builds and manages its own isolated venv for you behind the scenes and puts obliquity straight on your PATH -- no activation step, ever, in any terminal.

Option C -- a shell alias (simplest, one line in your shell rc file)
echo "alias obliquity-env='source /path/to/obliquity/.venv/bin/activate'" >> ~/.zshrc

Then just type obliquity-env in any new terminal before using the tool.

Quickstart

# 1. Create a project (and list all projects any time with `obliquity project list`)
obliquity project create acme

# 2. Add a host, with whatever metadata you already know
obliquity host add acme https://app.acme.com --server iis --tech aspnet

# 3. Preview the plan -- no --gameplan needed, Obliquity recommends one
#    from the host's --tech/--server/--profile (aspnet-standard here)
obliquity bust plan acme

# 4. Run it
obliquity bust run acme --open-report

That's the whole loop: project, host, plan, run, report. Everything below is what's available once you need more control.

🧪 Want to try every feature safely? The repo ships a Test Lab -- a localhost-only web/FTP/SSH/SMB target set plus hash fixtures, with a full begin-to-end walkthrough for both the regular CLI and the interactive console.

Usage

Working across projects

List projects, and set an active project so you stop repeating its name on every command (like kubectl/docker contexts):

obliquity project create acme --root ~/engagements/acme   # keep ALL output (runs, reports, raw) in one folder
obliquity project list            # all projects + host/job/run counts; * marks active
obliquity project use acme        # make acme the active project (persists)
obliquity project current         # show the active project + its defaults (see below)
obliquity project unset           # clear it
obliquity project archive acme --yes   # back up files, drop the DB record (keeps a copy)
obliquity project delete acme --yes    # PERMANENTLY delete DB records + files (no copy)

project current shows the active project's root, created date, hosts (with their profile/tech/server metadata), crack jobs, and the default gameplan for each tool -- with (default) marking the ones still inheriting Obliquity's built-in default rather than a value you set.

Per-project default gameplans. Instead of passing --gameplan on every run, set a default per tool per project so it auto-applies:

obliquity project set-gameplan bust generic-deep     # this project's bust default
obliquity project set-gameplan crack rules-basic     # this project's crack default
obliquity project set-gameplan fuzz --clear          # revert fuzz to the built-in default

Resolution precedence for a run's gameplan is explicit --gameplan > project default > (bust only) host-metadata recommendation > built-in fallback. An explicit --gameplan on the command line always wins.

Once a project is active, its name is optional everywhere. Resolution precedence is explicit name > OBLIQUITY_PROJECT env var > active project, and a Using active project: acme line prints whenever the name wasn't given explicitly, so it's never a surprise which project a command hit:

obliquity project use acme
obliquity bust run https://app.acme.com --gameplan generic-quick   # acme implied
obliquity history                                                   # acme implied
obliquity bust run other https://x.test --gameplan generic-quick   # explicit wins

Interactive console

Prefer an msfconsole-style prompt over retyping obliquity each time? Drop into the REPL:

$ obliquity console
obliquity(acme) > use fuzz
obliquity(acme:fuzz) > set endpoint /search
fuzz endpoint => /search
obliquity(acme:fuzz) > options
obliquity(acme:fuzz) > run --dry-run
obliquity(acme:fuzz) > back
obliquity(acme) > project current
obliquity(acme) > exit

It's a dual-mode CLI: the one-shot obliquity <command> still works everywhere (and stays the scriptable path). In the console, every command works without the prefix; use <tool> selects a tool so bare run/plan/resume/ set/unset/options/job/creds target it, and back deselects. The prompt shows project:tool context. Tab-completion and command history are on (via readline); help lists everything; exit or Ctrl-D quits. A bad command just prints an error -- it never drops you out of the session.

Stateful options (Metasploit-style)

If you'd rather set things up once and just run -- like msfconsole's set/show options/run -- each tool has options you can store on the active project. This is additive: every flag below still works on run and overrides a stored option.

obliquity set fuzz host https://app.acme.test
obliquity set fuzz endpoint /search
obliquity set fuzz gameplan parameter-names-quick
obliquity options fuzz          # 'show options': OPTION / VALUE / REQUIRED / SOURCE
obliquity fuzz run              # uses the stored options -- no flags needed
obliquity unset fuzz endpoint   # revert one option

set host <url> is Metasploit-style: if that host isn't in the project yet it's added automatically (a bare hostname gets an https:// prefix). With several hosts and none chosen, a run just uses the first one (and says so); pick another with a URL arg or set host <url>. options bust shows the project's host even when you haven't set one.

Precedence is explicit flag > stored option > project default > built-in default, so obliquity fuzz run --endpoint /admin still wins for that run. Stored options also show up in obliquity project current. Settable options per tool: bust (host, gameplan, threads, rate, proxy), fuzz (host, gameplan, endpoint, template, request), crack/brute (job, gameplan). gameplan is the same value project set-gameplan manages.

bust: content discovery

# Dry-run first to see the exact feroxbuster command that will run
obliquity bust run acme https://app.acme.com --gameplan aspnet-standard --dry-run

# Run it for real
obliquity bust run acme https://app.acme.com --gameplan aspnet-standard

# Resume later -- completed stages are skipped automatically
obliquity bust resume acme https://app.acme.com --gameplan aspnet-standard

The host URL is only needed when a project has more than one host -- with exactly one, every bust/fuzz plan/run/resume command uses it automatically:

obliquity bust run acme --gameplan aspnet-standard

Recursion control: each gameplan stage already sets feroxbuster's native recursion (it re-scans directories it finds, with the same wordlist). Override it for every stage without editing the gameplan:

obliquity bust run acme --depth 3        # force recursion depth 3 on all stages
obliquity bust run acme --no-recurse     # disable recursion entirely

(--depth and --no-recurse are mutually exclusive. A re-scan at a different depth is tracked as a distinct run, so it won't be skipped as "already done".)

Response filters: cut noise by dropping responses that match a status code, byte size, word count, line count, or a body regex (feroxbuster's -C/-S/-W/-N/-X). Pass them on the CLI (applies to every stage) or set them per stage in a gameplan's JSON:

obliquity bust run acme --filter-status 404,500 --filter-size 0 --filter-regex 'Not Found'

A run with different filters is fingerprinted as a distinct run, so tightening filters doesn't collide with an earlier, noisier scan.

Flood detection (wildcard / soft-404): some hosts answer every path with 200 (a wildcard or soft-404), which floods a scan with junk. During a run Obliquity watches the live results, and if one status code dominates past a threshold it stops that stage mid-run and offers to re-run it with that status filtered out, then continue:

  ! Flood detected: status 200 returned 400/400 matches -- likely a wildcard / soft-404.
    Re-run this stage excluding status 200 and continue? [Y/n]

Tune or disable it with --flood-threshold N (default 250; the re-run is a distinct, fingerprinted run) or --no-flood-guard. In non-interactive runs it auto-re-runs filtered and says so.

Live progress: during a run each stage shows an animated bar driven by feroxbuster's real request counter (parsed from its output over a PTY) -- percent, requests done, findings, and errors, updating as it goes. Prefer feroxbuster's own full interactive UI? Add --ferox-ui:

obliquity bust run acme --ferox-ui

Each stage then hands the terminal to feroxbuster's native display -- pinned progress bars, scrolling results, clickable URLs, and its Scan Management Menu (press ENTER to add a URL/filter, cancel a sub-scan, or set a scan limit) -- with the startup banner suppressed. Obliquity still prints its own stage header before and one-line summary after each stage, so the orchestration stays visible. Off a real terminal (piped output, the REPL) it falls back to the one-line progress automatically.

Link extraction (discovery multiplier): feroxbuster parses every response body (HTML, JavaScript, robots.txt, ...) for URLs and folds the ones it finds back into the scan. This reaches paths that live in no wordlist -- an admin console linked only from a <script>, a legacy API referenced in a bundled .js, a report linked from the homepage. It is on by default; disable it per run when you want a pure wordlist scan (faster, quieter, fewer requests):

obliquity bust run acme --no-extract-links

Toggling it fingerprints as a distinct run. The test lab has a built-in demo: its homepage and /assets/app.js link to deliberately obscure paths (/reports/q4-internal-8kd.html, /dev-x9f2-console/, ...) that only turn up when extraction is on -- run a default scan against it, then --no-extract-links, and compare.

Extension Intelligence: appending extensions to a wordlist that already contains them (index.php + .php -> index.php.php) just wastes requests. Obliquity detects it and warns during bust plan/run. Inspect any wordlist directly -- entry count, size, whether it carries extensions, and a preview:

obliquity wordlists inspect seclists-common
obliquity wordlists inspect /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt

Gameplan stages can also use named extension tiers instead of copy-pasting lists: set a stage's "extensions" to "low", "medium", or "high" and Obliquity expands it (low = common web pages; medium adds config/backup/data; high adds archives, scripts, secrets, and more).

Host/Application Awareness: --gameplan itself is optional. From the host metadata you set with host add, Obliquity recommends a matching built-in gameplan instead of always defaulting to generic-quick, and always shows why:

Selected gameplan: aspnet-standard  (recommended: tech=aspnet)

The three metadata fields have distinct, non-overlapping roles (checked tech → profile → server, then the default):

Field Role Examples
--tech backend language / framework aspnet, php, java, node, python
--profile application type api, wordpress, admin-panel (default generic)
--server web server software iis, apache, nginx, tomcat

An explicit --gameplan always wins outright, with no label.

fuzz: parameter and API fuzzing

bust finds content; fuzz is for when the interesting value belongs inside a request -- a parameter name, a parameter value, or a field in a raw captured request.

# List every fuzz gameplan
obliquity gameplans list

# Dry-run first to see the exact ffuf command without sending anything
obliquity fuzz run acme https://app.acme.com --gameplan parameter-names-quick --endpoint /search --dry-run

# Discover accepted GET parameter names
obliquity fuzz run acme https://app.acme.com --gameplan parameter-names-quick --endpoint /search

# Fuzz values for a known parameter
obliquity fuzz run acme https://app.acme.com --gameplan parameter-values-quick --template '/search?q=FUZZ'

# Fuzz a raw request captured from Burp (FUZZ marker inside the file)
obliquity fuzz run acme https://api.acme.com --gameplan api-request-quick --request ./request.txt

# Coverage across tools -- what's already been tried against this host
obliquity coverage acme
obliquity coverage acme https://api.acme.com

crack: hash cracking

hashcat cracks password hashes gathered during an engagement, staged the same way bust/fuzz gameplans are: a plain dictionary first, then the same dictionary with rules, then a mask fallback -- each stage runs only if the previous one didn't recover everything.

# Add a hash file as a crack job (plays the role a host URL plays for bust/fuzz)
obliquity crack job add acme ./dumped-hashes.txt --hash-type 1000 --name ntlm-dump

# --hash-type is hashcat's -m mode number: 0=MD5, 1000=NTLM, 1800=sha512crypt, ...
# (see `hashcat --help` for the full list)

obliquity crack job list acme

# Preview, dry-run, execute -- job name optional with exactly one job, same as hosts
obliquity crack plan acme --gameplan quick-dictionary
obliquity crack run acme --gameplan quick-dictionary --dry-run
obliquity crack run acme --gameplan standard --open-report

# Resume later -- resumes hashcat mid-keyspace from its own checkpoint
obliquity crack resume acme --gameplan standard

Crackplan stages support all five common hashcat attack modes: dictionary (-a 0), mask (-a 3), combinator (-a 1, two wordlists glued together), and the two hybrids hybrid-wordlist-mask (-a 6, e.g. a real word + ?d?d?d?d) and hybrid-mask-wordlist (-a 7). See hybrid-standard for a policy-shaped example.

A hashcat exit code of 1 ("exhausted this wordlist/mask, hashes remain") is treated as a normal stage completion, not a failure -- that's what lets a crackplan fall through to its next stage automatically.

Unlike bust/fuzz, crack resume genuinely resumes an interrupted stage from hashcat's own .restore checkpoint (mid-keyspace), not just from the last completed stage.

brute: online login attacks

brute (backend: thc-hydra) is the one online pillar: unlike crack (offline hashing, no host), it makes live login attempts against a service -- web login forms, SSH, FTP, SMB, RDP, and more -- so it fits the project/host model directly. A login job is service + target (optionally linked to a project host); a loginplan stages credential lists the same way the others stage wordlists. (The pillar is named for the function, like bust/fuzz/crack; hydra is just today's backend, with medusa/ncrack as future options.)

# Add a login target as a job (the role a host URL plays for bust/fuzz)
obliquity brute job add acme 10.0.0.5 --service ssh --name ssh-box --port 22

# Web login forms need hydra's form spec (path : body-with-^USER^/^PASS^ : failure-text)
obliquity brute job add acme app.acme.test --service http-post-form --name web-login \
  --form-spec "/login:user=^USER^&pass=^PASS^:F=invalid"

obliquity brute job list acme

# Preview, dry-run, execute -- job name optional with exactly one job
obliquity brute plan acme --gameplan quick
obliquity brute run ssh-box --gameplan common-creds --dry-run
obliquity brute run ssh-box --gameplan common-creds

# Review what was recovered
obliquity brute creds acme

Service profiles: each service carries sensible defaults, so a job needs almost nothing. Adding a job auto-fills the standard port (ssh→22, ftp→21, smb→445, rdp→3389, mysql→3306, …) when you don't pass --port, and brute run picks a service-tuned loginplan when you don't pass --gameplan: ssh-default, ftp-default, and smb-default use service-appropriate username lists (root/admin vs anonymous/ftp vs administrator/guest) against a small common-password list; other services fall back to common-creds. Precedence stays explicit --gameplan > project default > service default > quick, and --port always overrides.

Authorization required. hydra performs live credential guessing. Only run it against systems you're explicitly authorized to test. Online guessing can trigger account lockouts, rate limits, and alerts -- keep -t (tasks) low and prefer small, targeted credential lists. Obliquity prints this reminder on every plan/run and defaults the built-in plans to conservative parallelism.

brute resume is stage-level (it skips already-completed stages), not mid-attack resume within a single hydra pass.

obliquity doctor

doctor checks feroxbuster, ffuf, hashcat, and hydra (core), plus gobuster/wfuzz/john (optional, for future alternate-tool support -- see Roadmap).

Wordlist explorer (TUI)

Not sure which wordlist a gameplan uses, or what's actually inside a list before you throw it at a target? The explorer is an interactive, scrollable browser over every built-in gameplan and every wordlist they reference (plus the download catalog):

obliquity explore

Scroll the left pane (↑/↓, wrapping around at the ends; Home/End); the right pane shows details for the highlighted item and scrolls with PgUp/PgDn; q quits. Tool tags ([bust]/[fuzz]/[crack]/[brute]) are color-coded and the detail pane uses the app's subtle blue/orange accents. For a wordlist it shows length, size, whether entries already carry extensions (and which), a head and tail sample, its description and typical use, the gameplan(s) that reference it, and similar wordlists. For a gameplan it shows its stages, the wordlist each uses, and the exact run command.

It picks a TUI backend for your OS automatically: curses (stdlib) on macOS/Linux, and Textual on Windows (pip install textual). Force one with obliquity explore --backend {auto,curses,textual,text}, or skip the TUI entirely with --no-tui (or OBLIQUITY_NO_TUI=1) for a plain printed listing. With no interactive terminal (or nothing installed) it prints the overview anyway. To inspect a single list non-interactively, use wordlists inspect.

Loading a gameplan from the explorer: press Enter on a gameplan to set it as your active project's default for that tool (the same as project set-gameplan), so your next bust/fuzz/crack/brute run uses it automatically. (Enter on a bare wordlist points you to the gameplan(s) that include it, since Obliquity runs gameplans rather than lone wordlists.)

Typing a pillar with no subcommand -- obliquity bust, fuzz, crack, brute -- lists that tool's built-in gameplans (a quick reminder of what you can run).

Wordlist setup

Obliquity doesn't need the full SecLists repo -- it downloads just the specific files its built-in gameplans reference, stored under ~/.obliquity/wordlists/. A gameplan referencing e.g. /usr/share/seclists/... automatically falls back to the downloaded copy if that system path doesn't exist, with no profile edits needed.

obliquity wordlists status                            # what's missing
obliquity wordlists setup                              # interactive, asks per wordlist
obliquity wordlists install seclists-common rockyou     # install specific ones
obliquity wordlists install --all-missing               # everything the built-ins need
obliquity wordlists list                                # full catalog + install status

obliquity project create checks for missing defaults and offers to download them right there when run interactively.

rockyou is the largest catalog entry (~50MB download, ~133MB extracted) and is only needed for crack gameplans beyond smoke-test. For anything outside this small curated catalog -- CMS-specific lists, Assetnote's technology-specific collections -- obliquity wordlists list prints links to the full SecLists repo and Assetnote's site; point a custom gameplan JSON at whatever you download, the same way --gameplan ./my-plan.json already works.

Reports

HTML is the default/richest format; JSON, CSV, and Markdown are also available for further processing or sharing:

obliquity report html acme
obliquity report open acme         # generate the HTML report AND open it in your default browser
obliquity report serve             # browse EVERY project's report in your browser (local server, pick from a list)
obliquity report json acme
obliquity report csv acme --kind cracked-hashes    # or: findings, runs, crack-runs, found-credentials, login-runs
obliquity report markdown acme

report open (and report html --open) open the report in your default browser on any OS -- it uses Python's webbrowser first and falls back to open (macOS) / start/os.startfile (Windows) / xdg-open (Linux) if that no-ops. You can also open one automatically right after a scan:

obliquity bust run acme --gameplan generic-quick --open-report

Resume & scan history

obliquity history acme                     # unified log across bust/fuzz/crack
obliquity history acme --tool hashcat
obliquity history acme --status failed --limit 20

One readable line per run: timestamp, tool, status, target, gameplan/stage, finding count, duration. (obliquity runs still exists as the lower-level bust/fuzz-only view.)

Burp proxy example

obliquity bust run acme https://app.acme.com \
  --gameplan generic-quick \
  --proxy http://127.0.0.1:8080 \
  --header 'Cookie: session=replace-me'

Built-in gameplans

Bust (feroxbuster)Fuzz (ffuf)Crack (hashcat)
  • generic-quick
  • generic-standard
  • generic-deep (raft-medium + recursive raft-large -- slow, run after generic-standard comes up empty)
  • aspnet-standard
  • php-standard
  • api-quick
  • smoke-test
  • parameter-names-quick
  • parameter-values-quick
  • api-request-quick
  • quick-dictionary (single rockyou pass)
  • standard (rockyou, +best64 rules, +policy mask)
  • rules-basic (rockyou -> +best64 -> +best64 stacked)
  • onerule-all (rockyou -> +OneRuleToRuleThemAll)
  • onerule-still (rockyou -> +OneRuleToRuleThemStill)
  • hybrid-standard (rockyou + digit/special masks -- "Summer2024" shapes)
  • smoke-test (bundled wordlist, no SecLists needed)
  • smoke-test-hybrid (bundled wordlist + mask, no SecLists needed)

The onerule-* plans need their rule files -- grab them (and the classic best64) with obliquity wordlists install onerule-all onerule-still hashcat-best64-rules, or obliquity wordlists install --all-missing.

Use a custom gameplan path instead of a built-in name, same syntax everywhere:

obliquity bust run acme https://app.acme.com --gameplan ./examples/custom-gameplan.json

How resume actually works

Each bust stage is fingerprinted on: target URL, gameplan name, stage name, wordlist, extensions, recursion setting, depth, status codes, extra args. Crack stages the same way, keyed on hash file + hash type instead of a URL, plus attack mode/wordlist/rules/mask. A stage that completed successfully before is skipped unless --force is used.

obliquity bust run acme https://app.acme.com --gameplan aspnet-standard --force
obliquity crack run acme --gameplan standard --force

bust resume/crack resume always skip completed stages silently -- that's their whole job. A plain bust run/crack run is different: run it interactively against a target whose gameplan already fully completed, and Obliquity stops before doing anything, tells you when it last finished and what it found, and offers the next step up a small escalation ladder (generic-quick -> generic-standard -> generic-deep, quick-dictionary -> standard) -- accept with y. Decline that (or there's no escalation defined yet) and it falls back to an explicit "rerun anyway?" confirmation. This only triggers in a real interactive terminal; scripts and --force/--dry-run runs are unaffected.

One nuance worth knowing: for bust this is stage-level resume, not mid-scan. feroxbuster runs with --no-state, so an interrupted stage restarts its wordlist from the beginning (findings already found aren't lost, but requests get resent); ffuf has no native checkpointing either. crack is the exception -- it resumes an interrupted stage mid-keyspace from hashcat's own .restore checkpoint.

Data location

~/.obliquity/

Override with:

export OBLIQUITY_HOME=/path/to/workdir
Textual UI demo (fake-data terminal UI playground -- doesn't run tools or touch the real database)
python -m pip install -r requirements-tui-demo.txt
python -m obliquity.tui_demo

# Minimal terminal-style experiment
python -m obliquity.tui_terminal_demo

# Posting-inspired keyboard UI experiment
python -m obliquity.tui_posting_demo

Arrow keys, Enter, Tab, Space, Escape, and letter shortcuts navigate workflow screens, settings, review pages, a live dashboard, tables, sparklines, and a simulated terminal stream -- all fake data, purely a UI exploration for a possible future dashboard.

Roadmap / future features

Obliquity intentionally keeps the current design simple and honest about what it doesn't do yet. Roughly in the order they'd get built:

  • Extension Intelligence -- avoid double-appending extensions to wordlist entries that already have one baked in
  • Host/Application Awareness composition -- combine tech + intensity + auth-required into a generated stage list, instead of picking one static built-in gameplan file
  • Recursive auto-follow-up discovery -- automatically queue deeper scans against interesting discovered paths (/admin, /backup, /api)
  • Project export/import -- move a project, with its full scan history, to a different machine
  • Wordlist compiler/deduper (obliquity wordlists build) -- generate deduplicated staged lists (quick, standard-delta, large-delta)
  • Alternate-tool backends -- gobuster as an alternative to feroxbuster, wfuzz as an alternative to ffuf, john as an alternative to hashcat, medusa/ncrack as alternatives to hydra under brute, selectable per run (defaulting to the current backends) -- deliberately deferred until the core pillars are solid
  • Burp Suite integration -- a Burp extension bridging to a local Obliquity API, so findings and requests can move in both directions

The full prioritized breakdown, including what's already built vs. what's genuinely just an idea, lives in CHANGES.md.

Contributing

This is an actively developed MVP, not yet accepting external contributions in a structured way -- issues and ideas are still welcome. pytest (92 tests as of this writing) covers the core adapters, resume logic, and CLI wiring; run it with:

python3 -m pip install -e . pytest
pytest

License

MIT

About

A staged orchestration layer over feroxbuster, ffuf, and hashcat with project tracking and HTML reporting

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages