|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "encoding/base64" |
| 5 | + "encoding/hex" |
| 6 | + "fmt" |
| 7 | + "os" |
| 8 | + |
| 9 | + "github.com/TheManticoreProject/Manticore/logger" |
| 10 | + "github.com/TheManticoreProject/Manticore/network/ldap" |
| 11 | + "github.com/TheManticoreProject/Manticore/windows/credentials" |
| 12 | + "github.com/TheManticoreProject/goopts/parser" |
| 13 | + "github.com/TheManticoreProject/winacl/securitydescriptor" |
| 14 | +) |
| 15 | + |
| 16 | +var ( |
| 17 | + // Configuration |
| 18 | + useLdaps bool |
| 19 | + debug bool |
| 20 | + |
| 21 | + // Network settings |
| 22 | + domainController string |
| 23 | + ldapPort int |
| 24 | + |
| 25 | + // Authentication details |
| 26 | + authDomain string |
| 27 | + authUsername string |
| 28 | + authPassword string |
| 29 | + authHashes string |
| 30 | + useKerberos bool |
| 31 | + |
| 32 | + // Source values |
| 33 | + distinguishedName string |
| 34 | + |
| 35 | + sourceFileHex string |
| 36 | + sourceFileBase64 string |
| 37 | + sourceFileRaw string |
| 38 | + valueHex string |
| 39 | + valueBase64 string |
| 40 | +) |
| 41 | + |
| 42 | +func parseArgs() { |
| 43 | + ap := parser.ArgumentsParser{Banner: "DescribeNTSecurityDescriptor - by Remi GASCOU (Podalirius) @ TheManticoreProject - v1.3.0"} |
| 44 | + |
| 45 | + // Configuration flags |
| 46 | + ap.NewBoolArgument(&debug, "-d", "--debug", false, "Debug mode.") |
| 47 | + |
| 48 | + // Source value |
| 49 | + group_sourceValues, err := ap.NewRequiredMutuallyExclusiveArgumentGroup("Source Values") |
| 50 | + if err != nil { |
| 51 | + fmt.Printf("[error] Error creating ArgumentGroup: %s\n", err) |
| 52 | + } else { |
| 53 | + group_sourceValues.NewStringArgument(&distinguishedName, "-D", "--distinguished-name", "", false, "Distinguished Name.") |
| 54 | + // File sources |
| 55 | + group_sourceValues.NewStringArgument(&sourceFileHex, "-fh", "--file-hex", "", false, "Path to file containing the hexadecimal string value of NTSecurityDescriptor.") |
| 56 | + group_sourceValues.NewStringArgument(&sourceFileBase64, "-fb", "--file-base64", "", false, "Path to file containing the base64 encoded value of NTSecurityDescriptor.") |
| 57 | + group_sourceValues.NewStringArgument(&sourceFileRaw, "-fr", "--file-raw", "", false, "Path to file containing the raw binary value of NTSecurityDescriptor.") |
| 58 | + // Value sources |
| 59 | + group_sourceValues.NewStringArgument(&valueHex, "-vh", "--value-hex", "", false, "Raw hexadecimal string value of NTSecurityDescriptor.") |
| 60 | + group_sourceValues.NewStringArgument(&valueBase64, "-vb", "--value-base64", "", false, "Raw base64 encoded value of NTSecurityDescriptor.") |
| 61 | + } |
| 62 | + |
| 63 | + group_ldapSettings, err := ap.NewArgumentGroup("LDAP Connection Settings") |
| 64 | + if err != nil { |
| 65 | + fmt.Printf("[error] Error creating ArgumentGroup: %s\n", err) |
| 66 | + } else { |
| 67 | + group_ldapSettings.NewStringArgument(&domainController, "-dc", "--dc-ip", "", false, "IP Address of the domain controller or KDC (Key Distribution Center) for Kerberos. If omitted, it will use the domain part (FQDN) specified in the identity parameter.") |
| 68 | + group_ldapSettings.NewTcpPortArgument(&ldapPort, "-P", "--port", 389, false, "Port number to connect to LDAP server.") |
| 69 | + group_ldapSettings.NewBoolArgument(&useLdaps, "-l", "--use-ldaps", false, "Use LDAPS instead of LDAP.") |
| 70 | + group_ldapSettings.NewBoolArgument(&useKerberos, "-k", "--use-kerberos", false, "Use Kerberos instead of NTLM.") |
| 71 | + } |
| 72 | + |
| 73 | + group_auth, err := ap.NewArgumentGroup("Authentication") |
| 74 | + if err != nil { |
| 75 | + fmt.Printf("[error] Error creating ArgumentGroup: %s\n", err) |
| 76 | + } else { |
| 77 | + group_auth.NewStringArgument(&authDomain, "-d", "--domain", "", false, "Active Directory domain to authenticate to.") |
| 78 | + group_auth.NewStringArgument(&authUsername, "-u", "--username", "", false, "User to authenticate as.") |
| 79 | + group_auth.NewStringArgument(&authPassword, "-p", "--password", "", false, "Password to authenticate with.") |
| 80 | + group_auth.NewStringArgument(&authHashes, "-H", "--hashes", "", false, "NT/LM hashes, format is LMhash:NThash.") |
| 81 | + } |
| 82 | + |
| 83 | + ap.Parse() |
| 84 | + |
| 85 | + if useLdaps && !group_ldapSettings.LongNameToArgument["--port"].IsPresent() { |
| 86 | + ldapPort = 636 |
| 87 | + } |
| 88 | + |
| 89 | + if len(distinguishedName) != 0 && (len(domainController) == 0 || len(authUsername) == 0 || len(authPassword) == 0) { |
| 90 | + logger.Warn("Error: Options --dc-ip, --username, --password are required when using --distinguished-name.") |
| 91 | + os.Exit(1) |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +func main() { |
| 96 | + parseArgs() |
| 97 | + |
| 98 | + creds, err := credentials.NewCredentials(authDomain, authUsername, authPassword, authHashes) |
| 99 | + if err != nil { |
| 100 | + logger.Warn(fmt.Sprintf("Error creating credentials: %s", err)) |
| 101 | + return |
| 102 | + } |
| 103 | + |
| 104 | + rawNtsdValue := []byte{} |
| 105 | + |
| 106 | + // Parsing input values for hex format |
| 107 | + if len(rawNtsdValue) == 0 && (len(sourceFileHex) != 0 || len(valueHex) != 0) { |
| 108 | + value_hex_string := "" |
| 109 | + if len(valueHex) != 0 { |
| 110 | + value_hex_string = valueHex |
| 111 | + } |
| 112 | + if len(sourceFileHex) != 0 { |
| 113 | + data, err := os.ReadFile(sourceFileHex) |
| 114 | + if err != nil { |
| 115 | + logger.Warn(fmt.Sprintf("Error reading file: %s", err)) |
| 116 | + return |
| 117 | + } |
| 118 | + value_hex_string = string(data) |
| 119 | + } |
| 120 | + // Decoding the hex string |
| 121 | + if len(value_hex_string) != 0 { |
| 122 | + if len(value_hex_string)%2 == 1 { |
| 123 | + // encoding/hex: odd length hex string |
| 124 | + value_hex_string = value_hex_string + "0" |
| 125 | + } |
| 126 | + value, err := hex.DecodeString(value_hex_string) |
| 127 | + if err != nil { |
| 128 | + logger.Warn(fmt.Sprintf("Error decoding Hex value: %s", err)) |
| 129 | + return |
| 130 | + } else { |
| 131 | + rawNtsdValue = value |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + // Parsing input values for base64 format |
| 137 | + if len(rawNtsdValue) == 0 && (len(sourceFileBase64) != 0 || len(valueBase64) != 0) { |
| 138 | + value_base64_string := "" |
| 139 | + if len(valueBase64) != 0 { |
| 140 | + value_base64_string = valueBase64 |
| 141 | + } |
| 142 | + if len(sourceFileBase64) != 0 { |
| 143 | + data, err := os.ReadFile(sourceFileBase64) |
| 144 | + if err != nil { |
| 145 | + logger.Warn(fmt.Sprintf("Error reading file: %s", err)) |
| 146 | + return |
| 147 | + } |
| 148 | + value_base64_string = string(data) |
| 149 | + } |
| 150 | + |
| 151 | + // Decoding the base64 string |
| 152 | + if len(value_base64_string) != 0 { |
| 153 | + value, err := base64.StdEncoding.DecodeString(value_base64_string) |
| 154 | + if err != nil { |
| 155 | + logger.Warn(fmt.Sprintf("Error decoding Base64 value: %s", err)) |
| 156 | + return |
| 157 | + } else { |
| 158 | + rawNtsdValue = value |
| 159 | + } |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + // Parsing input values for raw format |
| 164 | + if len(rawNtsdValue) == 0 && len(sourceFileRaw) != 0 { |
| 165 | + data, err := os.ReadFile(sourceFileRaw) |
| 166 | + if err != nil { |
| 167 | + logger.Warn(fmt.Sprintf("Error reading file: %s", err)) |
| 168 | + return |
| 169 | + } |
| 170 | + rawNtsdValue = data |
| 171 | + } |
| 172 | + |
| 173 | + if len(rawNtsdValue) == 0 && len(distinguishedName) != 0 { |
| 174 | + // Parsing input values for Distinguished Name |
| 175 | + if debug { |
| 176 | + if !useLdaps { |
| 177 | + logger.Debug(fmt.Sprintf("Connecting to remote ldap://%s:%d ...", domainController, ldapPort)) |
| 178 | + } else { |
| 179 | + logger.Debug(fmt.Sprintf("Connecting to remote ldaps://%s:%d ...", domainController, ldapPort)) |
| 180 | + } |
| 181 | + } |
| 182 | + ldapSession := ldap.Session{} |
| 183 | + ldapSession.InitSession(domainController, ldapPort, creds, useLdaps, useKerberos) |
| 184 | + connected, err := ldapSession.Connect() |
| 185 | + if err != nil { |
| 186 | + logger.Warn(fmt.Sprintf("Error connecting to LDAP: %s", err)) |
| 187 | + return |
| 188 | + } |
| 189 | + |
| 190 | + if connected { |
| 191 | + logger.Info(fmt.Sprintf("Connected as '%s\\%s'", authDomain, authUsername)) |
| 192 | + |
| 193 | + query := fmt.Sprintf("(distinguishedName=%s)", distinguishedName) |
| 194 | + |
| 195 | + if debug { |
| 196 | + logger.Debug(fmt.Sprintf("LDAP query used: %s", query)) |
| 197 | + } |
| 198 | + |
| 199 | + attributes := []string{"distinguishedName", "ntSecurityDescriptor"} |
| 200 | + ldapResults, err := ldapSession.QueryWholeSubtree("", query, attributes) |
| 201 | + if err != nil { |
| 202 | + logger.Warn(fmt.Sprintf("Error querying LDAP: %s", err)) |
| 203 | + return |
| 204 | + } |
| 205 | + |
| 206 | + for _, entry := range ldapResults { |
| 207 | + if debug { |
| 208 | + logger.Debug(fmt.Sprintf("| distinguishedName: %s", entry.GetAttributeValue("distinguishedName"))) |
| 209 | + } |
| 210 | + rawNtsdValue = entry.GetEqualFoldRawAttributeValue("ntSecurityDescriptor") |
| 211 | + } |
| 212 | + } else { |
| 213 | + if debug { |
| 214 | + logger.Warn("Error: Could not create ldapSession.") |
| 215 | + } |
| 216 | + } |
| 217 | + } |
| 218 | + |
| 219 | + if len(rawNtsdValue) != 0 { |
| 220 | + ntsd := securitydescriptor.NtSecurityDescriptor{} |
| 221 | + logger.Debug(fmt.Sprintf("| ntSecurityDescriptor: %s", hex.EncodeToString(rawNtsdValue))) |
| 222 | + _, err := ntsd.Unmarshal(rawNtsdValue) |
| 223 | + if err != nil { |
| 224 | + logger.Warn(fmt.Sprintf("Error unmarshalling NTSecurityDescriptor: %s", err)) |
| 225 | + return |
| 226 | + } |
| 227 | + ntsd.Describe(0) |
| 228 | + } else { |
| 229 | + logger.Warn("No NTSecurityDescriptor found in source values.") |
| 230 | + } |
| 231 | +} |
0 commit comments