For a new software product that was being developed, we needed to be able to achieve Single-Sign-On (SSO) to other systems from 802.1X sessions with Network Policy Server (NPS), Microsoft’s RADIUS server implementation. (This was to be done without involving any integration with DHCP servers or SNMP to avoid taking dependencies and to minimise operational and deployment complexity.)
(One of these other systems, for example, was Palo Alto firewalls via the supplied User-Id API. To call this XML-based API, a client’s SAM compatible user name is provided along with a client’s IP address, as well as an assertion that either a log on or a log off event has occurred for that client, optionally, a time out too.)
To achieve this successfully, a client’s identity, its IP address and knowledge of when its session starts and when it ends must be ascertained so that appropriate events can be generated and appropriate calls in to other systems can be made. The best way to achieve this with NPS is by writing a Network Policy Server Extension.
Unfortunately, a major roadblock was hit along the way in trying to achieve this, uncovering a multifaceted identity spoofing security vulnerability in recent versions of NPS. Due to multiple product issues, this is a vulnerability that NPS now forces both upon consumers of its extensions API and also on to the wider RADIUS ecosystem in other products that are already in the market and deployed widely. These other products typically do not integrate with NPS directly via an extension but use the identity available from RADIUS accounting, SNMP or Syslog.
The core of the vulnerability can be traced back to a change in product behaviour that was made in Windows Server 2008 R2 with the introduction of EAP identity privacy support. As of this release, the value of the EAP-Response/Identity packet used by a NAS in the User-Name attribute in RADIUS Access-Request and Accounting-Request packets and the information that they make available via Syslog and SNMP can no longer be used as a source of identity information. This is because they now only contain the value of the EAP outer-identity where TLS-based EAP authentication is in use. This can be anonymised, spoofed or differ to the EAP inner-identity, the real identity of the client. Identity privacy is supported by EAP types such as PEAP.
For example, the EAP outer-identity could be anonymous@example.com or administrator@example.com, the EAP inner-identity actually being fred@example.com. This gives rise to a trivial identity spoofing security vulnerability if this value is used for access control, auditing or logging purposes, the scope of which is limited only by what the identity is subsequently used for by dependent systems.
(By impersonating another account, which can be higher privileged, this often allows a user to circumvent Web filtering restrictions, to falsely implicate another user for actions that they themselves have carried out or to get access to resources that their user account has no rights to.)
It is possible to configure identity privacy in many platforms such as Windows, OS X and iOS (via MobileConfig profile), and Android.
In Windows, this is from Windows 7 onwards. (The field is easily accessible so the barrier to entry is low.)
To get access to a client’s real identity when Accounting occurs, either the Authentication/Authorization that has previously occurred must conceptually be linked/bound to the Accounting, or the clients real identity must be returned to the NAS and the NAS must use this when Accounting so that the value of the User-Name attribute is correct.
(Where a revised identity is returned, this should also be used by a NAS in any session information that it makes available via Syslog or SNMP.
The first approach, binding, is most desirable as it maintains EAP identity privacy guarantees so this is the gold standard when designing a solution. This is explored first, as a primary concern, therefore. (The second approach is explored later on as a potential workaround.)
To perform such binding, the following process is conceptually required on the EAP terminating NPS instance:
- From the Authorization extension point where an Access-Accept will be generated, a client’s real identity is available in SAM compatible format to an NPS extension in the ratStrippedUserName extended RADIUS attribute. (This cannot have been spoofed or anonymised at this point.)
- From the Authorization extension point where an Accounting-Request packet has been received (Start, Stop, Interim-Update), a client’s IP address is available to an NPS extension as well as knowledge of when its session starts and when it ends. (For IPv4, the client’s IP address is available via the Framed-IP-Address attribute in Interim-Update Accounting-Request packets. For IPv6, client IP addresses are instead available via Framed-IPv6-Address attributes. They are made available by NASes that implement DHCP snooping functionality.)
The standards based, documented mechanism for achieving binding is the RADIUS Class attribute. (RFC 2865 and RFC 2866).
From RFC 2865:
“5.25. Class
Description
This Attribute is available to be sent by the server to the client in an Access-Accept and SHOULD be sent unmodified by the client to the accounting server as part of the Accounting-Request packet if accounting is supported.
The client MUST NOT interpret the attribute locally.”
A Class attribute is generated by NPS by default and is included in the Access-Accepts that it sends. (It is, in all but name, a session cookie.)
All subsequent accounting must then always include this Class attribute, which allows binding of Authentication/Authorization to Accounting to be performed against it.
The format of the Class attribute that is generated by NPS is documented by Microsoft as follows:
“Attribute: Class
ID: 25
Data type: TextRepresents the attribute sent to the client in an Access-Accept packet, which is useful for correlating Accounting-Request packets with authentication sessions. The format is:
- Type contains the value 25 (1 octet).
- Length contains a value of 20 or greater (1 octet).
- Checksum contains an Adler-32 checksum that is computed over the remainder of the Class attribute (4 octets).
- Vendor-ID contains the ID of the NAS vendor (4 octets). The high-order octet is 0 and the low-order 3 octets are the SMI Network Management Private Enterprise Code of the vendor in network byte order, as defined in “Private Enterprise Numbers” at http://www.iana.org/assignments/enterprise-numbers.
- Version contains the value of 1 (2 octets).
- Server-Address contains the IP address of the RADIUS server that issued the Access-Challenge message. For multihomed servers, this is the address of the network interface that received the original Access-Request message (2 octets).
- Service-Reboot-Time specifies the time at which the first serial number was returned (8 octets).
- Unique-Serial-Number contains a unique number to distinguish an individual connection attempt (8 octets).
- String contains information that is used to classify accounting records for additional analysis (0 or more octets). In NPS, the Class attribute is copied into the String field.
The Class attribute is used to match the accounting and authentication records if it is sent by the NAS in the Accounting-Request message. The combination of Serial-Number, Service-Reboot-Time, and Server-Address must be a unique identification for each authentication that the RADIUS server performs.”
(Poor aspects of this format is that it is a predictable construction that leaks information. It is also not apparent how the Server-Address field is handled when IPv6 is in use.)
The Class attribute is generally defined for NPS under the RADIUS_ATTRIBUTE_TYPE enumeration as ratClass and is documented by Microsoft as follows:
“Specifies a value that is provided to the NAS by the authentication provider. The NAS should use this value when communicating with the accounting provider. The value field in RADIUS_ATTRIBUTE for this type is a pointer. See RFC 2865 for more information.”
NPS defines two extension points, one for Authentication and one for Authorization.
The first fault in NPS that we encountered during the course of implementation was that the NPS generated RADIUS Class attribute (ratClass) is not made available to extensions at these points during the Authentication/Authorization process via the RadiusExtensionProcess2 API or the older generation RadiusExtensionProcessEx and RadiusExtensionProcess APIs. It should, both as documented and conceptually, be inserted at least before Authorization Extension DLLs have been called earlier on in the packet flow process, not after.
Microsoft clearly specify in their Invoking the Extension DLLs documentation that:
“In an Authorization DLL, RadiusExtensionProcess2 receives both the attributes generated by the NPS authorization service and the attributes generated from previously called Authorization DLLs.”
Had the generated Class attribute been inserted at the correct place, as documented, it would have allowed binding to be performed as the attribute would have been available to Authorization Extension DLLs at the point when an Access-Accept is pending to be sent in response. But, by inserting it after all extensions have been called, it does not allow for any binding to occur within an extension based on its value.
This is, therefore, a clear and unambiguous bug in Network Policy Server that causes extensions to be security vulnerable when they interact with RADIUS accounting information. It, for example, prohibits anybody from reliably and robustly implementing and integrating a Single Sign On / Federated Authentication system via the extensions API in a way where the solution can then be both widely distributed and immune to identity spoofing, while still meeting the privacy guarantees expected of EAP identity privacy. The same is true if the use case was auditing or logging the accounting information. (Potential workarounds, which turn out to be nuanced and complex, are explored later on.)
The NPS RADIUS packet flow diagram, originally provided by Microsoft, has been annotated in red to show the fault graphically and, hopefully, in a more clear manner:
Fault Isolation
In Wireshark, we can observe and validate that the RADIUS Class attribute is being sent in Access-Accepts to a NAS (switch or access point):
However, when a debugging mechanism is attached and is used to look at the contents of the response RADIUS_ATTRIBUTE_ARRAY at the Authorization extension point, we can observe and validate that the Class attribute (ratClass) is missing:
RadiusExtensionProcess2 called on thread: 5416 (0x1528)
RADIUS_EXTENSION_CONTROL_BLOCK
Size: 48
Version: 1
Point: Authorization (1)
RequestType: AccessRequest (1)
ResponseType: AccessAccept (2)Response RADIUS_ATTRIBUTE_ARRAY
Count: 8[0] Attribute Type: FilterId (11)
[0] Attribute Value: none (String, UTF-8 Decoded)[1] Attribute Type: FramedProtocol (7)
[1] Attribute Value: PPP (Integer, 1)[2] Attribute Type: ServiceType (6)
[2] Attribute Value: Framed (Integer, 2)[3] Attribute Type: MediumType (65)
[3] Attribute Value: IEEE-802 (Integer, 6)[4] Attribute Type: TunnelPrivateGroupID (81)
[4] Attribute Value: 10 (String, UTF-8 Decoded)[5] Attribute Type: TunnelType (64)
[5] Attribute Value: VLAN (Integer, 13)[6] Attribute Type: VendorSpecific (26)
[7] Attribute Type: VendorSpecific (26)
It is interesting to note that EAP-Message has also been appended past the Authorization Extension point. The only RADIUS attribute that should conceptually be added to or updated in the response RADIUS attributes after Authorization Extension DLLs have been called is the Message-Authenticator RADIUS attribute, which is used to sign the packet and is therefore sensitive to its contents.
Test Case
A concise and simple test case written in C is given below that confirms reproducibility of the issue.
- Compile the code to a DLL and ensure that an ACE in the ACL for the file permits the Network Service account to be able to Read and Execute.
- Ensure that a client can gain access to a network due to Access-Accept packets being generated by NPS and delivered to a NAS.
- Stop the Network Policy Server service (IAS), enable the extension by modifying the registry as documented in Setting Up the Extension DLLs and start the service again.
Access attempts are now rejected where the generated Class attribute is missing, demonstrating that it is impossible to perform binding based on its value in subsequent accounting.
(The extension explicitly rejecting the authentication attempt can be verified in the appropriate Event Log.)
/* classtest.h */ #pragma once #include "authif.h" #ifdef __cplusplus extern "C" { #endif __declspec(dllexport) DWORD WINAPI RadiusExtensionProcess2( __inout PRADIUS_EXTENSION_CONTROL_BLOCK pECB); #ifdef __cplusplus } #endif
/* classtest.c */ #include "windows.h" #include "classtest.h" DWORD WINAPI RadiusExtensionProcess2( PRADIUS_EXTENSION_CONTROL_BLOCK pECB) { PRADIUS_ATTRIBUTE_ARRAY pAttrs; DWORD dwIndex, dwSize, dwReturn; const RADIUS_ATTRIBUTE* pAttr; if (pECB->repPoint != repAuthorization) { return NO_ERROR; } if (!(pECB->rcRequestType == rcAccessRequest && pECB->rcResponseType == rcAccessAccept)) { return NO_ERROR; } pAttrs = pECB->GetResponse(pECB, rcAccessAccept); if (pAttrs == NULL) { return ERROR_CAN_NOT_COMPLETE; } dwSize = pAttrs->GetSize(pAttrs); if (dwSize == ERROR_INVALID_PARAMETER) { return ERROR_CAN_NOT_COMPLETE; } for (dwIndex = 0; dwIndex < dwSize; ++dwIndex) { pAttr = pAttrs->AttributeAt(pAttrs, dwIndex); if (pAttr == NULL) { return ERROR_CAN_NOT_COMPLETE; } if (pAttr->dwAttrType == ratClass) { return NO_ERROR; } } dwReturn = pECB->SetResponseType(pECB, rcAccessReject); if (dwReturn != NO_ERROR) { return ERROR_CAN_NOT_COMPLETE; } return NO_ERROR; }
Workarounds
There are no painless workarounds for the vulnerability and the issue is exacerbated due to other bugs in NPS:
1) Multiple Class Attributes
RFC 2865 says in its Table of Attributes section:
The following table provides a guide to which attributes may be found in which kinds of packets, and in what quantity. Request Accept Reject Challenge # Attribute 0 0+ 0 0 25 Class
It is possible via the extensions API and therefore valid according to the specification to add an additional Class attribute to have one that is known using a GUID or something with similar properties. However, multiples cannot be sent in practice in an Access-Accept in the real world as many NASes do not support the presence of them. They will either drop the packet or respond with only the last Class attribute it receives. It therefore stops an extension from being one that can be widely distributed. Where there is a need to support many different devices in heterogeneous environments, some of which will be end-of-life and out of all software support, getting a change made of this nature is not a remotely realistic proposition.
(For example, for Comware based devices, used on many HP, H3C and 3Com switches and wireless access points, only the last Class attribute received will be used in Accounting-Request packets.)
KB297317 documents that Microsoft recognise that multiple Class attributes cause interoperability issues:
“This behavior can cause interoperability issues with certain network access server (NAS) vendors.”
We have validated that an additional, known Class attributes can be added via the extensions API resulting in two being sent in an Access-Accept.
2) Use the User-Name Attribute in Access-Accept Packets
It is possible to add a User-Name attribute to an Access-Accept packet via the extensions API with the value of the ratStrippedUserName extended RADIUS attribute. This contains the client’s real identity in SAM compatible format.
RFC 2865 says of the User-Name attribute that:
“It MAY be sent in an Access-Accept packet, in which case the client SHOULD use the name returned in the Access-Accept packet in all Accounting-Request packets for this session.”
This is not, however, a general workaround to the problem in all deployment scenarios for three specific reasons:
- Firstly, as the client’s real identity is returned back to the NAS from the EAP terminating RADIUS server and all subsequent accounting will contain this identity, the privacy guarantees that EAP identity privacy is meant to bring are broken.
(It has to be said, however, that this is acceptable in many use cases due to there being differing degrees of identity privacy, it is not an all or nothing concern, and there is the likely consideration that security should trump absolute identity privacy. The main practical concern that identity privacy solves today is to not leak the identity in to the air of wireless clients in an unencrypted way. The integrity of the path from the NAS to the RADIUS server is usually of less concern as it is typically over a more physically secured hardwired, back end connection where there is far less of a tangible risk of interception.) - Secondly, the User-Name attribute is sometimes rewritten in RADIUS proxying scenarios. This adds fragility where the authentication path is not fully controlled.
- Thirdly, not all NASes support processing the User-Name attribute where one is present in an Access-Accept packet so will continue to account with the EAP outer-identity. At present, devices based on Extreme’s XOS and HP/H3C’s Comware are examples of this.
Many firewalls and Web filtering platforms have become identity aware and have started using either RADIUS accounting information via a method that does not involve an NPS extension or Syslog snooping for Single-Sign-On (SSO) purposes. In these systems, where the Authentication/Authorization process is inherently isolated and opaque and because there is no integration with it, it is necessary either that:
- The EAP terminating RADIUS server returns the User-Name attribute with the client’s real identity AND that the NASes support processing this attribute.
- The EAP terminating RADIUS server else mandates that the EAP outer-identity and EAP inner-identity resolve to the same discrete user, prohibiting the use of anonymous EAP outer-identities. (This is normally a bad idea, but doing this acts as a compromise workaround where the concern of preventing identity spoofing trumps privacy.)
It is unfortunate that Microsoft has not added either of these two bits of functionality within NPS itself. These issues, like this missing Class attribute, facilitate security vulnerable deployments. It is an oversight in the design and is a variation of the vulnerability.
From a defense in depth perspective, it would also be beneficial to have the ability on the EAP terminating RADIUS server to constrain the EAP outer-identity so that the user portion of the User-Name must have the value null or “anonymous” where it does not resolve to the same discrete user represented by the EAP inner-identity. This would mitigate the identity spoofing attack, assuming that a genuine user with the username anonymous does not exist. It forces EAP identity privacy use to be explicit and it would, therefore, be inherently obvious where it has not been handled properly.
Such functionality, if implemented, would be best configurable via boolean profile attributes.
The profile attributes would presumably be named something like Generate-User-Name, Bind-User-Identity and Limit-Outer-Identity, maintaining similarity to the existing Generate-Class-Attribute and Generate-Session-Timeout options, and the MMC-based GUI would need to be enhanced to be able to configure these. These options should all be disabled by default.
Some of the vendors, for example, who are affected by this today who have products that use RADIUS accounting or Syslog integrations for SSO purposes are:
Check Point
Fortinet
Palo Alto
Websense
Final Thoughts
Microsoft’s support for EAP identity privacy was added from both a client and server perspective starting with Windows 7 and Windows Server 2008 R2. While it is a highly desirable property to have for authentication, it appears that little to no consideration was given to the wider security impact of these changes to the ecosystem. The nature of the change was to silently introduce an identity spoofing security vulnerability both for existing IAS/NPS extensions and for external systems that integrate with NASes directly for identity information, typically via RADIUS accounting or Syslog.
Many of the people in the profession who we have engaged in discussion about the vulnerability have usually been aware of the feature but not of the implications that flow from it. This needs to change.
To resolve this properly, Microsoft, for its part, would likely need to do five things:
- Ensure that the Class attribute that NPS generates is available to extensions so that they can be implemented to perform binding from auth to accounting so that identity spoofing cannot occur in extensions on the EAP-terminating RADIUS server.
- Add functionality to allow NPS to be configured to mandate that the user portion of the EAP outer-identity must be null or “anonymous” where the EAP outer-identity and inner-identity do not resolve to the same discrete user. making identity privacy use explicit and therefore preventing identity spoofing. (Enabled by default.)
- Add functionality to allow NPS to be configured to return the client’s real identity back in the User-Name attribute of an Access-Accept to allow a NAS to account with it on the EAP-terminating RADIUS server, preventing identity spoofing. (Disabled by default with a warning about it partially compromising identity privacy.)
- Add functionality to allow NPS to be configured to mandate that the EAP outer-identity and inner-identity must resolve to the same discrete user on the EAP-terminating RADIUS server, preventing identity spoofing. (Disabled by default with a warning about it fully compromising identity privacy.)
- Document the issues so that implementers at all levels become aware of the vulnerability and can take appropriate remedial action in their products and deployments.