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
- Check that the serviceName string exactly matches a registered service (e.g. 'elasticsearch', 'findnet').
- Provide custom credentials in the scan config or ensure the password dictionary file exists and is non-empty.
- Log the output of GenerateCredentials(service, config) before calling TestCredentialsConcurrently to confirm it is non-empty.
- 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
- Validate scan config credentials/dictionary paths before starting a scan
- Assert service names against the plugin registry
- Log GenerateCredentials output during development
- Fail fast on empty config lists at config-load time
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.