shadow1ng/fscan · warning

%s

Error message

%s

What it means

TestCredentialsConcurrently returns this ScanResult when the credential list built for the service is empty, so there is nothing to test. The library treats 'no candidate credentials' as a hard stop rather than silently reporting success. The message text comes from the i18n key service_no_test_creds.

Source

Thrown at plugins/services/credential_tester.go:210

}

// TestCredentialsConcurrently 并发测试多个凭据
// 找到成功凭据后立即通知其他 worker 停止
func TestCredentialsConcurrently(
	ctx context.Context,
	credentials []Credential,
	authFn AuthFunc,
	serviceName string,
	testConfig ConcurrentTestConfig,
) *ScanResult {
	if ctx == nil {
		ctx = context.Background()
	}
	if len(credentials) == 0 {
		return &ScanResult{
			Success: false,
			Service: serviceName,
			Error:   fmt.Errorf("%s", i18n.GetText("service_no_test_creds")),
		}
	}
	testConfig = normalizeConcurrentTestConfig(testConfig)

	// TCP 预检:快速验证目标可达,避免对不可达目标浪费全部凭据尝试
	// 代理模式下跳过:net.DialTimeout 直连无法到达代理后的内网目标
	if testConfig.TargetAddr != "" && !testConfig.UseProxy {
		dialCtx, dialCancel := context.WithTimeout(ctx, 3*time.Second)
		defer dialCancel()

		var dialer net.Dialer
		preConn, err := dialer.DialContext(dialCtx, "tcp", testConfig.TargetAddr)
		if err != nil {
			return &ScanResult{
				Success: false,
				Service: serviceName,
				Error:   fmt.Errorf(i18n.Tr("service_target_unreachable", "%w"), err),
			}

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Check that the serviceName string exactly matches a registered service (e.g. 'elasticsearch', 'findnet').
  2. Provide custom credentials in the scan config or ensure the password dictionary file exists and is non-empty.
  3. Log the output of GenerateCredentials(service, config) before calling TestCredentialsConcurrently to confirm it is non-empty.
  4. If an empty credential list is legitimate for your use case, skip the concurrent test call instead of invoking it.

Example fix

// before
creds := GenerateCredentials(service, config)
return TestCredentialsConcurrently(ctx, service, creds, authFn, testConfig)
// after
creds := GenerateCredentials(service, config)
if len(creds) == 0 {
    return &ScanResult{Success: false, Service: service, Error: errors.New("no credentials generated; check config/dictionary")}
}
return TestCredentialsConcurrently(ctx, service, creds, authFn, testConfig)
Defensive patterns

Strategy: validation

Validate before calling

creds := GenerateCredentials(serviceName, config)
if len(creds) == 0 {
    return fmt.Errorf("no credentials generated for %q; check config and dictionary", serviceName)
}

Try / catch

res := TestCredentialsConcurrently(ctx, svc, creds, fn, cfg)
if !res.Success && res.Error != nil && res.Error.Error() == i18n.GetText("service_no_test_creds") {
    log.Printf("skipping %s: no credentials", svc)
    return nil
}

Prevention

When it happens

Trigger: Calling TestCredentialsConcurrently (directly or via Scan) when GenerateCredentials(service, config) yields zero entries — typically because the service name is unknown to the credential generator or config supplies neither custom credentials nor a password dictionary.

Common situations: Misspelled service name passed to Scan; custom credential config with an empty credentials list; dictionary files missing so no weak-password candidates are generated; running against a service that has no built-in default credentials.

Related errors


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