Skip to content

Commit 61637bd

Browse files
committed
Added option to dump rootDSE attributes as JSON
1 parent 6ea0cd7 commit 61637bd

File tree

3 files changed

+168
-1
lines changed

3 files changed

+168
-1
lines changed

dumpRootDSE.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package main
2+
3+
import (
4+
"github.com/lkarlslund/ldap/v3"
5+
)
6+
7+
var defaultDumpAttrs = []string{
8+
"configurationNamingContext",
9+
"currentTime",
10+
"defaultNamingContext",
11+
"dNSHostName",
12+
"dsSchemaAttrCount",
13+
"dsSchemaClassCount",
14+
"dsSchemaPrefixCount",
15+
"dsServiceName",
16+
"highestCommittedUSN",
17+
"isGlobalCatalogReady",
18+
"isSynchronized",
19+
"ldapServiceName",
20+
"namingContexts",
21+
"netlogon",
22+
"pendingPropagations",
23+
"rootDomainNamingContext",
24+
"schemaNamingContext",
25+
"serverName",
26+
"subschemaSubentry",
27+
"supportedCapabilities",
28+
"supportedControl",
29+
"supportedLDAPPolicies",
30+
"supportedLDAPVersion",
31+
"supportedSASLMechanisms",
32+
"domainControllerFunctionality",
33+
"domainFunctionality",
34+
"forestFunctionality",
35+
"msDS-ReplAllInboundNeighbors",
36+
"msDS-ReplAllOutboundNeighbors",
37+
"msDS-ReplConnectionFailures",
38+
"msDS-ReplLinkFailures",
39+
"msDS-ReplPendingOps",
40+
"msDS-ReplQueueStatistics",
41+
"msDS-TopQuotaUsage",
42+
"supportedConfigurableSettings",
43+
"supportedExtension",
44+
"validFSMOs",
45+
"dsaVersionString",
46+
"msDS-PortLDAP",
47+
"msDS-PortSSL",
48+
"msDS-PrincipalName",
49+
"serviceAccountInfo",
50+
"spnRegistrationResult",
51+
"tokenGroups",
52+
"usnAtRifm",
53+
"approximateHighestInternalObjectID",
54+
"databaseGuid",
55+
"schemaIndexUpdateState",
56+
"dumpLdapNotifications",
57+
"msDS-ProcessLinksOperations",
58+
"msDS-SegmentCacheInfo",
59+
"msDS-ThreadStates",
60+
"ConfigurableSettingsEffective",
61+
"LDAPPoliciesEffective",
62+
"msDS-ArenaInfo",
63+
"msDS-Anchor",
64+
"msDS-PrefixTable",
65+
"msDS-SupportedRootDSEAttributes",
66+
"msDS-SupportedRootDSEModifications",
67+
}
68+
69+
func dumpRootDSE(conn *ldap.Conn) (map[string][]string, error) {
70+
result := make(map[string][]string)
71+
72+
// See if we can ask the server what attributes it knows about
73+
probeAttrs := getRootDSEAttribute(conn, "msDS-SupportedRootDSEAttributes")
74+
if len(probeAttrs) == 0 {
75+
probeAttrs = defaultDumpAttrs
76+
}
77+
78+
// Extract what we can
79+
for _, attribute := range probeAttrs {
80+
result[attribute] = getRootDSEAttribute(conn, attribute)
81+
}
82+
return result, nil
83+
}
84+
85+
func getRootDSEAttribute(conn *ldap.Conn, attribute string) []string {
86+
request := ldap.NewSearchRequest(
87+
"", // The base dn to search
88+
ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false,
89+
"(objectClass=*)", // The filter to apply
90+
[]string{attribute}, // A list attributes to retrieve
91+
nil,
92+
)
93+
response, err := conn.Search(request)
94+
if err == nil && len(response.Entries) == 1 && len(response.Entries[0].Attributes) == 1 {
95+
return response.Entries[0].Attributes[0].Values
96+
}
97+
return nil
98+
}

main.go

