remove old Windows MDM PoC tool (#39473)

This PR cleans up an old tool/windows-mdm-poc, that we no longer use or
rely on.

It is also an effort to minimze inline dependencies when unused.
This commit is contained in:
Magnus Jensen 2026-02-06 13:55:16 -05:00 committed by GitHub
parent aab959a7c7
commit bce4c4afae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 4 additions and 2613 deletions

View file

@ -33,6 +33,8 @@
- CSPs: https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-configuration-service-provider
- Initial PoC PR's: [MDM Server](https://github.com/fleetdm/fleet/pull/9178) [Programmatic Enrollment](https://github.com/fleetdm/fleet/pull/9500)
### Android
See the [Android MDM documentation](./android-mdm.md)

View file

@ -42,16 +42,14 @@ import (
// The certificates generated by a Windows device during MDM enrollment were not being parsed correctly
// by the standard x509 package. To work around this issue, we used a modified version of the isPrintable
// function from the ASN.1 package that allows invalid characters.
// You can find the implementation of isPrintable here:
// You can find the original implementation of isPrintable here:
// https://github.com/golang/go/blob/e126572f8a91d42b86242012012d0cad4507dca8/src/encoding/asn1/asn1.go#L433
// The modification to the standard library requires applying the following patch to the x509 package:
// https://github.com/fleetdm/fleet/blob/main/tools/mdm/windows/poc-mdm-server/patch/patch.go
// Since this solution is not ideal and does not scale, we have wrapped a custom version of
// ParseCertificateRequest with the necessary changes in ParseCertificateRequestFromWindowsDevice().
// The invalid characters are: '!' and 0x00.
// Here's an example of a certificate with invalid characters in the CommonName field:
// Common Name: F717C0F0-5F68-4AC3-A341-01B254!4219DFB0A902F747A9C4FD43C8CE36CE
// The new characters have been added to the isPrintable() function below.
// The patched isPrintable method that allows these new characters, can be found below in this file.
func ParseCertificateRequestFromWindowsDevice(asn1Data []byte) (*x509.CertificateRequest, error) {
var csr certificateRequest

View file

@ -34,7 +34,6 @@ This directory solves that problem by creating "dummy" manifest files that list
| josharian/impl | server/mock/mockimpl/ | v1.4.0 |
| mitchellh/gon | orbit/pkg/packaging/macos_notarize.go | v0.2.3 |
| sassoftware/relic | pkg/file/xar.go | v7.2.1+incompatible |
| oscartbeaumont/windows_mdm | tools/mdm/windows/poc-mdm-server/ | v0.0.0-20210615145659-e52e28e50db7 |
### npm dependencies (package.json)

View file

@ -49,8 +49,4 @@ require (
// relic - XAR file parsing (pkg/file/xar.go)
// Copied: April 2023
github.com/sassoftware/relic v7.2.1+incompatible
// poc-mdm-server - Windows MDM demo (tools/mdm/windows/poc-mdm-server/)
// Forked from oscartbeaumont/windows_mdm
github.com/oscartbeaumont/windows_mdm v0.0.0-20210615145659-e52e28e50db7
)

View file

@ -262,7 +262,6 @@ go run ./tools/run-scripts -scripts-disabled -content 'echo "Test"'
| `mdm/migration/micromdm/` | MicroMDM migration tools | See [mdm/migration/micromdm/README.md](mdm/migration/micromdm/README.md) |
| `mdm/migration/simplemdm/` | SimpleMDM migration tools | `go run ./tools/mdm/migration/simplemdm` |
| `mdm/windows/bitlocker/` | BitLocker key management | Go utilities for BitLocker |
| `mdm/windows/poc-mdm-server/` | PoC Windows MDM server | See [mdm/windows/poc-mdm-server/README.md](mdm/windows/poc-mdm-server/README.md) |
| `mdm/windows/programmatic-enrollment/` | Windows MDM enrollment | `go run ./tools/mdm/windows/programmatic-enrollment` |
| `windows-mdm-enroll/` | Windows MDM enrollment | Enrollment utilities for Windows |
| **Other Utilities** | | |

View file

@ -1,18 +0,0 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Mac System Files
.DS_Store
# Ignore The Folder With My HTTPS Certifciates
certs/

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 Oscar Beaumont
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because one or more lines are too long

View file

@ -1,10 +0,0 @@
module github.com/oscartbeaumont/windows_mdm
go 1.12
require (
github.com/ernesto-jimenez/httplogger v0.0.0-20220128121225-117514c3f345
github.com/go-xmlfmt/xmlfmt v1.1.2
github.com/gorilla/handlers v1.4.2
github.com/gorilla/mux v1.7.3
)

View file

@ -1,8 +0,0 @@
github.com/ernesto-jimenez/httplogger v0.0.0-20220128121225-117514c3f345 h1:AZLrCR38RDhsyCQakz1UxCx72As18Ai5mObrKvT8DK8=
github.com/ernesto-jimenez/httplogger v0.0.0-20220128121225-117514c3f345/go.mod h1:pw+gaKQ52Cl/SrERU62yQAiWauPpLgKpuR1hkxwL4tM=
github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U=
github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=

View file

@ -1,180 +0,0 @@
package main
import (
"bytes"
"flag"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/http/httputil"
"os"
"github.com/go-xmlfmt/xmlfmt"
"github.com/gorilla/mux"
)
// Code forked from https://github.com/oscartbeaumont/windows_mdm
// Global config, populated via Command line flags
var (
domain string
deepLinkUserEmail string
authPolicy string
profileDir string
staticDir string
verbose bool
)
func main() {
fmt.Println("Starting Windows MDM Demo Server")
// Parse CMD flags. This populates the varibles defined above
flag.StringVar(&domain, "domain", "mdmwindows.com", "Your servers primary domain")
flag.StringVar(&deepLinkUserEmail, "dl-user-email", "demo@mdmwindows.com", "An email of the enrolling user when using the Deeplink ('/deeplink')")
flag.StringVar(&authPolicy, "auth-policy", "OnPremise", "An email of the enrolling user when using the Deeplink ('/deeplink')")
flag.StringVar(&profileDir, "mdm-profile-dir", "./profile", "The MDM policy directory contains the SyncML MDM profile commmands to enforce to enrolled devices")
flag.StringVar(&staticDir, "static-dir", "./static", "The directory to serve static files")
flag.BoolVar(&verbose, "verbose", false, "HTTP traffic dump")
flag.Parse()
// Verify authPolicy is valid
if authPolicy != "Federated" && authPolicy != "OnPremise" {
panic("unsupported authpolicy")
}
// Checking if profile directory exists
_, err := os.Stat(profileDir)
if err != nil {
if os.IsNotExist(err) {
panic("profile directory does not exists")
} else {
panic(err)
}
}
// Checking if static directory exists
_, err = os.Stat(staticDir)
if err != nil {
if os.IsNotExist(err) {
panic("static directory does not exists")
} else {
panic(err)
}
}
// Create HTTP request router
r := mux.NewRouter()
// MS-MDE and MS-MDM endpoints
r.Path("/EnrollmentServer/Discovery.svc").Methods("GET", "POST").HandlerFunc(DiscoveryHandler)
r.Path("/EnrollmentServer/Policy.svc").Methods("POST").HandlerFunc(PolicyHandler)
r.Path("/EnrollmentServer/Enrollment.svc").Methods("POST").HandlerFunc(EnrollHandler)
r.Path("/ManagementServer/MDM.svc").Methods("POST").HandlerFunc(ManageHandler)
// Static root endpoint
r.Path("/").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.Write([]byte(`<center><h1>FleetDM Windows MDM Demo Server<br></h1>.<center>`))
w.Write([]byte(`<br><center><img src="https://fleetdm.com/images/press-kit/fleet-logo-dark-rgb.png"></center>`))
})
// Static file serve
fileServer := http.FileServer(http.Dir(staticDir))
r.PathPrefix("/").Handler(http.StripPrefix("/static", fileServer))
// Start HTTPS Server
fmt.Println("HTTPS server listening on port 443")
err = http.ListenAndServeTLS(":443", "./certs/dev_cert_mdmwindows_com_cert.pem", "./certs/dev_cert_mdmwindows_com.key", globalHandler(r))
if err != nil {
panic(err)
}
}
// drainBody reads all of bytes to memory and then returns two equivalent
// ReadClosers yielding the same bytes.
//
// It returns an error if the initial slurp of all bytes fails. It does not attempt
// to make the returned ReadClosers have identical error-matching behavior.
func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, body []byte, err error) {
if b == nil || b == http.NoBody {
// No copying needed. Preserve the magic sentinel meaning of NoBody.
return http.NoBody, http.NoBody, nil, nil
}
var buf bytes.Buffer
if _, err = buf.ReadFrom(b); err != nil {
return nil, b, nil, err
}
if err = b.Close(); err != nil {
return nil, b, nil, err
}
return io.NopCloser(&buf), io.NopCloser(bytes.NewReader(buf.Bytes())), buf.Bytes(), nil
}
// global HTTP handler to log input and output https traffic
func globalHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if verbose {
// grabbing Input Header and Body
reqHeader, err := httputil.DumpRequest(r, false)
if err != nil {
panic(err)
}
var bodyBytes []byte
reqBodySave := r.Body
if r.Body != nil {
reqBodySave, r.Body, bodyBytes, err = drainBody(r.Body)
if err != nil {
panic(err)
}
}
r.Body = reqBodySave
var beautifiedReqBody string
if len(bodyBytes) > 0 {
beautifiedReqBody = xmlfmt.FormatXML(string(bodyBytes), " ", " ")
}
fmt.Printf("\n\n============================= Input Request =============================\n")
fmt.Println("----------- Input Header -----------\n", string(reqHeader))
if len(beautifiedReqBody) > 0 {
fmt.Println("----------- Input Body -----------\n", string(beautifiedReqBody))
} else {
fmt.Printf("----------- Empty Input Body -----------\n")
}
fmt.Printf("=========================================================================\n\n\n")
}
rec := httptest.NewRecorder()
h.ServeHTTP(rec, r)
if verbose {
// grabbing Output Header and Body
var beautifiedResponseBody string
responseBody := rec.Body.Bytes()
if len(responseBody) > 0 {
beautifiedResponseBody = xmlfmt.FormatXML(string(responseBody), " ", " ")
}
responseHeader, err := httputil.DumpResponse(rec.Result(), false)
if err != nil {
panic(err)
}
fmt.Printf("\n\n============================= Output Response =============================\n")
fmt.Println("----------- Response Header -----------\n", string(responseHeader))
if len(beautifiedResponseBody) > 0 {
fmt.Println("----------- Response Body -----------\n", string(beautifiedResponseBody))
} else {
fmt.Printf("----------- Empty Response Body -----------\n")
}
fmt.Printf("=========================================================================\n\n\n")
}
// we copy the captured response headers to our new response
for k, v := range rec.Header() {
w.Header()[k] = v
}
w.Write(rec.Body.Bytes())
})
}

