Skip to content

[Draft] - Optimize LDAP detector to cap combinations #4226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

ahrav
Copy link
Collaborator

@ahrav ahrav commented Jun 14, 2025

  • Overview:
    Updates the LDAP detector to improve performance and prevent a Cartesian product explosion during credential detection.

  • Key Changes:

    • Refactors pattern matching into high-confidence and proximity-based approaches to efficiently identify LDAP credentials.
    • Implements normalization and deduplication of credentials to mitigate false positives when combining detected URI, username, and password fragments.
    • Introduces a cap on evaluated combinations to prevent performance degradation on large inputs.
  • Rationale:
    These improvements reduce the risk of running expensive nested loops over many potential matches, ensuring that the detector remains performant even with inputs that could trigger an N³ worst-case scenario. The changes also enhance reliability by normalizing credential components before verification.

We created this PR after a client's scanner experienced an OOM event. Memory profiling identified several code paths with excessive memory usage, this detector among them.

memory profile

Screenshot 2025-06-13 at 2 25 41 PM

benchmarks

  • For a single combination, this approach is slightly slower, but the exponential improvements for larger combinations make the tradeoff worthwhile.

Screenshot 2025-06-14 at 11 29 56 AM

NOTE:This draft PR tests several methods to prevent the Cartesian-product explosion of results. Other approaches may exist, but these were the ones that I was able to think through.

Comment on lines +440 to +443
// We purposefully set InsecureSkipVerify because scanners very often run
// in environments where the target's certificate chain is not trusted.
// The objective is simply to confirm that the credentials *could* be
// used, not to validate the server's identity.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate this comment! I wonder if we should make a ticket to... idk make this configurable? It seems not ideal to send a bunch of could-be-creds to an unverified server.

// ensures that Scanner.FromData finishes in reasonable time/allocs despite
// the N³ theoretical combination space. This guards against accidental
// performance regressions that would re-introduce the Cartesian explosion.
func BenchmarkCartesianProductExplosion(b *testing.B) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm no Go benchmarking expert, but some suggestions:

  • Do a context w/ a Deadline/Timeout
  • It looks like the BenchmarkResult type will let you pull out the allocs (etc.), maybe you wanna put in a tripwire of max allocs you're expecting. I guess this also then has to become a normal test, but maybe that's more of what you're looking for anyway.

Comment on lines +302 to +304
// The zero value is not meaningful; instances are produced internally by
// findOptimalCombinations.
type CredentialSet struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is internal you can keep it private if you want

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants