diff --git a/README.md b/README.md index 8c81a22..401c3c1 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,15 @@ Based on work from Scott Sutherland (@\_nullbind), Antti Rantasaari, Eric Gruber ## Install -Use the executables in the releases section. If you want to build it yourself, make sure that your go environment is setup according to the Go setup doc. The goddi package also uses the below package. +`go install github.com/swarley7/goddi` - go get gopkg.in/ldap.v2 +Or, + +``` +git clone https://github.com/swarley7/goddi.git +cd goddi +go install +``` ### Windows diff --git a/ddi/conn.go b/ddi/conn.go index fd52f9b..226b57e 100644 --- a/ddi/conn.go +++ b/ddi/conn.go @@ -12,17 +12,19 @@ import ( // LdapInfo contains connection info type LdapInfo struct { - LdapServer string - LdapIP string - LdapPort uint16 - LdapTLSPort uint16 - User string - Usergpp string - Pass string - Domain string - Conn *ldap.Conn - Unsafe bool - StartTLS bool + LdapServer string + LdapIP string + LdapPort uint16 + LdapTLSPort uint16 + User string + Usergpp string + Pass string + Domain string + Conn *ldap.Conn + Unsafe bool + StartTLS bool + ForceInsecureTLS bool + MntPoint string } func dial(li *LdapInfo) { @@ -48,7 +50,7 @@ func dial(li *LdapInfo) { fmt.Printf("[i] PLAINTEXT LDAP connection to '%s' (%s) successful...\n[i] Upgrade to StartTLS connection...\n", li.LdapServer, li.LdapIP) - err = conn.StartTLS(&tls.Config{ServerName: li.LdapServer}) + err = conn.StartTLS(&tls.Config{ServerName: li.LdapServer, InsecureSkipVerify: li.ForceInsecureTLS}) if err != nil { log.Fatal(err) } @@ -58,7 +60,7 @@ func dial(li *LdapInfo) { } else { fmt.Printf("[i] Begin LDAP TLS connection to '%s' (%s)...\n", li.LdapServer, li.LdapIP) - config := &tls.Config{ServerName: li.LdapServer} + config := &tls.Config{ServerName: li.LdapServer, InsecureSkipVerify: li.ForceInsecureTLS} conn, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", li.LdapServer, li.LdapTLSPort), config) if err != nil { log.Fatal(err) diff --git a/ddi/gpp_unix.go b/ddi/gpp_unix.go index 5cee09f..99aabc9 100644 --- a/ddi/gpp_unix.go +++ b/ddi/gpp_unix.go @@ -15,7 +15,7 @@ import ( // GetGPP grabs all GPP passwords // Reference: Scott Sutherland (@_nullbind), Chris Campbell (@obscuresec) // https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Get-GPPPassword.ps1 -func GetGPP(conn *ldap.Conn, baseDN string, dc string, user string, pass string) { +func GetGPP(conn *ldap.Conn, baseDN string, dc string, user string, pass string, mntpoint string) { fmt.Printf("[i] GPP enumeration starting. This can take a bit...\n") @@ -43,7 +43,6 @@ func GetGPP(conn *ldap.Conn, baseDN string, dc string, user string, pass string) csv := [][]string{} csv = append(csv, attributes) - mntpoint := "/mnt/goddi/" existMount(mntpoint) checkMount(mntpoint) @@ -101,7 +100,7 @@ func existMount(mntpoint string) { // if /mnt/goddi does not exist, mkdir the directory if _, err := os.Stat(mntpoint); os.IsNotExist(err) { os.Mkdir(mntpoint, os.ModePerm) - fmt.Println("[i] /mnt/goddi mount point created...\n") + fmt.Printf("[i] %s mount point created...\n", mntpoint) } } @@ -109,7 +108,7 @@ func existMount(mntpoint string) { func checkMount(mntpoint string) { if len(getSubDirs(mntpoint)) != 0 { - fmt.Printf("[i] /mnt/goddi mounted, unmounting now...\n") + fmt.Printf("[i] %s mounted, unmounting now...\n", mntpoint) _, err := removeUnix(mntpoint) if err != nil { log.Fatal(err) diff --git a/ddi/gpp_win.go b/ddi/gpp_win.go index 9cc23b1..a8ee0a4 100644 --- a/ddi/gpp_win.go +++ b/ddi/gpp_win.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package goddi @@ -14,7 +15,7 @@ import ( // GetGPP grabs all GPP passwords // Reference: Scott Sutherland (@_nullbind), Chris Campbell (@obscuresec) // https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Get-GPPPassword.ps1 -func GetGPP(conn *ldap.Conn, domain string, dc string, user string, pass string) { +func GetGPP(conn *ldap.Conn, domain string, dc string, user string, pass string, _ string) { //Mountpoint not needed here cos windows, lol fmt.Printf("[i] GPP enumeration starting. This can take a bit...\n") diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f42a86a --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module github.com/swarley7/goddi + +go 1.18 + +require gopkg.in/ldap.v2 v2.5.1 + +require gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..96272d8 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU= +gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk= diff --git a/goddi b/goddi new file mode 100755 index 0000000..82108b1 Binary files /dev/null and b/goddi differ diff --git a/main.go b/main.go index 138c0cd..fa3a355 100644 --- a/main.go +++ b/main.go @@ -14,44 +14,80 @@ import ( "flag" "fmt" "log" + "os" "strings" "time" - "github.com/NetSPI/goddi/ddi" + goddi "github.com/swarley7/goddi/ddi" ) func main() { + ldapServer := flag.String("dc", "", "Hostname of DC to connect to. ex. -dc=\"dc.test.local\"") + ldapIP := flag.String("dc-ip", "", "Optional: IP address of DC to connect to (useful if proxying without DNS magic)") + domain := flag.String("domain", "", "Domain, ex. -domain=\"test.local\"") + user := flag.String("username", "", "Username to connect with, ex. -username=\"testuser@example.org\"") + pass := flag.String("password", "", "Password to connect with, ex. -password=\"testpass!\"") + startTLS := flag.Bool("startTLS", false, "Use StartTLS on 389. Default is TLS on 636") + unsafe := flag.Bool("unsafe", false, "Use for testing with plaintext connection") + forceInsecureTLS := flag.Bool("insecure", false, "Ignore TLS errors (e.g., self-signed certificates)") + mntpoint := flag.String("mountpoint", "", "Mount point to use for gpp_password") + basednArg := flag.String("basedn", "", "Base DN to use. If set, this overrides the domain-based calculation.") - ldapServer := flag.String("dc", "", "DC to connect to, use IP or full hostname ex. -dc=\"dc.test.local\"") - domain := flag.String("domain", "", "domain ex. -domain=\"test.local\"") - user := flag.String("username", "", "username to connect with ex. -username=\"testuser\"") - pass := flag.String("password", "", "password to connect with ex. -password=\"testpass!\"") - startTLS := flag.Bool("startTLS", false, "Use for StartTLS on 389. Default is TLS on 636") - unsafe := flag.Bool("unsafe", false, "Use for testing and plaintext connection") flag.Parse() - if len(*ldapServer) == 0 || len(*domain) == 0 || len(*user) == 0 || len(*pass) == 0 { + + dir, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + if *mntpoint == "" { + *mntpoint = dir + "/goddi_mount" + } + if !strings.HasSuffix(*mntpoint, "/") { + *mntpoint = *mntpoint + "/" + } + + if (len(*ldapServer) == 0 && len(*ldapIP) == 0) || len(*domain) == 0 || len(*user) == 0 || len(*pass) == 0 { flag.PrintDefaults() log.Fatal("[ERROR] Provide username, password, DC, and domain!\n") } + if *ldapIP == "" { + *ldapServer, *ldapIP = goddi.ValidateIPHostname(*ldapServer, *domain) + } else { + ldapServer = ldapIP + } - var ldapIP string - *ldapServer, ldapIP = goddi.ValidateIPHostname(*ldapServer, *domain) + // If -basedn is set, we will use that. Otherwise, we require domain and derive baseDN. + var baseDN string + if *basednArg != "" { + baseDN = *basednArg + } else { + if len(*domain) == 0 { + flag.PrintDefaults() + log.Fatal("[ERROR] Provide either a valid domain (-domain) or a custom baseDN (-basedn)!\n") + } + baseDN = "dc=" + strings.Replace(*domain, ".", ",dc=", -1) + } - baseDN := "dc=" + strings.Replace(*domain, ".", ",dc=", -1) - username := *user + "@" + *domain + username := *user li := &goddi.LdapInfo{ - LdapServer: *ldapServer, - LdapIP: ldapIP, - LdapPort: uint16(389), - LdapTLSPort: uint16(636), - User: username, - Usergpp: *user, - Pass: *pass, - Domain: *domain, - Unsafe: *unsafe, - StartTLS: *startTLS} + LdapServer: *ldapServer, + LdapIP: *ldapIP, + LdapPort: uint16(389), + LdapTLSPort: uint16(636), + User: username, + Usergpp: *user, + Pass: *pass, + Domain: *domain, + Unsafe: *unsafe, + StartTLS: *startTLS, + ForceInsecureTLS: *forceInsecureTLS, + MntPoint: *mntpoint, + } + //fmt.Printf("[i] basedn: %s\n", baseDN) + //fmt.Printf("[i] user: %s\n", username) + goddi.Connect(li) defer li.Conn.Close() @@ -78,9 +114,9 @@ func main() { goddi.GetFSMORoles(li.Conn, baseDN) goddi.GetSPNs(li.Conn, baseDN) goddi.GetLAPS(li.Conn, baseDN) - goddi.GetGPP(li.Conn, li.Domain, li.LdapServer, li.Usergpp, li.Pass) + goddi.GetGPP(li.Conn, li.Domain, li.LdapServer, li.Usergpp, li.Pass, li.MntPoint) stop := time.Since(start) cwd := goddi.GetCWD() fmt.Printf("[i] CSVs written to 'csv' directory in %s\n[i] Execution took %s...\n[i] Exiting...\n", cwd, stop) -} +} \ No newline at end of file