View file

@ -1,65 +0,0 @@
package main
import (
"io/ioutil"
"net/http"
"regexp"
"strconv"
"strings"
)
// DiscoveryHandler is the HTTP handler assosiated with the enrollment protocol's discovery endpoint.
func DiscoveryHandler(w http.ResponseWriter, r *http.Request) {
// Return HTTP Status 200 Ok when a HTTP GET request is received.
if r.Method == http.MethodGet {
w.WriteHeader(http.StatusOK)
return
}
// Read The HTTP Request body
bodyRaw, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
body := string(bodyRaw)
// Retrieve the MessageID From The Body For The Response
messageID := strings.Replace(strings.Replace(regexp.MustCompile(`<a:MessageID>[\s\S]*?<\/a:MessageID>`).FindStringSubmatch(body)[0], "<a:MessageID>", "", -1), "</a:MessageID>", "", -1)
var extraParams = ""
if authPolicy == "Federated" {
extraParams += "<AuthenticationServiceUrl>https://" + domain + "/EnrollmentServer/Auth</AuthenticationServiceUrl>"
}
// Create response payload
response := []byte(`
<s:Envelope
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.microsoft.com/windows/management/2012/01/enrollment/IDiscoveryService/DiscoverResponse</a:Action>
<ActivityId CorrelationId="8c6060c4-3d78-4d73-ae17-e8bce88426ee"
xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">8c6060c4-3d78-4d73-ae17-e8bce88426ee
</ActivityId>
<a:RelatesTo>` + messageID + `</a:RelatesTo>
</s:Header>
<s:Body>
<DiscoverResponse
xmlns="http://schemas.microsoft.com/windows/management/2012/01/enrollment">
<DiscoverResult>
<AuthPolicy>` + authPolicy + `</AuthPolicy>
<EnrollmentVersion>4.0</EnrollmentVersion>
<EnrollmentPolicyServiceUrl>https://` + domain + `/EnrollmentServer/Policy.svc</EnrollmentPolicyServiceUrl>
<EnrollmentServiceUrl>https://` + domain + `/EnrollmentServer/Enrollment.svc</EnrollmentServiceUrl>
` + extraParams + `
</DiscoverResult>
</DiscoverResponse>
</s:Body>
</s:Envelope>`)
// Return response body
w.Header().Set("Content-Type", "application/soap+xml; charset=utf-8")
w.Header().Set("Content-Length", strconv.Itoa(len(response)))
w.Write(response)
}

