Windows MDM research (#9500)

Adding a tool to showcase how to perform MDM programmatic enrollment on
Windows. Moved Windows MDM PoC server to a new location
This commit is contained in:
Marcos Oviedo
2023-01-25 17:05:19 -03:00
committed by GitHub
parent 851545c21f
commit d7f77f55ce
39 changed files with 1037 additions and 120 deletions
@@ -64,17 +64,29 @@ func getConfigurationProfiles(cmdIDstart int) string {
}
var syncmlCommands string
for _, file := range files {
cmdIDstart++
var tokenCmdID string = "xxcmdidxx"
fmt.Printf("\n--------- Command Request %d ---------\n", cmdIDstart)
fmt.Printf("Command payload retrieved from file %s\n", file.Name())
for _, file := range files {
fileContent, err := os.ReadFile(profileDir + "/" + file.Name())
if err != nil {
panic(err)
}
syncmlCommands += strings.Replace(string(fileContent), "xxcmdidxx", strconv.Itoa(cmdIDstart), -1) + "\n"
fileContentStr := string(fileContent)
nrTokenOcurrences := strings.Count(fileContentStr, tokenCmdID)
for i := 0; i < nrTokenOcurrences; i++ {
cmdIDstart++
fmt.Printf("\n--------- Command Request %d ---------\n", cmdIDstart)
fmt.Printf("Command payload retrieved from file %s\n", file.Name())
fileContentStr = strings.Replace(fileContentStr, tokenCmdID, strconv.Itoa(cmdIDstart), 1)
}
if len(fileContentStr) > 0 {
syncmlCommands += fileContentStr
syncmlCommands += "\n"
}
}
//input sanitization
File diff suppressed because one or more lines are too long
@@ -0,0 +1,24 @@
<Atomic>
<CmdID>7</CmdID>
<Add>
<CmdID>5</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall</LocURI>
</Target>
</Item>
</Add>
<Exec>
<CmdID>6</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall</LocURI>
</Target>
<Meta>
<A:Format>xml</A:Format>
<A:Type>text/plain</A:Type>
</Meta>
<Data>&lt;MsiInstallJob id="{90413BF7-7D99-482E-A7FB-C6616CC871FC}"&gt;&lt;Product Version="1.4.0"&gt;&lt;Download&gt;&lt;ContentURLList&gt;&lt;ContentURL&gt;https://mdmwindows.com/static/fleet-osquery.msi&lt;/ContentURL&gt;&lt;/ContentURLList&gt;&lt;/Download&gt;&lt;Validation&gt;&lt;FileHash&gt;3B9FD63248465A51500D41DECC794D1149506EB48EEF9D7A733516B482D16ABB&lt;/FileHash&gt;&lt;/Validation&gt;&lt;Enforcement&gt;&lt;CommandLine&gt;/quiet&lt;/CommandLine&gt;&lt;RetryCount&gt;5&lt;/RetryCount&gt;&lt;RetryInterval&gt;3&lt;/RetryInterval&gt;&lt;/Enforcement&gt;&lt;/Product&gt;&lt;/MsiInstallJob&gt;</Data>
</Item>
</Exec>
</Atomic>
@@ -0,0 +1,13 @@
<Add>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/Policy/Config/Settings/AllowDateTime</LocURI>
</Target>
<Meta>
<A:Format>int</A:Format>
<A:Type>text/plain</A:Type>
</Meta>
<Data>0</Data>
</Item>
</Add>
@@ -0,0 +1,13 @@
<Add>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/Policy/Config/Defender/AllowRealtimeMonitoring</LocURI>
</Target>
<Meta>
<A:Format>int</A:Format>
<A:Type>text/plain</A:Type>
</Meta>
<Data>0</Data>
</Item>
</Add>
@@ -0,0 +1,13 @@
<Add>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Vendor/MSFT/Firewall/MdmStore/PrivateProfile/EnableFirewall</LocURI>
</Target>
<Meta>
<A:Format>bool</A:Format>
<A:Type>text/plain</A:Type>
</Meta>
<Data>false</Data>
</Item>
</Add>
@@ -0,0 +1,8 @@
<Get>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Vendor/MSFT/CertificateStore/Root/System?list=StructData</LocURI>
</Target>
</Item>
</Get>
@@ -0,0 +1,38 @@
<Add>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413bf7-7d99-482e-a7fb-c6616cc871fc%7D/DownloadInstall</LocURI>
</Target>
</Item>
</Add>
<Exec>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413bf7-7d99-482e-a7fb-c6616cc871fc%7D/DownloadInstall</LocURI>
</Target>
<Data>&lt;MsiInstallJob id="{90413bf7-7d99-482e-a7fb-c6616cc871fc}"&gt;
&lt;Product Version="1.0.0.0"&gt;
&lt;Download&gt;
&lt;ContentURLList&gt;
&lt;ContentURL&gt;https://mdmwindows.com/static/fleet-osquery.msi&lt;/ContentURL&gt;
&lt;/ContentURLList&gt;
&lt;/Download&gt;
&lt;Validation&gt;
&lt;FileHash&gt;3B9FD63248465A51500D41DECC794D1149506EB48EEF9D7A733516B482D16ABB&lt;/FileHash&gt;
&lt;/Validation&gt;
&lt;Enforcement&gt;
&lt;CommandLine&gt;/quiet&lt;/CommandLine&gt;
&lt;TimeOut&gt;10&lt;/TimeOut&gt;
&lt;RetryCount&gt;1&lt;/RetryCount&gt;
&lt;RetryInterval&gt;5&lt;/RetryInterval&gt;
&lt;/Enforcement&gt;
&lt;/Product&gt;
&lt;/MsiInstallJob&gt;</Data>
<Meta>
<Type xmlns="syncml:metinf">text/plain</Type>
<Format xmlns="syncml:metinf">xml</Format>
</Meta>
</Item>
</Exec>
@@ -0,0 +1,13 @@
<Replace>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Vendor/MSFT/Personalization/DesktopImageUrl</LocURI>
</Target>
<Meta>
<Format xmlns="syncml:metinf">chr</Format>
<Type>text/plain</Type>
</Meta>
<Data>https://fleetdm.com/images/articles/fleet-4.24.0-cover-1600x900@2x.jpg</Data>
</Item>
</Replace>
@@ -0,0 +1,153 @@
//go:build windows
// +build windows
package testenroll
import (
"errors"
"flag"
"fmt"
"os"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var (
modmdmregistration *windows.LazyDLL = windows.NewLazySystemDLL("mdmregistration.dll")
procIsDeviceRegisteredWithManagement *windows.LazyProc = modmdmregistration.NewProc("IsDeviceRegisteredWithManagement")
procRegisterDeviceWithManagement *windows.LazyProc = modmdmregistration.NewProc("RegisterDeviceWithManagement")
)
const (
maxBufSize = 2048 // Max Unicode Length for UPN - https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adls/63f5e067-d1b3-4e6e-9e53-a92953b6005b
)
// checking for administrator rights
func checkForAdmin() bool {
f, err := os.Open("\\\\.\\PHYSICALDRIVE0")
if err != nil {
return false
}
f.Close()
return true
}
// builtin utf16tostring string expects a uint16 array but we have a pointer to a uint16
// so we need to cast it after converting it to an unsafe pointer
// this is a common pattern though the buffer size varies
// see https://golang.org/pkg/unsafe/#Pointer for more details
func localUTF16toString(ptr unsafe.Pointer) (string, error) {
if ptr == nil {
return "", errors.New("failed UTF16 conversion due to null pointer")
} else {
uint16ptrarr := (*[maxBufSize]uint16)(ptr)[:]
return windows.UTF16ToString(uint16ptrarr), nil
}
}
// getting the MDM enrollment status by calling into OS API IsDeviceRegisteredWithManagement()
func getEnrollmentInfo() (uint32, string, error) {
// variable to hold the MDM enrollment status
var isDeviceRegisteredWithMDM uint32 = 0
// heap-allocated buffer to hold the URI data
buffUriData := make([]uint16, 0, maxBufSize)
if buffUriData == nil {
return 0, "", errors.New("failed to allocate memory for URI data")
}
// IsDeviceRegisteredWithManagement is going to return the MDM enrollment status
// https://learn.microsoft.com/en-us/windows/win32/api/mdmregistration/nf-mdmregistration-isdeviceregisteredwithmanagement
if returnCode, _, err := procIsDeviceRegisteredWithManagement.Call(uintptr(unsafe.Pointer(&isDeviceRegisteredWithMDM)), maxBufSize, uintptr(unsafe.Pointer(&buffUriData))); returnCode != uintptr(windows.ERROR_SUCCESS) {
return 0, "", fmt.Errorf("there was an error calling IsDeviceRegisteredWithManagement(): %s (0x%X)", err, returnCode)
} else {
uriData, err := localUTF16toString(unsafe.Pointer(&buffUriData))
if err != nil {
return 0, "", err
}
return isDeviceRegisteredWithMDM, uriData, nil
}
}
// Perform the host MDM enrollment process using MS-MDE protocol
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-mde/5c841535-042e-489e-913c-9d783d741267
func enrollHostToMDM(mdmDiscoveryEndpoint string, mdmUpnUser string) (bool, error) {
// converting go server discovery endpoint string into UTF16 windows string
inputDiscoveryEndpoint := syscall.StringToUTF16Ptr(mdmDiscoveryEndpoint)
// converting go upn user string into UTF16 windows string
inputMdmUPN := syscall.StringToUTF16Ptr(mdmUpnUser)
// converting go csr string into UTF16 windows string
// passing empty value to force MDM OS stack to generate a CSR for us
// See here for details of CSR generation https://github.com/Gerenios/AADInternals/blob/2efc23e28dc66135d37eca117f456c64b2e48ea9/MDM_utils.ps1#L84
// CN should be <randomGUID>|<DeviceClientId>
// DeviceClientId value is located at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\MDMDeviceID
var encodedB64 string = ""
inputCSRreq := syscall.StringToUTF16Ptr(encodedB64)
// RegisterDeviceWithManagement() registers a device with a MDM service
// https://learn.microsoft.com/en-us/windows/win32/api/mdmregistration/nf-mdmregistration-registerdevicewithmanagement
if returnCode, _, err := procRegisterDeviceWithManagement.Call(uintptr(unsafe.Pointer(inputMdmUPN)), uintptr(unsafe.Pointer(inputDiscoveryEndpoint)), uintptr(unsafe.Pointer(inputCSRreq))); returnCode != uintptr(windows.ERROR_SUCCESS) {
return false, fmt.Errorf("there was an error calling RegisterDeviceWithManagement(): %s (0x%X)", err, returnCode)
}
return true, nil
}
func main() {
targetDiscoveryUrl := flag.String("discovery-url", "", "Target MDM discovery webservice url")
targetUserEmail := flag.String("upn-email", "", "Target email to enroll server")
flag.Usage = func() {
w := flag.CommandLine.Output()
fmt.Fprintf(w, "Available Command Line Options\n")
flag.PrintDefaults()
fmt.Fprintf(w, "\nExample Usage\n%s -discovery-url <target_mdm_server_discovery_url> -upn-email <target_mdm_enrollment_user>\n", os.Args[0])
fmt.Fprintf(w, "%s -discovery-url https://mdmwindows.com/EnrollmentServer/Discovery.svc -upn-email demo@mdmwindows.com\n", os.Args[0])
}
flag.Parse()
if len(*targetDiscoveryUrl) == 0 || len(*targetUserEmail) == 0 {
fmt.Printf("There was a problem with provided arguments.")
os.Exit(1)
}
// Checking if running with admin rights
if !checkForAdmin() {
fmt.Printf("You should have Administrator rights to run the tool.")
os.Exit(1)
}
// Checking if host is already enrolled
enrollmentStatus, enrollmentUser, err := getEnrollmentInfo()
if err != nil {
fmt.Print(err)
os.Exit(1)
}
if enrollmentStatus != 0 {
fmt.Printf("Host is already enrolled through user: %s\n", enrollmentUser)
os.Exit(1)
}
// Perform the actual enrollment
hostEnrolled, err := enrollHostToMDM(*targetDiscoveryUrl, *targetUserEmail)
if hostEnrolled {
fmt.Printf("Host was successfully enrolled into MDM server %s through user %s\n", *targetDiscoveryUrl, *targetUserEmail)
} else {
fmt.Printf(" There was a problem enrolling into MDM server %s through user %s - Error %s\n", *targetDiscoveryUrl, *targetUserEmail, err)
}
}
@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1RuIhpHWDudu07FfJ2bTAzm8DaD5F9WSxC8cKm6OCttN7tIj
FzyayThorwTZ37+VeJMZ7/bP/bqxTkF7Y59geEsFlDnLvQWm2Hh4f79MV08cWx/T
B3Wqgk6jPfr0dS2sQZv+JiS6/OR0BCTpUPZwM7eQiN3SoPDZEibIaLt53ECVc8ze
mD8cHrNmCk8HIWOmhzP5UzM21pXy8JOo2aEql6iVBoUa0QVfJVX9EHDPnmJmeA6f
d0uDZTvi8YSj+GqcmcBmGTe/rbpfaJjPiaXce0bF2JKjmK38bjgK29fHoEZ+Scw3
ul160fxMV1bBhIcaJt+uEQyOKkdqLYPZ+lZ+yQIDAQABAoIBAHbGfMZ8G/F8njGQ
53b/gVaH5D84W/0jxURg+XLQ4Yw9hOc56eL2nVLPhNEfhAuILVfhrRAo4O4LEu2J
46q31r3VGovt1pdIwiBerNKOnY8AAc7sIuNCesFb8PIHoB57UUnUFsfNqwZukhcJ
N50vbYP1qLIP6GhZNLNAOGzfKOFPfTA1hpgXkUAYFUC6D5DwIMNuxX/5QYjAw8fK
e0NsehjuzTb+HckazvB/B0SpPZyezQrpJU5AEWzxQgcL+Qx5BdcOPyR83vdH8PoJ
83uj4FhNA16dHmo87AGe6d2FncadCoLpxjcJgjctqcDnJFdAdPcJOcA76qYeDh4E
nkSali0CgYEA4POeRx1RnUI7YN1b4JwY8qTj8ucMTaZk/Kjb1BPXX5rqC6Vx7I4N
UNCLNvPn6U/1W9NXzijamyddxcNtHcy8jXqlYawOebE3MAZvayBlC4yPYCd230LJ
ADl5auQeNj2ktJbJwYgZQcwnMEu3SGYIMvy96pKdWilUenPbfO9pj/cCgYEA8oVr
W17vQJX+8hmItcnqEHoCFXi2hM/vKZCHF/MrknJcpANTo7rRQaxn3xIqZRkt6xJz
vp5yl20pzl/KDSMxLd4H51fKy6Fzk0hvFpdqbsxLidu+rf1o3Nice6WBLqz+XMtV
i/dTqvD9b+CVmnoVmwVHoAja2QTZCAQnLGqCNz8CgYEAwZ7PGFTS/7GXXEuLnmud
SZTFozhdraRP/ez1sbgWQ/MaCkYwJbUrHukxOm57qaUqAgyJ4ifl6W/b1bHdBK5J
iNkM6mHm37W6U7rmQeXTMzqb2d5+AbMBQRE3QdrxaixqzQmQxOR5INowzPAO5OD1
o7VJXlMt3wH99ZwtSn7jdIcCgYEAzxEDfNwtwyNOrj8G7tAbPT4vEU4j6HnxZbe0
4MoK5dsnJhKBE0aq7Dvb5CaKdA9vmUoD8Tkv9gKKs14uEdF+Z/8vGGNpDzwmhhZO
YyedBEUCKg6pW70GD6oS0a+aANRLyccCn6LomQdyHFfQ5Dhgwh9b7FQjJzBwbdu9
5rp5u9kCgYA1KZWTkSxjUEycTNKgQz4MKFCEbYkm8B6lfAY5GYhTI1fsoympgd4d
RYmQUuhHrLHP4s85NOSoRdrMD8MiRyhHna+FoaqIEIW5ErMkNDM9SYs4TyrIxf7l
R42IvNH76CcJpcWYhQ1PCLdsbuyO9lMkXBylPMI1VPZkjUZqj/Ixfw==
-----END RSA PRIVATE KEY-----
@@ -1,27 +0,0 @@
Bag Attributes
localKeyID: 01 00 00 00
friendlyName: mdmwindows.com
subject=CN = *.mdmwindows.com
issuer=CN = *.mdmwindows.com
-----BEGIN CERTIFICATE-----
MIIDPTCCAiWgAwIBAgIQGQpbyNZeY7hBkxKCbRRfaTANBgkqhkiG9w0BAQsFADAb
MRkwFwYDVQQDDBAqLm1kbXdpbmRvd3MuY29tMB4XDTIyMTIyODE0MTUxMloXDTMy
MTIyODE0MjUxMlowGzEZMBcGA1UEAwwQKi5tZG13aW5kb3dzLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBANUbiIaR1g7nbtOxXydm0wM5vA2g+RfV
ksQvHCpujgrbTe7SIxc8msk4aK8E2d+/lXiTGe/2z/26sU5Be2OfYHhLBZQ5y70F
pth4eH+/TFdPHFsf0wd1qoJOoz369HUtrEGb/iYkuvzkdAQk6VD2cDO3kIjd0qDw
2RImyGi7edxAlXPM3pg/HB6zZgpPByFjpocz+VMzNtaV8vCTqNmhKpeolQaFGtEF
XyVV/RBwz55iZngOn3dLg2U74vGEo/hqnJnAZhk3v626X2iYz4ml3HtGxdiSo5it
/G44CtvXx6BGfknMN7pdetH8TFdWwYSHGibfrhEMjipHai2D2fpWfskCAwEAAaN9
MHswDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
ATArBgNVHREEJDAigg5tZG13aW5kb3dzLmNvbYIQKi5tZG13aW5kb3dzLmNvbTAd
BgNVHQ4EFgQUsFD9edObvrbuLZopzjgGtpr8ZTQwDQYJKoZIhvcNAQELBQADggEB
AEEfa9BS75jG4D2fJ1/Q9Xn/SPsaAtwUYW+ilGCqYBfQ8lBmXGN8z8WETdw5xus3
FGdIYtw8SKF5fp3TOJlNkiF0LNhAEvwDEkNCtOK9XpqTScjDi2WT1c+gJmPyHj7M
+vn9+gHFI7tUT+JImqU1I6tzD2OsZS5H1Vow+QwD3/DswSoUKM+zQreJGKaKLZqo
i6B/fdS+XkYWymwXmQiu+7D8RwTGEMIrfPFon90I9APrDhOmjiDa7L+xs7zRfT4J
fzIbHn867msNrZzwPAmf3fRhEk8cwHD5jfgnXuoVL4icPG57rUvCfX+QI9FfpjH4
ZIbwI+HQg86S0hVwqhGs1RI=
-----END CERTIFICATE-----
@@ -1,8 +0,0 @@
<Add>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall</LocURI>
</Target>
</Item>
</Add>
@@ -1,31 +0,0 @@
<Exec>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall</LocURI>
</Target>
<Meta>
<Format xmlns="syncml:metinf">xml</Format>
<Type xmlns="syncml:metinf">text/plain</Type>
</Meta>
<Data>
&lt;MsiInstallJob id=&quot;{90413BF7-7D99-482E-A7FB-C6616CC871FC}&quot;&gt;
&lt;Product Version=&quot;1.0.0&quot;&gt;
&lt;Download&gt;
&lt;ContentURLList&gt;
&lt;ContentURL&gt;https://mdmwindows.com/static/fleet-osquery.msi&lt;/ContentURL&gt;
&lt;/ContentURLList&gt;
&lt;/Download&gt;
&lt;Validation&gt;
&lt;FileHash&gt;3B9FD63248465A51500D41DECC794D1149506EB48EEF9D7A733516B482D16ABB&lt;/FileHash&gt;
&lt;/Validation&gt;
&lt;Enforcement&gt;
&lt;CommandLine&gt;/quiet&lt;/CommandLine&gt;
&lt;RetryCount&gt;3&lt;/RetryCount&gt;
&lt;RetryInterval&gt;5&lt;/RetryInterval&gt;
&lt;/Enforcement&gt;
&lt;/Product&gt;
&lt;/MsiInstallJob&gt;
</Data>
</Item>
</Exec>
@@ -1,8 +0,0 @@
<Add>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall</LocURI>
</Target>
</Item>
</Add>
@@ -1,13 +0,0 @@
<Exec>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/EnterpriseDesktopAppManagement/MSI/%7B90413BF7-7D99-482E-A7FB-C6616CC871FC%7D/DownloadInstall</LocURI>
</Target>
<Meta>
<Format xmlns="syncml:metinf">xml</Format>
<Type xmlns="syncml:metinf">text/plain</Type>
</Meta>
<Data>&lt;MsiInstallJob id="{90413BF7-7D99-482E-A7FB-C6616CC871FC}"&gt;&lt;Product Version="1.4.0"&gt;&lt;Download&gt;&lt;ContentURLList&gt;&lt;ContentURL&gt;https://mdmwindows.com/static/fleet-osquery.msi&lt;/ContentURL&gt;&lt;/ContentURLList&gt;&lt;/Download&gt;&lt;Validation&gt;&lt;FileHash&gt;3B9FD63248465A51500D41DECC794D1149506EB48EEF9D7A733516B482D16ABB&lt;/FileHash&gt;&lt;/Validation&gt;&lt;Enforcement&gt;&lt;CommandLine&gt;/quiet&lt;/CommandLine&gt;&lt;RetryCount&gt;5&lt;/RetryCount&gt;&lt;RetryInterval&gt;3&lt;/RetryInterval&gt;&lt;/Enforcement&gt;&lt;/Product&gt;&lt;/MsiInstallJob&gt;</Data>
</Item>
</Exec>