This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Engagement Setup

Scope, rules of engagement, a tracked workspace, and a working toolbox — everything before the first packet leaves your box.

    The work you do before scanning is what keeps the engagement clean, repeatable, and defensible. Skip it and you end up scanning out-of-scope hosts, losing evidence, or unable to reconstruct what you ran on day 9.

    1. Pin down scope and rules of engagement

    Get these in writing before anything else. They decide which commands you’re allowed to run.

    • In-scope assets — root domains, IP ranges/CIDRs, ASNs, specific URLs.
    • Out-of-scope assets — explicit exclusions (shared SaaS, third-party CDNs, partner domains). These become your blacklist (see below).
    • Allowed activity — passive only? Active scanning? Exploitation? Brute force? Phishing?
    • Rate / timing limits — some clients cap requests-per-second or forbid scanning during business hours.
    • Blackout windows & emergency contact — who to call when something falls over, and when not to test.
    • Credentials & test accounts — for authenticated testing.

    A wildcard like *.example.com means “enumerate and prove the subdomains”; a bare example.com usually means just that host. Confirm which one the client means — it changes the size of the engagement.

    2. Stand up a tracked workspace

    I treat the engagement as a git repo from the first minute. Every scan output, every scope change, and every note ends up version-controlled — it’s both the audit trail and the backup.

    mkdir ~/engagements/acme && cd ~/engagements/acme
    git init
    mkdir -p recon hosts report tmp
    printf '/tmp\n' > .gitignore
    git add .gitignore && git commit -m "init workspace"
    

    A directory convention that scales (this is basically the layout Arsenic enforces):

    acme/
    ├── scope-domains.txt        # in-scope root domains, one per line
    ├── scope-ips.txt            # in-scope IPs / CIDRs
    ├── recon/                   # org-wide recon output (domains, ips, discovery)
    ├── hosts/<host>/recon/      # per-host scan output
    ├── report/
    │   ├── findings/            # one folder per finding
    │   └── static/              # screenshots & evidence
    └── tmp/                     # scratch (git-ignored)
    

    Commit early and often. A habit I borrowed straight from the Arsenic scripts: after each meaningful scan, git add the new output and commit with a message describing what ran. If you’re collaborating, push between steps so teammates don’t re-scan the same hosts.

    3. Define the scope files

    The whole pipeline is driven by two seed files. Everything you discover later gets validated back against these plus a blacklist.

    # Seed roots — the things you were explicitly told are in scope
    printf 'example.com\nexample.net\n' >> scope-domains.txt
    printf '203.0.113.0/24\n198.51.100.10\n' >> scope-ips.txt
    

    Keep a blacklist of root domains that show up in results but aren’t yours to test — shared infrastructure that certificate transparency and reverse DNS will constantly surface. Arsenic ships a sensible default; the usual offenders:

    1e100.net            akamaitechnologies.com   amazonaws.com
    azurewebsites.net    cloudfront.net           cloudapp.net
    googleusercontent.com  readthedocs.io         sites.hubspot.net
    

    Every time you generate a new candidate list of domains/IPs, run it through this blacklist before adding it to scope. This one habit prevents the most common engagement mistake: scanning someone else’s CDN.

    Ingesting scope from a CSV / bug-bounty program

    Real scope rarely arrives as a clean list. For a HackerOne-style CSV, I normalize it with mlr (Miller) and jq:

    curl -s https://hackerone.com/teams/acme/assets/download_csv.csv \
      | mlr --icsv --ojson cat | jq | tee acme-scope.json
    
    # Pull eligible, non-wildcard identifiers into the domain scope
    jq -r '.[]
      | select(.eligible_for_submission == "true")
      | select(.max_severity != "none")
      | .identifier' acme-scope.json \
      | grep -v '\*' \
      | sort -u >> scope-domains.txt
    

    Handle wildcard entries (*.acme.com) separately — strip the *. and feed the parent to subdomain enumeration in the Discovery phase.

    4. Build your toolbox

    Install the toolchain once and keep it on $PATH. Full install commands are in the Toolbox Reference; the essentials:

    # ProjectDiscovery suite (Go)
    go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
    go install github.com/projectdiscovery/dnsx/cmd/dnsx@latest
    go install github.com/projectdiscovery/naabu/v2/cmd/naabu@latest
    go install github.com/projectdiscovery/httpx/cmd/httpx@latest
    go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
    
    # Content discovery & fuzzing
    go install github.com/ffuf/ffuf/v2@latest
    # feroxbuster, gobuster — package manager or release binaries
    
    # Classics
    sudo apt install -y nmap amass exploitdb jq miller   # searchsploit ships with exploitdb
    
    # Wordlists
    git clone https://github.com/danielmiessler/SecLists /opt/SecLists
    

    Let nmap run unprivileged

    Most useful nmap scans need raw sockets. Rather than sudo on every run, grant the binary the capabilities once:

    sudo setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip "$(command -v nmap)"
    

    With setup done, move on to Discovery to turn your seed scope into a full asset inventory.