View file

@ -1,237 +0,0 @@
package main
import (
"crypto/rand"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"fmt"
"io/ioutil"
"math/big"
mathrand "math/rand"
"net/http"
"regexp"
"strconv"
"strings"
"time"
)
// EnrollHandler is the HTTP handler assosiated with the enrollment protocol's enrollment endpoint.
func EnrollHandler(w http.ResponseWriter, r *http.Request) {
// Read The HTTP Request body
bodyRaw, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
body := string(bodyRaw)
// Retrieve the MessageID From The Body For The Response
messageID := strings.Replace(strings.Replace(regexp.MustCompile(`<a:MessageID>[\s\S]*?<\/a:MessageID>`).FindStringSubmatch(body)[0], "<a:MessageID>", "", -1), "</a:MessageID>", "", -1)
// Retrieve the BinarySecurityToken (which contains a Certificate Signing Request) From The Body For The Response
binarySecurityToken := strings.Replace(strings.Replace(regexp.MustCompile(`<wsse:BinarySecurityToken ValueType="http:\/\/schemas.microsoft.com\/windows\/pki\/2009\/01\/enrollment#PKCS10" EncodingType="http:\/\/docs\.oasis-open\.org\/wss\/2004\/01\/oasis-200401-wss-wssecurity-secext-1\.0\.xsd#base64binary">[\s\S]*?<\/wsse:BinarySecurityToken>`).FindStringSubmatch(body)[0], `<wsse:BinarySecurityToken ValueType="http://schemas.microsoft.com/windows/pki/2009/01/enrollment#PKCS10" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#base64binary">`, "", -1), "</wsse:BinarySecurityToken>", "", -1)
// Retrieve the DeviceID From The Body For The Response
deviceID := strings.Replace(strings.Replace(regexp.MustCompile(`<ac:ContextItem Name="DeviceID"><ac:Value>[\s\S]*?<\/ac:Value><\/ac:ContextItem>`).FindStringSubmatch(body)[0], `<ac:ContextItem Name="DeviceID"><ac:Value>`, "", -1), "</ac:Value></ac:ContextItem>", "", -1)
// Retrieve the EnrollmentType From The Body For The Response
enrollmentType := strings.Replace(strings.Replace(regexp.MustCompile(`<ac:ContextItem Name="EnrollmentType"><ac:Value>[\s\S]*?<\/ac:Value><\/ac:ContextItem>`).FindStringSubmatch(body)[0], `<ac:ContextItem Name="EnrollmentType"><ac:Value>`, "", -1), "</ac:Value></ac:ContextItem>", "", -1)
/* Sign binary security token */
// Load raw Root CA
rootCertificateDer, err := ioutil.ReadFile("./identity/identity.crt")
if err != nil {
panic(err)
}
rootPrivateKeyDer, err := ioutil.ReadFile("./identity/identity.key")
if err != nil {
panic(err)
}
// Convert the raw Root CA cert & key to parsed version
rootCert, err := x509.ParseCertificate(rootCertificateDer)
if err != nil {
panic(err)
}
rootPrivateKey, err := x509.ParsePKCS1PrivateKey(rootPrivateKeyDer)
if err != nil {
panic(err)
}
// Decode Base64
csrRaw, err := base64.StdEncoding.DecodeString(binarySecurityToken)
if err != nil {
panic(err)
}
// Decode and verify CSR
csr, err := x509.ParseCertificateRequest(csrRaw)
if err != nil {
panic(err)
}
if err = csr.CheckSignature(); err != nil {
panic(err)
}
// Create client identity certificate
NotBefore1 := time.Now().Add(time.Duration(mathrand.Int31n(120)) * -time.Minute) // This randomises the creation time a bit for added security (Recommended by x509 signing article not the MDM spec)
clientCertificate := &x509.Certificate{
Signature: csr.Signature,
SignatureAlgorithm: csr.SignatureAlgorithm,
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
PublicKey: csr.PublicKey,
SerialNumber: big.NewInt(2),
Issuer: rootCert.Issuer,
Subject: pkix.Name{
CommonName: deviceID,
}, // The Subject is not used from the CSR because the characters in it are causing issues.
NotBefore: NotBefore1,
NotAfter: NotBefore1.Add(365 * 24 * time.Hour),
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
// Sign certificate with the identity
clientCRTRaw, err := x509.CreateCertificate(rand.Reader, clientCertificate, rootCert, csr.PublicKey, rootPrivateKey)
if err != nil {
panic(err)
}
// Note: SHA-1 Hash OID is deprecated
// Fingerprint (SHA-1 hash) of client certificate
h := sha1.New()
h.Write(clientCRTRaw)
signedClientCertFingerprint := strings.ToUpper(fmt.Sprintf("%x", h.Sum(nil))) // TODO: Cleanup -> This line is probally messer than it needs to be
// Fingerprint (SHA-1 hash) of client certificate
h2 := sha1.New()
h2.Write(rootCertificateDer)
identityCertFingerprint := strings.ToUpper(fmt.Sprintf("%x", h2.Sum(nil))) // TODO: Cleanup -> This line is probally messer than it needs to be
// Determain Certstore
certStore := "User"
if enrollmentType == "Device" {
certStore = "System"
}
// End Sign binary security token
// Generate WAP provisioning profile for inside the payload
wapProvisionProfile := `
<?xml version="1.0" encoding="UTF-8"?>
<wap-provisioningdoc version="1.1">
<characteristic type="CertificateStore">
<characteristic type="Root">
<characteristic type="System">
<characteristic type="` + identityCertFingerprint /* Root CA Certificate Fingureprint (SHA-1 hash of Der) */ + `">
<parm name="EncodedCertificate" value="` + base64.StdEncoding.EncodeToString(rootCertificateDer) /* Base64 encoded root CA certificate */ + `" />
</characteristic>
</characteristic>
</characteristic>
<characteristic type="My">
<characteristic type="` + certStore + `">
<characteristic type="` + signedClientCertFingerprint /* Signed Client Certificate (From the BinarySecurityToken) Fingureprint (SHA-1 hash of Der) */ + `">
<parm name="EncodedCertificate" value="` + base64.StdEncoding.EncodeToString(clientCRTRaw) /* Base64 encoded signed certificate */ + `" />
</characteristic>
<characteristic type="PrivateKeyContainer" />
</characteristic>
<characteristic type="WSTEP">
<characteristic type="Renew">
<parm name="ROBOSupport" value="true" datatype="boolean"/>
<parm name="RenewPeriod" value="60" datatype="integer"/>
<parm name="RetryInterval" value="4" datatype="integer"/>
</characteristic>
</characteristic>
</characteristic>
</characteristic>
<characteristic type="APPLICATION">
<parm name="APPID" value="w7" />
<parm name="PROVIDER-ID" value="DEMO MDM" />
<parm name="NAME" value="FleetDM Demo Server - Windows" />
<parm name="ADDR" value="https://` + domain + `/ManagementServer/MDM.svc" />
<parm name="ServerList" value="https://` + domain + `/ManagementServer/ServerList.svc" />
<parm name="ROLE" value="4294967295" />
<parm name="BACKCOMPATRETRYDISABLED" />
<parm name="CONNRETRYFREQ" value="6" />
<parm name="INITIALBACKOFFTIME" value="30000" />
<parm name="MAXBACKOFFTIME" value="120000" />
<parm name="DEFAULTENCODING" value="application/vnd.syncml.dm+xml" />
<characteristic type="APPAUTH">
<parm name="AAUTHLEVEL" value="CLIENT" />
<parm name="AAUTHTYPE" value="DIGEST" />
<parm name="AAUTHSECRET" value="dummy" />
<parm name="AAUTHDATA" value="nonce" />
</characteristic>
<characteristic type="APPAUTH">
<parm name="AAUTHLEVEL" value="APPSRV" />
<parm name="AAUTHTYPE" value="DIGEST" />
<parm name="AAUTHNAME" value="dummy" />
<parm name="AAUTHSECRET" value="dummy" />
<parm name="AAUTHDATA" value="nonce" />
</characteristic>
</characteristic>
<characteristic type="DMClient">
<characteristic type="Provider">
<characteristic type="DEMO MDM">
<characteristic type="Poll">
<parm name="NumberOfFirstRetries" value="8" datatype="integer" />
<parm name="IntervalForFirstSetOfRetries" value="15" datatype="integer" />
<parm name="NumberOfSecondRetries" value="5" datatype="integer" />
<parm name="IntervalForSecondSetOfRetries" value="3" datatype="integer" />
<parm name="NumberOfRemainingScheduledRetries" value="0" datatype="integer" />
<parm name="IntervalForRemainingScheduledRetries" value="1560" datatype="integer" />
<parm name="PollOnLogin" value="true" datatype="boolean" />
</characteristic>
</characteristic>
</characteristic>
</characteristic>
</wap-provisioningdoc>`
wapProvisionProfileRaw := []byte(strings.ReplaceAll(strings.ReplaceAll(wapProvisionProfile, "\n", ""), "\t", ""))
// Create response payload
response := []byte(`
<s:Envelope
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.microsoft.com/windows/pki/2009/01/enrollment/RSTRC/wstep</a:Action>
<a:RelatesTo>` + messageID + `</a:RelatesTo>
<o:Security
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
<u:Timestamp u:Id="_0">
<u:Created>2018-11-30T00:32:59.420Z</u:Created>
<u:Expires>2018-12-30T00:37:59.420Z</u:Expires>
</u:Timestamp>
</o:Security>
</s:Header>
<s:Body>
<RequestSecurityTokenResponseCollection
xmlns="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<RequestSecurityTokenResponse>
<TokenType>http://schemas.microsoft.com/5.0.0.0/ConfigurationManager/Enrollment/DeviceEnrollmentToken</TokenType>
<DispositionMessage
xmlns="http://schemas.microsoft.com/windows/pki/2009/01/enrollment">
</DispositionMessage>
<RequestedSecurityToken>
<BinarySecurityToken
xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" ValueType="http://schemas.microsoft.com/5.0.0.0/ConfigurationManager/Enrollment/DeviceEnrollmentProvisionDoc" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#base64binary">` + base64.StdEncoding.EncodeToString(wapProvisionProfileRaw) + `
</BinarySecurityToken>
</RequestedSecurityToken>
<RequestID
xmlns="http://schemas.microsoft.com/windows/pki/2009/01/enrollment">0
</RequestID>
</RequestSecurityTokenResponse>
</RequestSecurityTokenResponseCollection>
</s:Body>
</s:Envelope>`)
// Return response body
w.Header().Set("Content-Type", "application/soap+xml; charset=utf-8")
w.Header().Set("Content-Length", strconv.Itoa(len(response)))
w.Write(response)
}

View file

@ -1,94 +0,0 @@
package main
import (
"io/ioutil"
"net/http"
"regexp"
"strconv"
"strings"
)
// PolicyHandler is the HTTP handler assosiated with the enrollment protocol's policy endpoint.
func PolicyHandler(w http.ResponseWriter, r *http.Request) {
// Read The HTTP Request body
bodyRaw, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
body := string(bodyRaw)
// Retrieve the MessageID From The Body For The Response
messageID := strings.Replace(strings.Replace(regexp.MustCompile(`<a:MessageID>[\s\S]*?<\/a:MessageID>`).FindStringSubmatch(body)[0], "<a:MessageID>", "", -1), "</a:MessageID>", "", -1)
response := []byte(`
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.microsoft.com/windows/pki/2009/01/enrollmentpolicy/IPolicy/GetPoliciesResponse</a:Action>
<a:RelatesTo>` + messageID + `</a:RelatesTo>
</s:Header>
<s:Body
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<GetPoliciesResponse xmlns="http://schemas.microsoft.com/windows/pki/2009/01/enrollmentpolicy">
<response>
<policyID />
<policyFriendlyName xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<nextUpdateHours xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<policiesNotChanged xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<policies>
<policy>
<policyOIDReference>0</policyOIDReference>
<cAs xsi:nil="true" />
<attributes>
<commonName>CEPUnitTest</commonName>
<policySchema>3</policySchema>
<certificateValidity>
<validityPeriodSeconds>1209600</validityPeriodSeconds>
<renewalPeriodSeconds>172800</renewalPeriodSeconds>
</certificateValidity>
<permission>
<enroll>true</enroll>
<autoEnroll>false</autoEnroll>
</permission>
<privateKeyAttributes>
<minimalKeyLength>2048</minimalKeyLength>
<keySpec xsi:nil="true" />
<keyUsageProperty xsi:nil="true" />
<permissions xsi:nil="true" />
<algorithmOIDReference xsi:nil="true" />
<cryptoProviders xsi:nil="true" />
</privateKeyAttributes>
<revision>
<majorRevision>101</majorRevision>
<minorRevision>0</minorRevision>
</revision>
<supersededPolicies xsi:nil="true" />
<privateKeyFlags xsi:nil="true" />
<subjectNameFlags xsi:nil="true" />
<enrollmentFlags xsi:nil="true" />
<generalFlags xsi:nil="true" />
<hashAlgorithmOIDReference>0</hashAlgorithmOIDReference>
<rARequirements xsi:nil="true" />
<keyArchivalAttributes xsi:nil="true" />
<extensions xsi:nil="true" />
</attributes>
</policy>
</policies>
</response>
<oIDs>
<oID>
<value>1.3.14.3.2.29</value>
<group>1</group>
<oIDReferenceID>0</oIDReferenceID>
<defaultName> szOID_NIST_sha256</defaultName>
</oID>
</oIDs>
</GetPoliciesResponse>
</s:Body>
</s:Envelope>`)
// Return response body
w.Header().Set("Content-Type", "application/soap+xml; charset=utf-8")
w.Header().Set("Content-Length", strconv.Itoa(len(response)))
w.Write(response)
}

View file

@ -1,311 +0,0 @@
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"strconv"
"strings"
)
// SyncML XML Parsing Types - This needs to be improved
type SyncMLHeader struct {
DTD string `xml:"VerDTD"`
Version string `xml:"VerProto"`
SessionID int `xml:"SessionID"`
MsgID int `xml:"MsgID"`
Target string `xml:"Target>LocURI"`
Source string `xml:"Source>LocURI"`
MaxMsgSize int `xml:"Meta>A:MaxMsgSize"`
}
type SyncMLCommandMeta struct {
XMLinfo string `xml:"xmlns,attr"`
Type string `xml:"Type"`
}
type SyncMLCommandItem struct {
Meta SyncMLCommandMeta `xml:"Meta"`
Source string `xml:"Source>LocURI"`
Data string `xml:"Data"`
}
type SyncMLCommand struct {
XMLName xml.Name
CmdID int `xml:",omitempty"`
MsgRef string `xml:",omitempty"`
CmdRef string `xml:",omitempty"`
Cmd string `xml:",omitempty"`
Target string `xml:"Target>LocURI"`
Source string `xml:"Source>LocURI"`
Data string `xml:",omitempty"`
Item []SyncMLCommandItem `xml:",any"`
}
type SyncMLBody struct {
Item []SyncMLCommand `xml:",any"`
}
type SyncMLMessage struct {
XMLinfo string `xml:"xmlns,attr"`
Header SyncMLHeader `xml:"SyncHdr"`
Body SyncMLBody `xml:"SyncBody"`
}
// Returns the MDM configuration profile SyncML content from profile dir
func getConfigurationProfiles(cmdIDstart int) string {
files, err := ioutil.ReadDir(profileDir)
if err != nil {
panic(err)
}
var syncmlCommands string
var tokenCmdID string = "xxcmdidxx"
for _, file := range files {
fileContent, err := os.ReadFile(profileDir + "/" + file.Name())
if err != nil {
panic(err)
}
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
sanitizedSyncmlOutput := strings.ReplaceAll(syncmlCommands, "\r\n", "\n")
if len(sanitizedSyncmlOutput) > 0 {
fmt.Print("\n")
}
return sanitizedSyncmlOutput
}
// Alert Command IDs
const DeviceUnenrollmentID = "1226"
const HostInitMessageID = "1201"
// Checks if body contains a DM device unrollment SyncML message
func isDeviceUnenrollmentMessage(body SyncMLBody) bool {
for _, element := range body.Item {
if element.Data == DeviceUnenrollmentID {
return true
}
}
return false
}
// Checks if body contains a DM session initialization SyncML message sent by device
func isSessionInitializationMessage(body SyncMLBody) bool {
isUnenrollMessage := isDeviceUnenrollmentMessage(body)
for _, element := range body.Item {
if element.Data == HostInitMessageID && !isUnenrollMessage {
return true
}
}
return false
}
// Get IP address from HTTP Request
func getIP(r *http.Request) (string, error) {
//Get IP from the X-REAL-IP header
ip := r.Header.Get("X-REAL-IP")
netIP := net.ParseIP(ip)
if netIP != nil {
return ip, nil
}
//Get IP from X-FORWARDED-FOR header
ips := r.Header.Get("X-FORWARDED-FOR")
splitIps := strings.Split(ips, ",")
for _, ip := range splitIps {
netIP := net.ParseIP(ip)
if netIP != nil {
return ip, nil
}
}
//Get IP from RemoteAddr
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return "", err
}
netIP = net.ParseIP(ip)
if netIP != nil {
return ip, nil
}
return "", fmt.Errorf("no valid ip found")
}
// ManageHandler is the HTTP handler assosiated with the mdm management service. This is what constantly pushes configuration profiles to the device.
func ManageHandler(w http.ResponseWriter, r *http.Request) {
// Read The HTTP Request body
bodyRaw, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
var responseRaw []byte
var response string
var message SyncMLMessage
//Parsing input SyncML message
if err := xml.Unmarshal(bodyRaw, &message); err != nil {
panic(err)
}
// Cmd ID variable with getNextCmdID() increment statement hack
CmdID := 0
getNextCmdID := func(i *int) string { *i++; return strconv.Itoa(*i) }
// Retrieve the MessageID From The Body For The Response
DeviceID := message.Header.Source
// Retrieve the SessionID From The Body For The Response
SessionID := message.Header.SessionID
// Retrieve the MsgID From The Body For The Response
MsgID := message.Header.MsgID
//Only handle DM session initialization SyncML message sent by device
// Retrieve the IP Address from calling device
ipAddressBytes, err := getIP(r)
if err != nil {
panic(err)
}
//Checking the SyncML message types
if isSessionInitializationMessage(message.Body) {
fmt.Printf("\n========= New OMA-DM session from Windows Host %s (%s) =========\n", string(ipAddressBytes), r.UserAgent())
// Create response payload - MDM syncml configuration profiles commands will be enforced here
response = `
<?xml version="1.0" encoding="UTF-8"?>
<SyncML xmlns="SYNCML:SYNCML1.2">
<SyncHdr>
<VerDTD>1.2</VerDTD>
<VerProto>DM/1.2</VerProto>
<SessionID>` + strconv.Itoa(SessionID) + `</SessionID>
<MsgID>` + strconv.Itoa(MsgID) + `</MsgID>
<Target>
<LocURI>` + DeviceID + `</LocURI>
</Target>
<Source>
<LocURI>https://` + domain + `/ManagementServer/MDM.svc</LocURI>
</Source>
</SyncHdr>
<SyncBody>
<Status>
<CmdID>` + getNextCmdID(&CmdID) + `</CmdID>
<MsgRef>` + strconv.Itoa(MsgID) + `</MsgRef>
<CmdRef>0</CmdRef>
<Cmd>SyncHdr</Cmd>
<Data>200</Data>
</Status>
<Status>
<CmdID>` + getNextCmdID(&CmdID) + `</CmdID>
<MsgRef>` + strconv.Itoa(MsgID) + `</MsgRef>
<CmdRef>2</CmdRef>
<Cmd>Alert</Cmd>
<Data>200</Data>
</Status>
<Status>
<CmdID>` + getNextCmdID(&CmdID) + `</CmdID>
<MsgRef>` + strconv.Itoa(MsgID) + `</MsgRef>
<CmdRef>3</CmdRef>
<Cmd>Alert</Cmd>
<Data>200</Data>
</Status>
<Status>
<CmdID>` + getNextCmdID(&CmdID) + `</CmdID>
<MsgRef>` + strconv.Itoa(MsgID) + `</MsgRef>
<CmdRef>4</CmdRef>
<Cmd>Replace</Cmd>
<Data>200</Data>
</Status>
` + getConfigurationProfiles(CmdID) + `
<Final />
</SyncBody>
</SyncML>`
// Return response
responseRaw = []byte(strings.ReplaceAll(strings.ReplaceAll(response, "\n", ""), "\t", ""))
w.Header().Set("Content-Type", "application/vnd.syncml.dm+xml")
w.Header().Set("Content-Length", strconv.Itoa(len(response)))
w.Write(responseRaw)
} else {
//Log if this is a device unrollment message
if isDeviceUnenrollmentMessage(message.Body) {
fmt.Printf("\nWindows Device at %s was removed from MDM!\n\n", string(ipAddressBytes))
}
//Acknowledge the HTTP request sent by device
response = `
<?xml version="1.0" encoding="UTF-8"?>
<SyncML xmlns="SYNCML:SYNCML1.2">
<SyncHdr>
<VerDTD>1.2</VerDTD>
<VerProto>DM/1.2</VerProto>
<SessionID>` + strconv.Itoa(SessionID) + `</SessionID>
<MsgID>` + strconv.Itoa(MsgID) + `</MsgID>
<Target>
<LocURI>` + DeviceID + `</LocURI>
</Target>
<Source>
<LocURI>https://` + domain + `/ManagementServer/MDM.svc</LocURI>
</Source>
</SyncHdr>
<SyncBody>
<Status>
<CmdID>` + getNextCmdID(&CmdID) + `</CmdID>
<MsgRef>` + strconv.Itoa(MsgID) + `</MsgRef>
<CmdRef>0</CmdRef>
<Cmd>SyncHdr</Cmd>
<Data>200</Data>
</Status>
<Final />
</SyncBody>
</SyncML>`
// Dump Response Payload
for _, element := range message.Body.Item {
if element.XMLName.Local != "Final" && element.Cmd != "SyncHdr" {
commandStr, _ := xml.MarshalIndent(element, "", " ")
if element.XMLName.Local == "Status" {
fmt.Printf("\n--------- Command Response %s - Return Code: %s ---------\n", element.CmdRef, element.Data)
} else {
fmt.Printf("%s\n", commandStr)
}
}
}
// Return response body
responseRaw = []byte(strings.ReplaceAll(strings.ReplaceAll(response, "\n", ""), "\t", ""))
w.Header().Set("Content-Type", "application/vnd.syncml.dm+xml")
w.Header().Set("Content-Length", strconv.Itoa(len(response)))
w.Write(responseRaw)
}
}

