Friday 24 May 2019

Diagnosing VPN problems with Windows 10 VPN client


One approach to resolving a problem with a VPN set-up is to describe the problem as best as one can in a technical forum and hope that someone recognizes the situation and can make useful suggestions as to how to resolve the problem. Needless to say, this approach requires some luck and probably only works for simple problems.

Another approach is to “trace” the behaviour of the systems and either analyse the collected data or submit it for analysis by someone else. The network traffic and some of the events generated by Event Tracing for Windows providers are useful sources to trace.

Tracing network traffic


A straightforward network trace, performed on the client PC is a good first step. This could be performed with well-known tools (typically requiring installation) like Wireshark, Microsoft's Message Analyzer and Network Monitor or with built-in commands like “netsh trace” or the NetEventPacketCapture suite of PowerShell cmdlets (the well-known tools can also interpret and display captured network trace data).

One common class of problems is data sent by the VPN client that is never received by the VPN server. Ideally, one would perform a network trace on both the client and the server. It might be the case that fragments of the data reach the receiver but, perhaps because of a lossy network, a complete message is never received or it may be the case that the data is blocked/lost on route to the receiver. With just a network trace from one party, it is often possible to infer whether data is being blocked or lost but it is obviously better to compare the concurrent traces made by the two parties. If the problem can be easily reproduced, initially just tracing from the VPN client and only reverting to a concurrent client and server trace if the client-only trace is inconclusive is a pragmatic approach.

Tracing VPN protocol interactions


Most VPN protocols quickly switch from exchanging plaintext messages during initial connection set-up to exchanging encrypted messages. For the two IKE (Internet Key Exchange) based VPN protocols (L2TP/IPsec and IKEv2), tracing the Microsoft-Windows-RRAS, Microsoft-Windows-WFP and “IKEEXT Trace Provider” Event Tracing for Windows providers is most helpful.

In the format of a file suitable for use with the logman.exe “-pf” option, the following providers are a good starting point for the “Automatic” VPN type (where various VPN protocols are tried until one succeeds or they all fail):

"IKEEXT Trace Provider" 0xFFFFFFFF 255
Microsoft-Windows-RRAS
Microsoft-Windows-Ras-NdisWanPacketCapture
Microsoft-Windows-RasSstp
Microsoft-Windows-TCPIP
Microsoft-Windows-WFP
Microsoft-Windows-WebIO

All of these providers and a network trace as well can be started with a single command:

netsh trace start provider=Microsoft-Windows-RRAS provider=Microsoft-Windows-TCPIP provider=Microsoft-Windows-WFP provider=Microsoft-Windows-Ras-NdisWanPacketCapture provider=Microsoft-Windows-RasSstp provider=Microsoft-Windows-WebIO  provider={106B464D-8043-46B1-8CB8-E92A0CD7A560} keywords=0xFFFFFFFFFFFFFFFF level=255 Ethernet.Type=(IPv4,IPv6,0) Wifi.Type=Data capture=yes report=disabled correlation=disabled overwrite=yes tracefile=vpn-prob.etl

Trace Provider Types


There are several types of trace providers, including:

·         MOF (Managed Object Format)
·         WPP (Windows software trace Pre-Processor)
·         Manifest
·         TraceLogging

For the MOF, Manifest and TraceLogging types there is normally enough information in the trace output coupled with information included with the Windows installation to present all of the captured data in a human readable format.

For the WPP type, a “.pdb” (Program DataBase, debug symbols) file containing “TMF” or “PUBLIC_TMF” annotations is needed to interpret the trace data. “TMF” annotations are normally only available in private symbol files (i.e. symbols files only available to the developer (e.g. Microsoft)).

IKEEXT Trace Provider


The IKEEXT trace provider is a WPP provider type and difficult to interpret with the private symbols for ikeext.dll. Nonetheless, it is the most useful provider when one needs to understand IKE behaviour (for the L2TP/IPsec and IKEv2 VPN types).

