#1140·amass

v5: `-alts` generates no names, because the alteration technique flags are `json:"-"` and never reach the engine

Author: t0kubetsuCreated Aug 26, 2026Updated Aug 26, 2026

amass enum -alts -brute generates zero altered names in v5.1.1. The run exits 0, with no output and no error.

The five flags that control alteration techniques are tagged json:"-". The CLI drives the engine over HTTP. The flags therefore never reach the engine, and every technique stays disabled.

This is the same class of silent gap as #1122, on the alteration path instead of the brute force path.

Root cause

engine/plugins/brute/alterations.go gates every generation technique on five config fields:

go
if cfg.FlipWords && len(cfg.AltWordlist) > 0 {    // line 104
if cfg.FlipNumbers {                              // line 107
if cfg.AddNumbers {                               // line 110
if cfg.AddWords && len(cfg.AltWordlist) > 0 {     // line 113
if distance := cfg.EditDistance; distance > 0 {   // line 117

config/config.go lines 98 to 104 declare those fields:

go
Alterations    bool     `yaml:"-" json:"alterations,omitempty"`
FlipWords      bool     `yaml:"-" json:"-"`
FlipNumbers    bool     `yaml:"-" json:"-"`
AddWords       bool     `yaml:"-" json:"-"`
AddNumbers     bool     `yaml:"-" json:"-"`
MinForWordFlip int      `yaml:"-" json:"-"`
EditDistance   int      `yaml:"-" json:"-"`

NewConfig() assigns useful defaults at config/config.go lines 205 to 210. EditDistance becomes 1. The four boolean fields become true. That code runs inside the CLI process only.

The CLI then creates the session over the engine API. engine/api/server/v1/handlers.go lines 120 and 121 rebuild the config:

go
var config config.Config
if err := json.Unmarshal(raw, &config); err != nil {

The struct starts at its zero value. No later call restores the defaults from NewConfig().

Alterations and AltWordlist cross that boundary, because both carry a real JSON name. The five technique fields carry json:"-", so they do not. Inside the engine they hold false, false, false, false and 0.

Every branch in alterations.go is therefore skipped. guesses stays empty. The plugin never calls store, never calls MarkAssetMonitored, and returns nil.

Each of the five identifiers appears in exactly two files in the repository. config/config.go declares it. engine/plugins/brute/alterations.go reads it. No code assigns any of them on the engine side.

Reproduction

Version v5.1.1, built with go install github.com/owasp-amass/amass/v5/cmd/amass@main on 2026-08-26.

Pick a domain you own that has a subdomain with a plausible prefixed sibling. Use that subdomain as the seed.

bash
export XDG_CONFIG_HOME=$(mktemp -d)
printf 'sub.your_domain_here\n' > seeds.txt
printf 'new\n' > altwords.txt

amass enum -d your_domain_here -alts -brute -aw altwords.txt -nf seeds.txt \
  -silent -nocolor -timeout 3
echo "exit=$?"

sqlite3 "$XDG_CONFIG_HOME/amass/asset.db" \
  "SELECT fqdn FROM fqdn WHERE fqdn LIKE '%your_domain_here'"

addPrefixWords calls addPrefix with the full name, and addPrefix prepends both new and new-. The expected candidates are newsub.your_domain_here and new-sub.your_domain_here.

Observed behaviour:

  • The command exits 0. It writes nothing to stdout and nothing to stderr.
  • The fqdn table holds the seed name and the names found by the other plugins.
  • The fqdn table holds no altered name.
  • The session log holds zero FQDN-Alterations entries.
  • The engine log holds one Plugin started entry for FQDN-Alterations, so the plugin loads and registers correctly.

I ran four variants against a live domain. The variants were -alts alone, -alts -brute, four seed names instead of one, and a fresh isolated database. All four produced zero altered names.

I also verified that the expected candidate resolves to a real address. The target was therefore not the cause.

I withhold the tested domain, because it belongs to a third party.

Related gate at line 74

go
if cfg != nil && (!cfg.BruteForcing || !cfg.Alterations) {

-alts alone does not enable the plugin. The user must also pass -brute, which #1122 describes as unused for brute forcing. We recommend documenting that coupling, whichever fix you choose.

Possible fixes

Option Change
A Give the five fields real JSON names, so the client value reaches the engine.
B Apply the NewConfig() defaults on the engine side, after json.Unmarshal.
C Load the five fields from YAML in loadAlterationSettings, and expose them in config.yaml.

Option B alone restores the documented behaviour. Options A and C also give per-run control.

I am happy to send a pull request for whichever option you prefer. Please tell me which one fits your direction for v5.

Acceptance criteria

  • amass enum -d example.com -alts -brute -aw words.txt stores altered names.
  • The five technique fields hold their intended values inside the engine session.
  • A run that generates zero candidates reports the reason, instead of exiting 0 in silence.
  • The documentation states that -alts requires -brute.