File diff suppressed because one or more lines are too long

View file

@ -1,62 +0,0 @@
package main
import (
"bufio"
"fmt"
"os"
"path"
)
var (
sourceFilePath = path.Join(os.Getenv("GOROOT"), "src", "encoding", "asn1", "asn1.go")
patchedFilePath = path.Join(os.Getenv("GOROOT"), "src", "encoding", "asn1", "asn1-patched.go")
)
func main() {
// Check for the GOROOT env varible. Should be set by Go automatically
if os.Getenv("GOROOT") == "" {
panic("Plese set your GOROOT path")
}
// Load The file and create a scanner
file, err := os.Open(sourceFilePath)
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(file)
// Open Output File
out, err2 := os.Create(patchedFilePath)
if err2 != nil {
panic(err2)
}
// Loop of each line of the file checking it
for scanner.Scan() {
out.Write(scanner.Bytes())
out.Write([]byte("\n"))
if scanner.Text() == " b == '?' ||" {
scanner.Scan()
if scanner.Text() != " b == '!' || // Windows MDM Certificate Parsing Patch" {
out.Write([]byte(" b == '!' || // Windows MDM Certificate Parsing Patch\n"))
out.Write([]byte(" b == 0 || // Windows MDM Certificate Parsing Patch\n"))
}
out.Write(scanner.Bytes())
out.Write([]byte("\n"))
}
}
// Close writters
file.Close()
out.Close()
// Replace the main file with the patched one
if err := os.Rename(patchedFilePath, sourceFilePath); err != nil {
panic(err)
}
// Success
fmt.Println("Patch Applied To Your Go Sources! Please be carefull with the certs you are loading as they could cause undesired outcomes in the future.")
}