For VPN connections that use a pre-shared key for authentication, the key will probably be present (in plaintext) in the trace data.

Microsoft-Windows-WFP


This is a manifest based provider for the Windows Filtering Platform. Most (if not all) of the useful information recorded by this provider is also present in an IKEEXT trace, but this trace data is more easily readable.

Microsoft-Windows-RRAS


This manifest based provider delivers useful information for all of the VPN types, as one would expect from the Routing and Remote Access Server provider.

Microsoft-Windows-Ras-NdisWanPacketCapture


This manifest based provider shows the packets that flow through the VPN in their unencrypted form. It is well suited to examining the initial data exchanges through the VPN (including DHCP) and subsequently recording which traffic uses the VPN (useful if there are any routing problems).

Microsoft-Windows-TCPIP


This manifest provider generates a lot of low-level TCP/UDP/IP information, including routing table changes and routing decisions. Because it is a high-volume source of events, if the size of the generated trace file might become an issue (e.g. when sharing the trace or even just analysing the trace) then this provider should only be enabled if there are good grounds to believe that it will be useful when analysing the problem.

Microsoft-Pef-WFP-MessageProvider and Microsoft-Windows-NDIS-PacketCapture


These manifest based providers record the raw network packets on the local network. They can be useful for the very first (unencrypted) message exchanges during the establishment of a VPN connection and diagnosing IP fragmentation/reassembly issues but since these providers cannot be simply started and stopped (Microsoft-Windows-NDIS-PacketCapture, for example, requires rebinding of network stacks), one should have good grounds for using them (similar to the Microsoft-Windows-TCPIP provider).

Microsoft-Windows-RasSstp


This manifest based provider is only useful for an SSTP VPN connection and probably logs at most one event per connection attempt – a description of an error that occurred.

Microsoft-Windows-WebIO


This manifest based provider is only useful for an SSTP VPN connection and is probably the most security sensitive provider: it traces WinHttp (API) activity from all process and records the HTTP headers (Including authentication headers) – if passwords can be extracted from the headers (e.g. in the case of Basic authentication) then the passwords are compromised.

Other Providers


There are many other event providers that can be useful under particular circumstances (RAS, VPN, EAP, NPS, IAS, MPR, BFE, Firewall, Security, etc. related). Typically, one would only search for and select such providers once one has gathered evidence that they might be useful.

General security considerations


Event tracing of VPN connections will probably reveal most of the VPN configuration data, including IP addresses and VPN user name – but not VPN passwords (as far as I am aware, unless PAP (Password Authentication Protocol) is used) or certificate private keys. As already mentioned, pre-shared keys could be compromised.

Many manifest providers that potentially generate events containing Personally Identifiable Information (PII) flags such events with a keyword like PII_PRESENT (often having a value of 0x20000000000). Examining the manifest (metadata) of a provider will show the keywords that it supports.

There is no practical method to remove security sensitive data from a trace file whilst preserving the integrity of the trace data.

Other event tracing considerations


If the rate of event generation is too high, events can be lost (i.e. not captured); there are tracing parameters that can be adjusted to increase the level of buffering (and therefore reduce the level of event loss). One should also try to keep the trace period short – reproducing the problem as quickly as possible.

The files created by event tracing can be sparsely populated (to assist performant logging) and can often be substantially compressed (perhaps to less than a tenth of the original size) – which can be useful if trace files are shared.

An understanding of VPN protocols is very useful when examining a trace file and a very high degree of protocol and Windows experience is needed to interpret VPN relevant WPP trace providers.

There are many event trace providers that can provide insights into specific aspects of VPN behaviour. It is not uncommon to repeatedly trace a VPN problem, modifying the selection of trace providers (and perhaps also tracing from both client and server) in response to the analysis of earlier traces.

Update 2023

Since this blog entry was first published, a number of things have changed.

