● Notice //GitHub (@shehzade) is temporarily suspended pending an automated false-positive review. All tools are hosted on this Code Mirror and available on request (contact); GitHub links will return once reinstated.

Offline extractor and decryptor for SCCM/MECM Network Access Account (NAA) credentials and policy secrets from dead-disk WMI repositories and DPAPI master keys.

$ git clone https://shehzade.io/repos/naa-dump-offline.git Copy
Source ZIP (290.0 KB) Prebuilt Binaries
Code 15 Commits 3 Releases 5
naa-dump-offline / pkg / wmi / policy.go
← Back to directory
162 lines · 4.0 KB
Raw
1package wmi
2
3import (
4 "bytes"
5 "compress/zlib"
6 "encoding/hex"
7 "fmt"
8 "io"
9 "regexp"
10 "strings"
11
12 "github.com/shehzade/naa-dump-offline/pkg/dpapi"
13)
14
15type NAACredential struct {
16 Username string `json:"username"`
17 Password string `json:"password"`
18 Source string `json:"source,omitempty"`
19}
20
21type CarvedSecret struct {
22 Index int `json:"index"`
23 Offset int64 `json:"offset"`
24 MasterKeyGUID string `json:"masterkey_guid"`
25 Context string `json:"context"`
26 RawHex string `json:"-"`
27 DecryptedBytes []byte `json:"-"`
28 DecryptedText string `json:"decrypted_text,omitempty"`
29 IsEmpty bool `json:"is_empty"`
30}
31
32var (
33 cdataRegex = regexp.MustCompile(`<!\[CDATA\[([0-9A-Fa-f]{100,})\]\]>`)
34 xmlHexRe = regexp.MustCompile(`<!\[CDATA\[([0-9A-Fa-f]+)\]\]>`)
35)
36
37func DecompressZlib(hexPayload string) ([]byte, error) {
38 data, err := hex.DecodeString(strings.TrimSpace(hexPayload))
39 if err != nil {
40 return nil, err
41 }
42 r, err := zlib.NewReader(bytes.NewReader(data))
43 if err != nil {
44 return nil, err
45 }
46 defer r.Close()
47 return io.ReadAll(r)
48}
49
50func ParseDecryptedPayload(data []byte) (string, bool) {
51 if len(data) == 0 || (len(data) <= 2 && bytes.Equal(data, []byte{0, 0})) {
52 return "", true
53 }
54
55 text := dpapi.DecodeUTF16LE(data)
56 if text == "" {
57 text = strings.TrimRight(string(data), "\x00")
58 }
59
60 return text, false
61}
62
63func ExtractCredentialsFromSecrets(secrets []*CarvedSecret) []NAACredential {
64 credentials := []NAACredential{}
65 seen := make(map[string]bool)
66
67 var potentialUsers []string
68 var potentialPasswords []string
69
70 for _, s := range secrets {
71 if s.IsEmpty || s.DecryptedText == "" {
72 continue
73 }
74
75 if strings.Contains(s.DecryptedText, "CCM_NetworkAccessAccount") {
76 extractFromXML(s.DecryptedText, &credentials, seen)
77 }
78
79 if strings.Contains(s.DecryptedText, `Compression="zlib"`) {
80 matches := xmlHexRe.FindAllStringSubmatch(s.DecryptedText, -1)
81 for _, m := range matches {
82 decomp, err := DecompressZlib(m[1])
83 if err == nil {
84 decompText := dpapi.DecodeUTF16LE(decomp)
85 if decompText == "" {
86 decompText = string(decomp)
87 }
88 extractFromXML(decompText, &credentials, seen)
89 }
90 }
91 }
92
93 text := strings.TrimSpace(s.DecryptedText)
94 if !strings.HasPrefix(text, "<") && !strings.Contains(text, "\n") {
95 if strings.Contains(text, "\\") || strings.Contains(text, "@") {
96 potentialUsers = append(potentialUsers, text)
97 } else if len(text) > 0 && len(text) < 128 {
98 potentialPasswords = append(potentialPasswords, text)
99 }
100 }
101 }
102
103 for _, u := range potentialUsers {
104 for _, p := range potentialPasswords {
105 key := fmt.Sprintf("%s:%s", u, p)
106 if !seen[key] {
107 seen[key] = true
108 credentials = append(credentials, NAACredential{
109 Username: u,
110 Password: p,
111 Source: "OBJECTS.DATA",
112 })
113 }
114 }
115 }
116
117 return credentials
118}
119
120func isLikelyHexCiphertext(s string) bool {
121 if len(s) < 32 {
122 return false
123 }
124 for _, c := range s {
125 if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
126 return false
127 }
128 }
129 return true
130}
131
132func extractFromXML(xmlContent string, creds *[]NAACredential, seen map[string]bool) {
133 userRe := regexp.MustCompile(`name="NetworkAccessUsername"[^>]*>[\s\r\n]*<value>[\s\r\n]*<!\[CDATA\[([^\]]+)\]\]>`)
134 passRe := regexp.MustCompile(`name="NetworkAccessPassword"[^>]*>[\s\r\n]*<value>[\s\r\n]*<!\[CDATA\[([^\]]+)\]\]>`)
135
136 userMatches := userRe.FindAllStringSubmatch(xmlContent, -1)
137 passMatches := passRe.FindAllStringSubmatch(xmlContent, -1)
138
139 minLen := len(userMatches)
140 if len(passMatches) < minLen {
141 minLen = len(passMatches)
142 }
143
144 for i := 0; i < minLen; i++ {
145 u := strings.TrimSpace(userMatches[i][1])
146 p := strings.TrimSpace(passMatches[i][1])
147
148 if isLikelyHexCiphertext(u) || isLikelyHexCiphertext(p) {
149 continue
150 }
151
152 key := fmt.Sprintf("%s:%s", u, p)
153 if !seen[key] && (u != "" || p != "") {
154 seen[key] = true
155 *creds = append(*creds, NAACredential{
156 Username: u,
157 Password: p,
158 Source: "Policy XML",
159 })
160 }
161 }
162}