Skip to content

Commit 2b27843

Browse files
authored
Merge pull request #19 from jazofra/main
Improve Windows AD fallback and progress reporting for large domain enumeration
2 parents 3dfe2e9 + ef5ad7f commit 2b27843

12 files changed

Lines changed: 1049 additions & 1014 deletions

File tree

.claude/plans/ldap_sspi_fix.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# LDAP SSPI Fix Plan
2+
3+
## Goal
4+
Make Windows LDAP enumeration use native SSPI integrated authentication when no explicit LDAP credentials are supplied, matching ShareHound behavior.
5+
6+
## Tasks
7+
1. Inspect MSSQLHound LDAP connection and bind selection logic.
8+
2. Inspect ShareHound Windows LDAP/SSPI implementation for the working pattern.
9+
3. Implement the minimal Windows-specific SSPI path or correct the existing selection logic.
10+
4. Add focused unit coverage where feasible without requiring a live domain controller.
11+
5. Run `go test ./...` and address failures related to this change.
12+
13+
## Follow-up
14+
1. Keep successful Windows ADSI fallback logs concise at info level.
15+
2. Preserve the full Go LDAP failure only at verbose level for troubleshooting.
16+
3. Re-run `go test ./...` and rebuild `mssqlhound.exe`.
17+
18+
## Go ADSI Computer Enumeration
19+
1. Add an in-process Windows ADSI/ADO computer enumeration helper in Go; do not fork PowerShell.
20+
2. Use the same filter and attributes as ShareHound's Go LDAP path: all computer objects, `dNSHostName` first and `name` as fallback.
21+
3. Route `--scan-all-computers` Windows fallback to the Go helper.
22+
4. Add non-Windows stub/tests, run `go test ./...`, and rebuild `mssqlhound.exe`.
23+
24+
## IP Deduplication Progress
25+
1. Resolve unique hostnames concurrently instead of serially resolving every server entry.
26+
2. Emit info-level progress during DNS resolution and final dedupe processing.
27+
3. Preserve existing input-order dedupe preference behavior.

.claude/tasks/lessons.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Lessons
2+
3+
- For Windows AD fallback paths, avoid `exec.Command("powershell", ...)` when in-process Go/COM/LDAP APIs can do the work. Fork/exec can be blocked by policy even when native ADSI works.
4+
- With go-ole ADO collections, retrieve `Item` via property access first (`GetProperty(collection, "Item", name)`), not method invocation; otherwise provider properties like `Page Size` can fail with `Member not found`.
5+
- Long-running AD enumeration must emit progress at info level and carry useful attributes like `objectSid` forward to avoid slow follow-up lookup loops.
6+
- Long-running DNS/IP dedupe must emit info-level progress and avoid serial per-entry lookups; resolve unique hostnames concurrently with a bounded worker count.

README.md

Lines changed: 29 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -222,17 +222,13 @@ Each authentication below generates log entries on the target system.
222222
| WinRM login | SQL Server host | NTLM or Basic auth | Logged as a Windows authentication event | **Only `test-epa-matrix` subcommand** |
223223
| WMI/DCOM login | SQL Server host | Current user's Windows credentials | Logged as a DCOM authentication event | **Windows only**, during local group enumeration |
224224