Microsoft Message Analyzer has been retired and its associated packet capture driver (Microsoft-Pef-WFP-MessageProvider) no longer passes driver code signing checks. 

PktMon became known to me. PktMon can (any, by default, does) trace both the VPN protocol packets and the encapsulated data carried by the VPN protocol (the same data as captured by Microsoft-Windows-Ras-NdisWanPacketCapture). The PktMon event provider Microsoft-Windows-PktMon requires the pktmon.sys driver to be started and configured via IOCTLs, so Microsoft-Windows-Ras-NdisWanPacketCapture is still useful when a “pure” ETW trace controller (such as WPR or logman) is being used to control tracing. 

A minor bug was introduced into IKEEXT.dll, which I reported thus:

An exception can occur in IkeGetUdpEncapIfDoingNatt during L2TP or IKEv2 VPN connection establishment. A variable that is not initialized via all code paths is used in the WPP logging code at the end of the routine.

An effective workaround is to limit the trace level to level 4 (no particularly useful information is lost); I now use "IKEEXT Trace Provider" with keywords 0x10 and level 4.

A new TraceLogging provider named Microsoft.Windows.Networking.Ikeext was introduced; this provider largely mirrors the same events as "IKEEXT Trace Provider" but in an easier to decode format. The GUID for this provider is consistent with the “ETW name-hashing algorithm”; for tools, such as WPR which understand and support the convention, this provider can be specified by preceding its name with a star/asterisk: *Microsoft.Windows.Networking.Ikeext

The IKEEXT trace providers "IKEEXT Trace Provider" and Microsoft.Windows.Networking.Ikeext can log plaintext versions of IKEv1 and IKEv2 packets ("IKEEXT Trace Provider" logs the full packet and Microsoft.Windows.Networking.Ikeext logs the first 511 bytes). The REG_DWORD registry value “TestFlags” under key “HKLM\SYSTEM\CurrentControlSet\Services\IKEEXT\Parameters” enables this logging when set to the value 0x20. The key is monitored by IKEEXT for changes, so there is no need to restart IKEEXT after modifying the value.

The packets are logged as hex strings; when decoded to binary and prepended with a dummy IP/UDP header, they can be viewed in tools such as Wireshark and Microsoft Message Analyzer (if you still have a working copy). Received IKE packets can be usefully displayed “as is” whereas sent IKE packets benefit from a few small “tweaks” before display (setting the Length field in the IKE header, reversing the bytes of the Message ID in the IKE header, setting the Payload length of the IKEv2 Encrypted payload to cover just the length of the initialisation vector, clearing the encryption flag in an IKEv1 header).

Sunday 12 May 2019

Setting a pre-shared key for an L2TP over IPsec Incoming Connection

Windows 10 (i.e. a non-server version of Windows) can act as a VPN server. However not all of the configuration options available to a Routing and Remote Access Server are available via a built-in user interface – in particular the option to set a pre-shared key for incoming L2TP over IPsec connections.

The underlying functionality and API to set and use a pre-shared key is present and is simple to use, although the API documentation is rather brief. The main routine is MprAdminInterfaceSetCredentialsEx and this need only be preceded by a call to MprAdminServerConnect and (strictly speaking) eventually be followed by a call to MprAdminServerDisconnect.

The documentation on docs.microsoft.com for MprAdminInterfaceSetCredentialsEx only mentions a pre-shared key in the description of its third parameter (“dwLevel”): “A value of 1 indicates the information is a pre-shared key for the interface”. The Microsoft Open Specification document “[MS-RRASM]: Routing and Remote Access Server (RRAS) Management Protocol” contains additional (and useful) information. For example, in section “3.1.4.41 RRouterInterfaceSetCredentialsEx”, it mentions that: “If dwLevel is 0x0000002 and hInterface is NULL, the preshared key is used for L2TP”.