View file

@ -1,13 +0,0 @@
<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>

View file

@ -1,24 +0,0 @@
<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>

View file

@ -1,13 +0,0 @@
<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>

View file

@ -1,13 +0,0 @@
<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>

View file

@ -1,13 +0,0 @@
<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>

View file

@ -1,8 +0,0 @@
<Get>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Vendor/MSFT/CertificateStore/Root/System?list=StructData</LocURI>
</Target>
</Item>
</Get>

View file

@ -1,8 +0,0 @@
<Get>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/DeviceManageability/Capabilities/CSPVersions</LocURI>
</Target>
</Item>
</Get>

View file

@ -1,8 +0,0 @@
<Get>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./DevDetail/Ext/Microsoft/DeviceName</LocURI>
</Target>
</Item>
</Get>

View file

@ -1,8 +0,0 @@
<Get>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./DevDetail/HwV</LocURI>
</Target>
</Item>
</Get>

View file

@ -1,8 +0,0 @@
<Get>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./DevDetail/Ext/Microsoft/LocalTime</LocURI>
</Target>
</Item>
</Get>

View file

@ -1,8 +0,0 @@
<Get>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./DevDetail/Ext/Microsoft/OSPlatform</LocURI>
</Target>
</Item>
</Get>

View file

@ -1,8 +0,0 @@
<Get>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./DevDetail/SwV</LocURI>
</Target>
</Item>
</Get>

View file

@ -1,8 +0,0 @@
<Get>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./DevDetail/Ext/Microsoft/TotalStorage</LocURI>
</Target>
</Item>
</Get>

View file

@ -1,38 +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>
<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>

View file

@ -1,13 +0,0 @@
<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>

View file

@ -1,13 +0,0 @@
<Replace>
<CmdID>xxcmdidxx</CmdID>
<Item>
<Target>
<LocURI>./Vendor/MSFT/Personalization/LockScreenImageUrl</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>

View file

@ -1 +0,0 @@
world