225-
## Subprocesses Executed
226-
227-
MSSQLHound spawns local `powershell.exe` processes as fallbacks when native Go clients fail. All subprocesses run on the **operator's machine**, not on targets (except WinRM remote execution).
228-
229-
| Executable | Arguments | Purpose | Conditions |
230-
|------------|-----------|---------|------------|
231-
| `powershell.exe` | `-NoProfile -NonInteractive -Command <script>` | SQL query execution via `System.Data.SqlClient` | Windows only. Fallback when Go TDS driver fails with "untrusted domain" error. **Not used when `--proxy` is set.** |
232-
| `powershell.exe` | `-NoProfile -NonInteractive -Command <script>` | SPN enumeration via `[adsisearcher]` (ADSI) | Windows only. Fallback when Go LDAP client fails. |
233-
| `powershell.exe` | `-NoProfile -NonInteractive -Command <script>` | Domain computer enumeration via `[adsisearcher]` (ADSI) | Windows only. Fallback when Go LDAP client fails. |
234-
| `powershell.exe` | `-NoProfile -NonInteractive -Command <script>` | SPN lookup by hostname via `[adsisearcher]` (ADSI) | Windows only. Fallback when Go LDAP client fails. |
235-
| `powershell.exe` | `-NoProfile -NonInteractive -EncodedCommand <base64>` | Remote PowerShell via WinRM: EPA registry configuration and SQL service restart on target host | **Only `test-epa-matrix` subcommand.** Executes on the remote target via WinRM. |
225+
## Subprocesses Executed
226+
227+
MSSQLHound does not spawn local `powershell.exe` processes as collection fallbacks. The only PowerShell command constructed by the Go binary is sent through WinRM for the EPA matrix workflow.
228+
229+
| Executable | Arguments | Purpose | Conditions |
230+
|------------|-----------|---------|------------|
231+
| `powershell.exe` | `-NoProfile -NonInteractive -EncodedCommand <base64>` | Remote PowerShell via WinRM: EPA registry configuration and SQL service restart on target host | **Only `test-epa-matrix` subcommand.** Executes on the remote target via WinRM. |
236232

237233
## SQL Queries Executed on Targets
238234

@@ -359,9 +355,8 @@ The original MSSQLHound PowerShell script is an excellent tool for SQL Server se
359355
- **Single Binary**: No dependencies, easy to deploy and run
360356
- **No PowerShell Required**: Can run on systems without PowerShell installed
361357

362-
### Compatibility
363-
- **PowerShell Fallback**: When the native Go SQL driver fails (e.g., certain SSPI configurations), automatically falls back to PowerShell's `System.Data.SqlClient` for maximum compatibility
364-
- **Full Feature Parity**: Produces identical BloodHound-compatible output
358+
### Compatibility
359+
- **Full Feature Parity**: Produces identical BloodHound-compatible output
365360

366361
### Maintainability
367362
- **Strongly Typed**: Go's type system catches errors at compile time
@@ -371,12 +366,11 @@ The original MSSQLHound PowerShell script is an excellent tool for SQL Server se
371366
## Features
372367

373368
- **SQL Server Collection**: Enumerates server principals (logins, server roles), databases, database principals (users, roles), permissions, and role memberships
374-
- **Linked Server Discovery**: Maps SQL Server linked server relationships
375-
- **Active Directory Integration**: Resolves Windows logins to domain principals via LDAP
376-
- **BloodHound Output**: Produces OpenGraph JSON format compatible with BloodHound CE
377-
- **Streaming Output**: Memory-efficient streaming JSON writer for large environments
378-
- **Automatic Fallback**: Falls back to PowerShell for servers with SSPI issues
379-
- **LDAP Paging**: Handles large domains with thousands of computers/SPNs
369+
- **Linked Server Discovery**: Maps SQL Server linked server relationships
370+
- **Active Directory Integration**: Resolves Windows logins to domain principals via LDAP
371+
- **BloodHound Output**: Produces OpenGraph JSON format compatible with BloodHound CE
372+
- **Streaming Output**: Memory-efficient streaming JSON writer for large environments
373+
- **LDAP Paging**: Handles large domains with thousands of computers/SPNs
380374

381375
## Building
382376

@@ -751,24 +745,23 @@ mssqlhound completion powershell | Out-String | Invoke-Expression
751745

752746
### Connection Handling
753747

754-
The Go version includes automatic PowerShell fallback for servers that fail with the native `go-mssqldb` driver:
748+
The Go version uses the native `go-mssqldb` driver with multiple connection strategies for hostname, encryption, and SPN handling:
755749

