[SHEHZADE//ABDULLAH ANSARI]
Red Team Consultant @ Google | MS in Cybersecurity @ NYU
● 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
95 lines · 2.2 KB
| 1 | package registry |
| 2 | |
| 3 | import ( |
| 4 | "encoding/binary" |
| 5 | "encoding/hex" |
| 6 | "fmt" |
| 7 | "io" |
| 8 | "os" |
| 9 | "unicode/utf16" |
| 10 | |
| 11 | "www.velocidex.com/golang/regparser" |
| 12 | ) |
| 13 | |
| 14 | var bootKeyPermutation = []int{8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7} |
| 15 | |
| 16 | func readClassString(r io.ReaderAt, key *regparser.CM_KEY_NODE) (string, error) { |
| 17 | offset := key.Class() |
| 18 | length := key.ClassLength() |
| 19 | if length == 0 { |
| 20 | return "", fmt.Errorf("empty class") |
| 21 | } |
| 22 | buf := make([]byte, length) |
| 23 | _, err := r.ReadAt(buf, int64(4096+offset+4)) |
| 24 | if err != nil { |
| 25 | return "", err |
| 26 | } |
| 27 | u16 := make([]uint16, length/2) |
| 28 | for i := 0; i < len(u16); i++ { |
| 29 | u16[i] = binary.LittleEndian.Uint16(buf[i*2 : (i+1)*2]) |
| 30 | } |
| 31 | return string(utf16.Decode(u16)), nil |
| 32 | } |
| 33 | |
| 34 | func GetBootKey(systemHivePath string) ([]byte, error) { |
| 35 | f, err := os.Open(systemHivePath) |
| 36 | if err != nil { |
| 37 | return nil, fmt.Errorf("failed to open SYSTEM hive: %w", err) |
| 38 | } |
| 39 | defer f.Close() |
| 40 | |
| 41 | reg, err := regparser.NewRegistry(f) |
| 42 | if err != nil { |
| 43 | return nil, fmt.Errorf("failed to parse SYSTEM hive: %w", err) |
| 44 | } |
| 45 | |
| 46 | selectKey := reg.OpenKey("Select") |
| 47 | if selectKey == nil { |
| 48 | return nil, fmt.Errorf("could not find Select key in SYSTEM hive") |
| 49 | } |
| 50 | |
| 51 | var currentControlSet string |
| 52 | for _, val := range selectKey.Values() { |
| 53 | if val.ValueName() == "Current" { |
| 54 | currentControlSet = fmt.Sprintf("ControlSet%03d", val.ValueData().Uint64) |
| 55 | break |
| 56 | } |
| 57 | } |
| 58 | if currentControlSet == "" { |
| 59 | currentControlSet = "ControlSet001" |
| 60 | } |
| 61 | |
| 62 | subkeys := []string{"JD", "Skew1", "GBG", "Data"} |
| 63 | var hexStr string |
| 64 | |
| 65 | for _, sub := range subkeys { |
| 66 | path := fmt.Sprintf("%s\\Control\\Lsa\\%s", currentControlSet, sub) |
| 67 | k := reg.OpenKey(path) |
| 68 | if k == nil { |
| 69 | return nil, fmt.Errorf("could not find key: %s", path) |
| 70 | } |
| 71 | s, err := readClassString(f, k) |
| 72 | if err != nil { |
| 73 | return nil, fmt.Errorf("failed to read class for %s: %w", path, err) |
| 74 | } |
| 75 | if len(s) > 8 { |
| 76 | s = s[:8] |
| 77 | } |
| 78 | hexStr += s |
| 79 | } |
| 80 | |
| 81 | rawBytes, err := hex.DecodeString(hexStr) |
| 82 | if err != nil { |
| 83 | return nil, fmt.Errorf("failed to decode bootkey hex: %w", err) |
| 84 | } |
| 85 | if len(rawBytes) != 16 { |
| 86 | return nil, fmt.Errorf("expected 16 scrambled bytes, got %d", len(rawBytes)) |
| 87 | } |
| 88 | |
| 89 | bootKey := make([]byte, len(rawBytes)) |
| 90 | for i := range bootKey { |
| 91 | bootKey[i] = rawBytes[bootKeyPermutation[i]] |
| 92 | } |
| 93 | |
| 94 | return bootKey, nil |
| 95 | } |