The documentation of the companion routine MprAdminInterfaceGetCredentialsEx mentions that: “A value of 1 indicates the information is a pre-shared key for the interface, which is in an encrypted format”. Empirically, the routine always seems to return the string “****************” (16 asterisks) – which corresponds with how a pre-shared key is displayed in the Routing and Remote Access Properties page Security tab.

The L2TP pre-shared key set by MprAdminInterfaceSetCredentialsEx is persisted in the registry as an LSA secret at HKLM\Security\Policy\Secrets\L$_RasServerCredentials#0.

A C# code extract demonstrating the process is shown below.

if (MprAdminServerConnect(null, out IntPtr mpr) == 0)
{
    string key = args[0];

    MPR_CREDENTIALSEX_1 creds = new MPR_CREDENTIALSEX_1 { Size = key.Length, CredentialsInfo = Marshal.StringToHGlobalAnsi(key) };
    MprAdminInterfaceSetCredentialsEx(mpr, IntPtr.Zero, 2, creds);
    MprAdminServerDisconnect(mpr);
}


In a “live” system, the L2TP pre-shared key is an element in a providerContext in the Windows Filtering Platform (WFP) Base Filtering Engine (BFE) configuration. More precisely, it is a presharedKeyAuthentication sub-element of the FWPM_IPSEC_IKE_MM_CONTEXT providerContext named "L2TP Main Mode Policy". The complete BFE configuration can be viewed with the command “netsh wfp show state”.

Setting a pre-shared key via the WFP API


It is possible to set a pre-shared key on a “temporary” basis (until the next reboot or the end of the WFP session) using the WFP API.

A recipe for doing this is to search for the FWPM_IPSEC_IKE_MM_CONTEXT providerContext named "L2TP Main Mode Policy" and then add a slightly modified copy, changing the providerContextKey to a new (unique) value, optionally changing the displayData to something appropriate and changing/extending the ikeMmPolicy to contain a presharedKeyAuthentication method (with the desired pre-shared key value). A new FWPM_LAYER_IKEEXT_V4 filter must also be added to utilise the new providerContext. A C# code extract demonstrating the process is shown below.

FWPM_SESSION0 session = new FWPM_SESSION0();
session.DisplayData.Name = "Gary";
session.Flags = FWPM_SESSION_FLAG_DYNAMIC;