756750
```
757-
Native connection: go-mssqldb (fast, cross-platform)
758-
↓ fails with "untrusted domain" error
759-
Fallback: PowerShell + System.Data.SqlClient (Windows only, more compatible)
760-
```
761-
762-
This ensures maximum compatibility while maintaining performance for the majority of servers.
751+
Native connection: go-mssqldb (fast, cross-platform)
752+
```
753+
754+
If all native strategies fail, the connection error is returned directly.
763755

764756
### LDAP Connection Methods
765757

766758
The Go version tries multiple LDAP connection methods in order:
767759

768-
1. **LDAPS (port 636)** - TLS encrypted, most secure
769-
2. **LDAP + StartTLS (port 389)** - Upgrade to TLS
770-
3. **Plain LDAP (port 389)** - Unencrypted (may fail if DC requires signing)
771-
4. **PowerShell/ADSI Fallback** - Windows COM-based fallback
760+
1. **LDAPS (port 636)** - TLS encrypted, most secure
761+
2. **LDAP + StartTLS (port 389)** - Upgrade to TLS
762+
3. **Plain LDAP (port 389)** - Unencrypted (may fail if DC requires signing)
763+
764+
For `--scan-all-computers` on Windows with implicit LDAP authentication, computer enumeration may use in-process Go ADSI if LDAP computer enumeration fails. This path does not launch PowerShell.
772765

773766
## CVE Detection
774767

@@ -827,18 +820,7 @@ Some SQL Server instances with specific SSPI configurations may fail to connect
827820
Login failed. The login is from an untrusted domain and cannot be used with Windows authentication
828821
```
829822

830-
**Automatic Handling:** The Go version detects this error and automatically retries using PowerShell's `System.Data.SqlClient`, which handles these edge cases more reliably. This fallback requires PowerShell to be available on the system.
831-
832-
### PowerShell Fallback Limitations
833-
834-
The PowerShell fallback for SQL connections and AD enumeration requires:
835-
- Windows operating system
836-
- PowerShell execution not blocked by security policy
837-
- Access to `System.Data.SqlClient` (.NET Framework)
838-
839-
If PowerShell is blocked (e.g., `Access is denied` error), the fallback will not work. In this case:
840-
- For SQL connections: Some servers may not be reachable
841-
- For AD enumeration: Use explicit LDAP credentials instead
823+
There is no local PowerShell retry path for this condition. Check domain trust, SPN selection, EPA settings, and the selected authentication method; use SQL authentication or explicit Kerberos material where appropriate.
842824

843825
### When to Use LDAP Credentials
844826

@@ -872,11 +854,10 @@ Use `-v` or `--verbose` to see detailed connection attempts and errors:
872854

873855
| Error | Cause | Solution |
874856
|-------|-------|----------|
875-
| `untrusted domain` | SSPI negotiation failed | Automatic PowerShell fallback; check domain trust |
876-
| `Size Limit Exceeded` | Too many LDAP results | Update to latest version (has paging) |
877-
| `80090346` | GSSAPI/Kerberos failure | Use explicit LDAP credentials |
878-
| `Strong Auth Required` | DC requires LDAP signing | Will automatically try LDAPS/StartTLS |
879-
| `Access is denied` (PowerShell) | Execution policy blocked | Use explicit LDAP credentials instead |
857+
| `untrusted domain` | SSPI negotiation failed | Check domain trust, SPN selection, EPA settings, and authentication method |
858+
| `Size Limit Exceeded` | Too many LDAP results | Update to latest version (has paging) |
859+
| `80090346` | GSSAPI/Kerberos failure | Use explicit LDAP credentials |
860+
| `Strong Auth Required` | DC requires LDAP signing | Will automatically try LDAPS/StartTLS |
880861

881862
### Debug LDAP Connection
882863

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build !windows
2+
3+
package collector
4+
5+
import "fmt"
6+
7+
func (c *Collector) enumerateComputersViaWindowsADSI() ([]domainComputer, error) {
8+
return nil, fmt.Errorf("Windows ADSI computer enumeration is only available on Windows")
9+
}

0 commit comments

Comments
 (0)