kgretzky/evilginx2 · error

login: `domain` must contain a value of one of the hostnames

Error message

login: `domain` must contain a value of one of the hostnames (`orig_subdomain` + `domain`) defined in `proxy_hosts` section

What it means

The `login.domain` value must match (case-insensitively) one of the hostnames derived from the `proxy_hosts` section (`orig_subdomain` + "." + `domain`, optionally with a leading `*.` check). If it does not correspond to any proxy host, loading aborts because Evilginx could not route the login page through any proxied host.

Source

Thrown at core/phishlet.go:658

	}
	p.login.domain = p.paramVal(*fp.LoginItem.Domain)
	if p.login.domain == "" {
		return fmt.Errorf("login: `domain` field cannot be empty")
	}
	login_domain_ok := false
	for _, h := range p.proxyHosts {
		var check_host string
		if h.orig_subdomain != "" {
			check_host = h.orig_subdomain + "."
		}
		check_host += h.domain
		if strings.ToLower(check_host) == strings.ToLower(p.login.domain) {
			login_domain_ok = true
			break
		}
	}
	if !login_domain_ok {
		return fmt.Errorf("login: `domain` must contain a value of one of the hostnames (`orig_subdomain` + `domain`) defined in `proxy_hosts` section")
	}

	p.login.path = p.paramVal(*fp.LoginItem.Path)
	if p.login.path == "" {
		p.login.path = "/"
	}
	if p.login.path[0] != '/' {
		p.login.path = "/" + p.login.path
	}

	if fp.Credentials.Custom != nil {
		for _, cp := range *fp.Credentials.Custom {
			var err error
			if cp.Key == nil {
				return fmt.Errorf("credentials: missing custom `key` field")
			}
			if cp.Search == nil {
				return fmt.Errorf("credentials: missing custom `search` field")

View on GitHub (pinned to 4c0988a1d9)

Solutions

  1. Set `login.domain` to exactly `orig_sub + "." + domain` as written in one of the proxy_hosts entries.
  2. If the login happens on a different host, add a matching proxy_hosts entry for that host.
  3. Do not use the phishing (phish_sub) hostname in login.domain — it must be the original one.
  4. Re-check for typos and case; matching is case-insensitive but text must otherwise be identical.

Example fix

// before
proxy_hosts:
  - phish_sub: acct
    orig_sub: accounts
    domain: example.com
login:
  domain: login.example.com
// after
login:
  domain: accounts.example.com
Defensive patterns

Strategy: validation

Validate before calling

d := strings.ToLower(pl.Login.Domain)
ok := false
for _, h := range pl.ProxyHosts {
    if strings.ToLower(h.OrigSubdomain+"."+h.Domain) == d { ok = true; break }
}
if !ok {
    return fmt.Errorf("login.domain %q not found in proxy_hosts", pl.Login.Domain)
}

Type guard

func loginDomainMatches(l *LoginItem, hosts []ProxyHost) bool {
    if l == nil || l.Domain == nil { return false }
    for _, h := range hosts {
        if strings.EqualFold(h.OrigSubdomain+"."+h.Domain, *l.Domain) { return true }
    }
    return false
}

Try / catch

if err := pl.Load(cfg); err != nil {
    if strings.Contains(err.Error(), "must contain a value of one of the hostnames") {
        log.Printf("login.domain must equal orig_sub+'.'+domain of a proxy_host in %s", pl.Name)
    }
}

Prevention

When it happens

Trigger: `login.domain` set to a hostname like `login.example.com` while proxy_hosts only defines `accounts.example.com`; or a typo/top-level mismatch (e.g. `example.org` vs `example.com`); or omitting the subdomain when proxy_hosts expects `orig_subdomain.domain`.

Common situations: Renaming proxy_hosts after writing the login block; copying a phishlet for one site and editing only the domain; mixing up `phish_sub` (your phishing subdomain) with `orig_sub` (must be the orig one).

Related errors


AI-assisted analysis of kgretzky/evilginx2@4c0988a1d9 (2026-09-05). Data as JSON: /api/errors/ae02aaac906b1620. Report an issue: GitHub.