shadow1ng/fscan · error

%s

Error message

%s

What it means

The FTP plugin's Scan returns a ScanResult whose Error field carries the i18n message for 'no credentials available' when GenerateCredentials("ftp", config) yields an empty list. Scan aborts before any connection is attempted because there is nothing to test. This is a configuration/input problem, not a network failure.

Source

Thrown at plugins/services/ftp.go:47

	config := session.Config
	state := session.State
	if config.DisableBrute {
		return p.identifyService(info, session)
	}

	target := info.Target()

	// 优先检测匿名访问
	if result := p.testAnonymousAccess(ctx, info, session); result != nil && result.Success {
		return result
	}

	credentials := GenerateCredentials("ftp", config)
	if len(credentials) == 0 {
		return &ScanResult{
			Success: false,
			Service: "ftp",
			Error:   fmt.Errorf("%s", i18n.GetText("service_no_credentials")),
		}
	}

	// 使用公共框架进行并发凭据测试
	authFn := p.createAuthFunc(info, config, state)
	testConfig := DefaultConcurrentTestConfigWithTarget(config, info)

	result := TestCredentialsConcurrently(ctx, credentials, authFn, "ftp", testConfig)

	if result.Success {
		// 成功后重新连接获取文件列表
		fileList := p.getFileListAfterAuth(info, result.Username, result.Password, config, state)
		var output strings.Builder
		fmt.Fprintf(&output, "FTP %s %s:%s", target, result.Username, result.Password)
		if len(fileList) > 0 {
			for _, file := range fileList {
				fmt.Fprintf(&output, "\n   [->] %s", file)
			}

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Provide at least one username and password in the FTP scan config
  2. Enable built-in default credential lists if you intended dictionary brute force
  3. Validate the config before invoking Scan and fail fast with a clear message
  4. Check GenerateCredentials("ftp", config) unit behavior for your config shape

Example fix

// before
credentials := GenerateCredentials("ftp", config) // may be empty
// after
credentials := GenerateCredentials("ftp", config)
if len(credentials) == 0 {
    return nil, fmt.Errorf("ftp scan requires at least one username/password pair in config")
}
Defensive patterns

Strategy: validation

Validate before calling

cfg := loadFTPConfig(config)
if len(cfg.Usernames) == 0 || len(cfg.Passwords) == 0 {
    return fmt.Errorf("ftp config must contain at least one username and password")
}

Type guard

func hasCredentials(c map[string][]string) bool {
    return len(c["usernames"]) > 0 && len(c["passwords"]) > 0
}

Try / catch

res := plugin.Scan(info, config, state)
if res != nil && res.Error != nil && strings.Contains(res.Error.Error(), i18n.GetText("service_no_credentials")) {
    log.Println("ftp: add usernames/passwords to the scan config and rerun")
    return
}

Prevention

When it happens

Trigger: Scan (public) on the FTP plugin: credentials := GenerateCredentials("ftp", config) returns len 0 — e.g. empty or missing user/pass lists in the config with no defaults enabled.

Common situations: Config supplied with no username or password wordlists; typo'd config keys so the credential generator finds nothing; user intentionally restricted to custom credentials but passed an empty list.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/80feab9855209a17. Report an issue: GitHub.