Windows MDM Fix Manual Detection (#17721)
#15565 Replace the use of the isFederated registry key with a keys that check for AAD (Azure Active Directory, now Entra ID) Federated enrollment (`isFederated`) seems to be when windows uses a Discovery MDM endpoint to get its policy and management endpoint configuration. This is always the case when a client is enrolled with fleet, so installations always show up as automatic. It's being replaced by a different key, `AADResourceID`, which appears to identify the resource that controls the automated deployment. In my tests it only appears to be populated when the computer is enrolled through automated deployments. This key appears on both Windows 10 and 11. There is a similar key, `AADTenantID`, which appears to identify the client (tenant) to the Azure cloud. I haven't seen this ID in our systems, so it is likely exclusively used in Azure. Both this key and `AADResourceID` seem to always be set at the same time, so we only check for the `AADResourceID`. I've also added documentation on the registry keys I've analyzed for future reference.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- Fix a bug where all Windows MDM enrollments were detected as automatic
|
||||
@@ -1514,12 +1514,12 @@ func (a *agent) mdmWindows() []map[string]string {
|
||||
if !a.mdmEnrolled() {
|
||||
return []map[string]string{
|
||||
// empty service url means not enrolled
|
||||
{"is_federated": "0", "discovery_service_url": "", "provider_id": "", "installation_type": "Client"},
|
||||
{"aad_resource_id": "", "discovery_service_url": "", "provider_id": "", "installation_type": "Client"},
|
||||
}
|
||||
}
|
||||
return []map[string]string{
|
||||
{
|
||||
"is_federated": "0",
|
||||
"aad_resource_id": "",
|
||||
"discovery_service_url": a.serverAddress,
|
||||
"provider_id": fleet.WellKnownMDMFleet,
|
||||
"installation_type": "Client",
|
||||
|
||||
@@ -57,6 +57,88 @@ The certificate created through the WSTEP process is used to authenticate mTLS b
|
||||
|
||||
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-mdm/33769a92-ac31-47ef-ae7b-dc8501f7104f
|
||||
|
||||
## MDM Device Registration Summary
|
||||
|
||||
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dvrd/296ebf70-bd4b-489e-a531-460d8ef7519b
|
||||
|
||||
# Registry
|
||||
|
||||
- `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\`
|
||||
Each enrollment gets its own subdirectory with a UUID as a key,
|
||||
inside each directory is a set of keys associated with that enrollment
|
||||
- `CurCryptoProvider`
|
||||
Often `Microsoft Software Key Storage Provider`
|
||||
Cryptographic Key storage provider
|
||||
- `CurKeyContainer`
|
||||
Key within key provider
|
||||
- `DiscoveryServiceFullURL`
|
||||
MDM Discovery service URL
|
||||
- `DMPCertThumbPrint`
|
||||
According to [this blog post](https://call4cloud.nl/2022/10/fullmetal-certificate-the-revenge-of-renewal/), this is the thumbprint of your MDM device certificate
|
||||
- `EnrollmentFlags`
|
||||
See [this link](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xcep/cd22d3a0-f469-4a44-95ed-d10ce4dc2063) for details
|
||||
|
||||
| Integer value | Meaning |
|
||||
|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 0x00000001 | Instructs the client and CA to include an S/MIME extension, as specified in [RFC4262]. |
|
||||
| 0x00000008 | Instructs the CA to append the issued certificate to the userCertificate attribute, on the user object in Active Directory. |
|
||||
| 0x00000010 | Instructs the CA to check the user's userCertificate attribute in Active Directory, as specified in [RFC4523], for valid certificates that match the template enrolled for. |
|
||||
| 0x00000040 | This flag instructs clients to sign the renewal request using the private key of the existing certificate. For more information, see [MS-WCCE] section 3.2.2.6.2.1.4.5.6. This flag also instructs the CA to process the renewal requests as specified in [MS-WCCE] section 3.2.2.6.2.1.4.5.6. |
|
||||
| 0x00000100 | Instructs the client to get a user's consent before attempting to enroll for a certificate based on the specified template. |
|
||||
| 0x00000400 | Instructs the client to delete any expired, revoked, or renewed certificate from the user's certificate stores. |
|
||||
| 0x00002000 | This flag instructs the client to reuse the private key for a smart card–based certificate renewal if it is unable to create a new private key on the card. |
|
||||
- `EnrollmentState`
|
||||
The best documentation we can find is [here](https://learn.microsoft.com/en-us/graph/api/resources/intune-shared-enrollmentstate?view=graph-rest-beta)
|
||||
|
||||
| Member | Value | Description |
|
||||
|--------------|-------|--------------------------------------------------------------------------------------------------------------------|
|
||||
| unknown | 0 | Device enrollment state is unknown |
|
||||
| enrolled | 1 | Device is Enrolled. |
|
||||
| pendingReset | 2 | Enrolled but it's enrolled via enrollment profile and the enrolled profile is different from the assigned profile. |
|
||||
| failed | 3 | Not enrolled and there is enrollment failure record. |
|
||||
| notContacted | 4 | Device is imported but not enrolled. |
|
||||
| blocked | 5 | Device is enrolled as userless, but is blocked from moving to user enrollment because the app failed to install. |
|
||||
|
||||
- `EnrollmentType`
|
||||
According to [this PDF](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-MDE2/%5BMS-MDE2%5D.pdf) it can have three different values.
|
||||
|
||||
Device, Full, and AppManaged
|
||||
|
||||
From what I've seen, value 6 on AAD, 1 on manual
|
||||
- `isFederated`
|
||||
According to [this web page](https://learn.microsoft.com/en-us/windows/client-management/federated-authentication-device-enrollment), being federated means that the MDM
|
||||
endpoints and details were fetched from a Discovery endpoint,
|
||||
instead of being manually installed. The page does not make mention
|
||||
of the specific registry key, but we are making an assumption that
|
||||
it means the same thing.
|
||||
- `ProviderID`
|
||||
Set during enrollment. In our case it's the word "Fleet".
|
||||
- `RenewalPeriod`
|
||||
Set during enrollment. Period to renew WSTEP certificate.
|
||||
- `RenewErrorCode`
|
||||
Presumably set if there is an error renewing WSTEP certificate.
|
||||
- `RenewROBOSupport`
|
||||
According to [this post](https://call4cloud.nl/2022/10/fullmetal-certificate-the-revenge-of-renewal/) this means "Request On Behalf Of".
|
||||
It seems to have to do with automatic certificate renewal
|
||||
- `RenewStatus`
|
||||
Status of the renewal
|
||||
- `RenewTimestamp`
|
||||
Presumably the timestamp of the last renewal
|
||||
- `RootCertThumbPrint`
|
||||
The thumbprint of the WSTEP root certificate
|
||||
- `SID`
|
||||
Security Identifier
|
||||
- `UPN`
|
||||
User Principal Name of the user that enrolled the device
|
||||
- `AADResourceID`
|
||||
Appears to be the domain of the server managing the enrollment,
|
||||
always appears to be present on machines enrolled through Microsoft
|
||||
Entra (Azure Active Directory)
|
||||
- `AADTenantID`
|
||||
Also related to Azure Active Directory, and also appears to be
|
||||
present at the same time as AADResourceID.
|
||||
- `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\Diagnostics\AutoPilot`
|
||||
Autopilot provisioning diagnostic data
|
||||
|
||||
|
||||
<meta name="pageOrderInSection" value="2900">
|
||||
<meta name="pageOrderInSection" value="2900">
|
||||
|
||||
@@ -176,10 +176,10 @@ WITH registry_keys AS (
|
||||
enrollment_info AS (
|
||||
SELECT
|
||||
MAX(CASE WHEN name = 'UPN' THEN data END) AS upn,
|
||||
MAX(CASE WHEN name = 'IsFederated' THEN data END) AS is_federated,
|
||||
MAX(CASE WHEN name = 'DiscoveryServiceFullURL' THEN data END) AS discovery_service_url,
|
||||
MAX(CASE WHEN name = 'ProviderID' THEN data END) AS provider_id,
|
||||
MAX(CASE WHEN name = 'EnrollmentState' THEN data END) AS state
|
||||
MAX(CASE WHEN name = 'EnrollmentState' THEN data END) AS state,
|
||||
MAX(CASE WHEN name = 'AADResourceID' THEN data END) AS aad_resource_id
|
||||
FROM registry_keys
|
||||
GROUP BY key
|
||||
),
|
||||
@@ -190,7 +190,7 @@ WITH registry_keys AS (
|
||||
LIMIT 1
|
||||
)
|
||||
SELECT
|
||||
e.is_federated,
|
||||
e.aad_resource_id,
|
||||
e.discovery_service_url,
|
||||
e.provider_id,
|
||||
i.installation_type
|
||||
@@ -374,7 +374,7 @@ SELECT * FROM os_version LIMIT 1
|
||||
- Query:
|
||||
```sql
|
||||
SELECT os.name, r.data as display_version, k.version
|
||||
FROM
|
||||
FROM
|
||||
registry r,
|
||||
os_version os,
|
||||
kernel_info k
|
||||
|
||||
@@ -473,10 +473,10 @@ var extraDetailQueries = map[string]DetailQuery{
|
||||
enrollment_info AS (
|
||||
SELECT
|
||||
MAX(CASE WHEN name = 'UPN' THEN data END) AS upn,
|
||||
MAX(CASE WHEN name = 'IsFederated' THEN data END) AS is_federated,
|
||||
MAX(CASE WHEN name = 'DiscoveryServiceFullURL' THEN data END) AS discovery_service_url,
|
||||
MAX(CASE WHEN name = 'ProviderID' THEN data END) AS provider_id,
|
||||
MAX(CASE WHEN name = 'EnrollmentState' THEN data END) AS state
|
||||
MAX(CASE WHEN name = 'EnrollmentState' THEN data END) AS state,
|
||||
MAX(CASE WHEN name = 'AADResourceID' THEN data END) AS aad_resource_id
|
||||
FROM registry_keys
|
||||
GROUP BY key
|
||||
),
|
||||
@@ -487,7 +487,7 @@ var extraDetailQueries = map[string]DetailQuery{
|
||||
LIMIT 1
|
||||
)
|
||||
SELECT
|
||||
e.is_federated,
|
||||
e.aad_resource_id,
|
||||
e.discovery_service_url,
|
||||
e.provider_id,
|
||||
i.installation_type
|
||||
@@ -1612,7 +1612,7 @@ func directIngestMDMWindows(ctx context.Context, logger log.Logger, host *fleet.
|
||||
serverURL := data["discovery_service_url"]
|
||||
if serverURL != "" {
|
||||
enrolled = true
|
||||
if isFederated := data["is_federated"]; isFederated == "1" {
|
||||
if data["aad_resource_id"] != "" {
|
||||
// NOTE: We intentionally nest this condition to eliminate `enrolled == false && automatic == true`
|
||||
// as a possible status for Windows hosts (which would be otherwise be categorized as
|
||||
// "Pending"). Currently, the "Pending" status is supported only for macOS hosts.
|
||||
|
||||
@@ -692,7 +692,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "",
|
||||
"is_federated": "1",
|
||||
"aad_resource_id": "https://example.com",
|
||||
"provider_id": "Some_ID",
|
||||
"installation_type": "Client",
|
||||
},
|
||||
@@ -703,7 +703,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
wantServerURL: "",
|
||||
},
|
||||
{
|
||||
name: "off missing is_federated and server url",
|
||||
name: "off missing aad_resource_id and server url",
|
||||
data: []map[string]string{
|
||||
{
|
||||
"provider_id": "Some_ID",
|
||||
@@ -728,7 +728,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://example.com",
|
||||
"is_federated": "1",
|
||||
"aad_resource_id": "https://example.com",
|
||||
"provider_id": "Some_ID",
|
||||
"installation_type": "Client",
|
||||
},
|
||||
@@ -743,7 +743,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://example.com",
|
||||
"is_federated": "0",
|
||||
"aad_resource_id": "",
|
||||
"provider_id": "Local_Management",
|
||||
"installation_type": "Client",
|
||||
},
|
||||
@@ -754,7 +754,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
wantServerURL: "https://example.com",
|
||||
},
|
||||
{
|
||||
name: "on manual missing is_federated",
|
||||
name: "on manual missing aad_resource_id",
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://example.com",
|
||||
@@ -772,7 +772,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://example.com",
|
||||
"is_federated": "1",
|
||||
"aad_resource_id": "https://example.com",
|
||||
"provider_id": "Some_ID",
|
||||
"installation_type": "Windows SeRvEr 99.9",
|
||||
},
|
||||
@@ -790,7 +790,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://jumpcloud.com",
|
||||
"is_federated": "0",
|
||||
"aad_resource_id": "",
|
||||
"provider_id": "Local_Management",
|
||||
"installation_type": "Client",
|
||||
},
|
||||
@@ -806,7 +806,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://airwatch.com",
|
||||
"is_federated": "0",
|
||||
"aad_resource_id": "",
|
||||
"provider_id": "Local_Management",
|
||||
"installation_type": "Client",
|
||||
},
|
||||
@@ -822,7 +822,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://awmdm.com",
|
||||
"is_federated": "0",
|
||||
"aad_resource_id": "",
|
||||
"provider_id": "Local_Management",
|
||||
"installation_type": "Client",
|
||||
},
|
||||
@@ -838,7 +838,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://microsoft.com",
|
||||
"is_federated": "0",
|
||||
"aad_resource_id": "",
|
||||
"provider_id": "Local_Management",
|
||||
"installation_type": "Client",
|
||||
},
|
||||
@@ -854,7 +854,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://fleetdm.com",
|
||||
"is_federated": "0",
|
||||
"aad_resource_id": "",
|
||||
"provider_id": "Local_Management",
|
||||
"installation_type": "Client",
|
||||
},
|
||||
@@ -871,7 +871,7 @@ func TestDirectIngestMDMWindows(t *testing.T) {
|
||||
data: []map[string]string{
|
||||
{
|
||||
"discovery_service_url": "https://myinstall.local",
|
||||
"is_federated": "0",
|
||||
"aad_resource_id": "",
|
||||
"provider_id": "Fleet",
|
||||
"installation_type": "Client",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user