if (FwpmEngineOpen0(null, RPC_C_AUTHN_DEFAULT, null, session, out IntPtr engine) == 0)
{
    var x = new FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE0 { ProviderContextType = FWPM_PROVIDER_CONTEXT_TYPE.FWPM_IPSEC_IKE_MM_CONTEXT };

    if (FwpmProviderContextCreateEnumHandle0(engine, x, out IntPtr h) == 0)
    {
        if (FwpmProviderContextEnum2(engine, h, 2, out IntPtr entries, out uint m) == 0)
        {
            for (uint i = 0; i < m; i++)
            {
                FWPM_PROVIDER_CONTEXT2 ctx = Marshal.PtrToStructure<FWPM_PROVIDER_CONTEXT2>(Marshal.ReadIntPtr(entries, (int)i * IntPtr.Size));

                Console.WriteLine(ctx.DisplayData.Name);
                if (ctx.DisplayData.Name == "L2TP Main Mode Policy")
                {
                    uint n = ctx.Policy.IkeMmPolicy->NumAuthenticationMethods;

                    IKEEXT_AUTHENTICATION_METHOD2* methods = stackalloc IKEEXT_AUTHENTICATION_METHOD2[(int)n + 1];
                    for (uint j = 0; j < n; j++) methods[j] = ctx.Policy.IkeMmPolicy->AuthenticationMethods[j];
                    string preshared = args[0];
                    methods[n].AuthenticationMethodType = IKEEXT_AUTHENTICATION_METHOD_TYPE.IKEEXT_PRESHARED_KEY;
                    methods[n].PresharedKeyAuthentication.PresharedKey.Size = (uint)preshared.Length;
                    methods[n].PresharedKeyAuthentication.PresharedKey.Data = (byte*)Marshal.StringToHGlobalAnsi(preshared);

                    ctx.ProviderContextKey = Guid.NewGuid();
                    ctx.DisplayData.Name = "Gary Context";
                    ctx.Policy.IkeMmPolicy->NumAuthenticationMethods = n + 1;
                    ctx.Policy.IkeMmPolicy->AuthenticationMethods = methods;

                    if (FwpmProviderContextAdd2(engine, ctx, null, out ulong _) == 0)
                    {
                        ulong weight = 1;

                        FWPM_FILTER0 filter = new FWPM_FILTER0();
                        filter.DisplayData.Name = "Gary Filter";
                        filter.Flags = FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
                        filter.LayerKey = FWPM_LAYER_IKEEXT_V4;
                        filter.SubLayerKey = FWPM_SUBLAYER_UNIVERSAL;
                        filter.Weight.Type = FWP_DATA_TYPE.FWP_UINT64;
                        filter.Weight.uint64 = &weight;
                        filter.Action.Type = FWP_ACTION_TYPE.FWP_ACTION_PERMIT;
                        filter.U.ProviderContextKey = ctx.ProviderContextKey;

                        if (FwpmFilterAdd0(engine, filter, null, out ulong _) == 0)
                        {
                            Console.Write("Waiting..."); Console.ReadKey();
                        }
                    }
                }
            }

            FwpmFreeMemory0(&entries);
        }

        FwpmProviderContextDestroyEnumHandle0(engine, h);
    }

    FwpmEngineClose0(engine);
}


The only constraint on the filter subLayerKey and weight is that the filter should have a higher effective weight than the existing FWPM_LAYER_IKEEXT_V4 filter.

Simple trick to set a pre-shared key


It is straightforward to set a pre-shared key with MprAdminInterfaceSetCredentialsEx, but it does require a custom program. There is however a trick, using built-in tools, that seems to work: define a “dummy” IPsec tunnel with “Preshared key” authentication; any tool can be used to do this (e.g. the “Windows Defender Firewall with Advanced Security” control panel applet, the “netsh advfirewall consec add rule” command or the “New-NetIPsecRule” PowerShell cmdlet).

Here are examples of creating suitable “dummy” IPsec tunnels (the endpoint value can be any address that does not apply to any potential traffic) with a shared secret of “XXX”:

netsh advfirewall consec add rule name="Dummy" endpoint1=192.168.3.1/32 endpoint2=any mode=tunnel action=requireinrequireout auth1=computerpsk auth1psk="XXX"

New-NetIPsecRule -DisplayName "Dummy" -Phase1AuthSet (New-NetIPsecPhase1AuthSet -DisplayName "Dummy" -Proposal (New-NetIPsecAuthProposal -Machine -PreSharedKey "XXX")).InstanceID -InboundSecurity Require -OutboundSecurity Require -KeyModule IKEv1 -Mode Tunnel -LocalAddress 192.168.3.1/32 -RemoteAddress Any

The pre-shared key created by this method is persisted in the registry under HKLM\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\Phase1AuthenticationSets.

Similar to the L2TP pre-shared key, in a “live” system the pre-shared key created by this method is a presharedKeyAuthentication sub-element of an FWPM_IPSEC_IKE_MM_CONTEXT providerContext named after the Phase1AuthenticationSet ("Dummy", in the examples above). The filter that references this providerContext has a subLayerKey of FWPM_SUBLAYER_IPSEC_TUNNEL which gives the filter a higher effective weight than the L2TP filter.

On an “out-of-the-box” system, the ikeProposals in the L2TP providerContext and the new providerContext are usually identical; if testing (or a review of the “netsh wfp show state” output) indicates discrepancies then the commands to create the “Dummy” tunnel can be extended to compensate for the differences.