+60
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"bufio"
55
"crypto/tls"
6+
"encoding/json"
67
"flag"
78
"fmt"
89
"log"
@@ -46,6 +47,8 @@ func main() {
4647
inputname := flag.String("input", "", "File to read usernames from, uses stdin if not supplied")
4748
outputname := flag.String("output", "", "File to write detected usernames to, uses stdout if not supplied")
4849

50+
dumpDSE := flag.Bool("dump", false, "Just dump the rootDSE, no bruteforcing")
51+
4952
// evasive maneuvers
5053
throttle := flag.Int("throttle", 0, "Only do a request every N ms, 0 to disable")
5154
maxrequests := flag.Int("maxrequests", 0, "Disconnect and reconnect a connection after n requests, 0 to disable")
@@ -239,6 +242,63 @@ func main() {
239242
log.Fatal("missing AD controller server name - please provide this on commandline")
240243
}
241244

245+
if *dumpDSE {
246+
type response struct {
247+
Server string
248+
Error string `json:",omitempty"`
249+
Response map[string][]string `json:",omitempty"`
250+
}
251+
252+
var responses []response
253+
254+
for _, server := range servers {
255+
var conn *ldap.Conn
256+
switch tlsmode {
257+
case NoTLS:
258+
conn, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", server, *port))
259+
case StartTLS:
260+
conn, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", server, *port))
261+
if err == nil {
262+
err = conn.StartTLS(&tls.Config{ServerName: server})
263+
}
264+
case TLS:
265+
config := &tls.Config{
266+
ServerName: server,
267+
InsecureSkipVerify: *ignoreCert,
268+
}
269+
conn, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", server, *port), config)
270+
}
271+
272+
if err != nil {
273+
log.Printf("Problem connecting to LDAP %v server: %v", server, err)
274+
continue
275+
}
276+
277+
result, err := dumpRootDSE(conn)
278+
279+
nr := response{
280+
Server: server,
281+
Response: result,
282+
}
283+
if err != nil {
284+
nr.Error = err.Error()
285+
}
286+
responses = append(responses, nr)
287+
conn.Close()
288+
}
289+
290+
// all done
291+
js, _ := json.Marshal(responses)
292+
293+
// sort JSON, stupid hack but it works
294+
var ifce any
295+
json.Unmarshal(js, &ifce)
296+
js, _ = json.MarshalIndent(ifce, "", " ")
297+
298+
output.Write(js)
299+
return
300+
}
301+
242302
inputqueue := make(chan string, 128)
243303
outputqueue := make(chan string, 128)
244304

readme.MD

+10-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ go install github.com/lkarlslund/ldapnomnom@latest
2626
ldapnomnom [--server dc1.domain.suffix[,dc2.domain.suffix] | --dnsdomain domain.suffix] [--port number] [--tlsmode notls|tls|starttls] [--input filename] [--output filename [--progressbar]] [--parallel number-of-connections] [--maxservers number-of-servers] [--maxstrategy fastest|random] [--throttle n] [--maxrequests n]
2727
```
2828
29-
### Examples
29+
### Bruteforcing examples
3030
3131
Connect to up to 32 servers from contoso.local with 16 connections to each - FAAAAAAAST
3232
```bash
@@ -40,6 +40,15 @@ ldapnomnom --input 10m_usernames.txt --output results.txt --server 192.168.0.11
4040
4141
Look for username lists to feed into this elsewhere - for instance the 10M list from [here](https://github.com/danielmiessler/SecLists/tree/master/Usernames)
4242
43+
### Extract rootDSE attributes
44+
45+
You can also use LDAP Nom Nom to dump attributes from the rootDSE object, by adding the "--dump" option.
46+
47+
Connect to all servers you can find, and output all readable attributes to JSON:
48+
```bash
49+
ldapnomnom --output rootDSEs.json --dump
50+
```
51+
4352
## Detection
4453
4554
- No Windows event logs are generated (tested on Windows 2016 / 2019)

0 commit comments

Comments
 (0)